Initial import
authorNastaran Shafiei <nastaran.shafiei@nasa.gov>
Wed, 31 May 2017 00:46:11 +0000 (17:46 -0700)
committerNastaran Shafiei <nastaran.shafiei@nasa.gov>
Wed, 31 May 2017 00:46:11 +0000 (17:46 -0700)
1239 files changed:
.classpath [new file with mode: 0644]
.gitignore [new file with mode: 0644]
.idea/.name [new file with mode: 0644]
.idea/annotations.iml [new file with mode: 0644]
.idea/artifacts/RunJPF.xml [new file with mode: 0644]
.idea/artifacts/jpf.xml [new file with mode: 0644]
.idea/artifacts/jpf_annotations.xml [new file with mode: 0644]
.idea/artifacts/jpf_classes.xml [new file with mode: 0644]
.idea/classes.iml [new file with mode: 0644]
.idea/codeStyleSettings.xml [new file with mode: 0644]
.idea/compiler.xml [new file with mode: 0644]
.idea/copyright/profiles_settings.xml [new file with mode: 0644]
.idea/encodings.xml [new file with mode: 0644]
.idea/examples.iml [new file with mode: 0644]
.idea/main.iml [new file with mode: 0644]
.idea/misc.xml [new file with mode: 0644]
.idea/modules.xml [new file with mode: 0644]
.idea/peers.iml [new file with mode: 0644]
.idea/runConfigurations/run_example_jpf.xml [new file with mode: 0644]
.idea/runConfigurations/run_test.xml [new file with mode: 0644]
.idea/scopes/scope_settings.xml [new file with mode: 0644]
.idea/tests.iml [new file with mode: 0644]
.idea/vcs.xml [new file with mode: 0644]
.project [new file with mode: 0644]
LICENSE-2.0.txt [new file with mode: 0644]
META-INF/RunJPF/MANIFEST.MF [new file with mode: 0644]
README [new file with mode: 0644]
bin/javajpf [new file with mode: 0755]
bin/jpf [new file with mode: 0755]
bin/jpf.bat [new file with mode: 0755]
bin/print_class [new file with mode: 0755]
bin/print_class.bat [new file with mode: 0755]
bin/print_events [new file with mode: 0755]
bin/test [new file with mode: 0755]
bin/test.bat [new file with mode: 0755]
build.properties [new file with mode: 0644]
build.xml [new file with mode: 0644]
doc/devel/attributes.md [new file with mode: 0644]
doc/devel/bytecode_factory.md [new file with mode: 0644]
doc/devel/choicegenerator.md [new file with mode: 0644]
doc/devel/coding_conventions.md [new file with mode: 0644]
doc/devel/create_project.md [new file with mode: 0644]
doc/devel/design.md [new file with mode: 0644]
doc/devel/eclipse_plugin_update.md [new file with mode: 0644]
doc/devel/embedded.md [new file with mode: 0644]
doc/devel/index.md [new file with mode: 0644]
doc/devel/jpf_tests.md [new file with mode: 0644]
doc/devel/listener.md [new file with mode: 0644]
doc/devel/logging.md [new file with mode: 0644]
doc/devel/mercurial.md [new file with mode: 0644]
doc/devel/mji.md [new file with mode: 0644]
doc/devel/mji/mangling.md [new file with mode: 0644]
doc/devel/modules.md [new file with mode: 0644]
doc/devel/partial_order_reduction.md [new file with mode: 0644]
doc/devel/report.md [new file with mode: 0644]
doc/graphics/DFSListener.svg [new file with mode: 0644]
doc/graphics/app-types.svg [new file with mode: 0644]
doc/graphics/attributes.svg [new file with mode: 0644]
doc/graphics/bc-factory.svg [new file with mode: 0644]
doc/graphics/cg-impl.svg [new file with mode: 0644]
doc/graphics/cg-motivation.svg [new file with mode: 0644]
doc/graphics/cg-ontology.svg [new file with mode: 0644]
doc/graphics/cg-sequence.svg [new file with mode: 0644]
doc/graphics/choicegen-example.svg [new file with mode: 0644]
doc/graphics/genpeer.svg [new file with mode: 0644]
doc/graphics/interleavings.svg [new file with mode: 0644]
doc/graphics/jpf-abstractions.svg [new file with mode: 0644]
doc/graphics/jpf-basic.svg [new file with mode: 0644]
doc/graphics/jpf-intro-new.svg [new file with mode: 0644]
doc/graphics/jpf-layers.svg [new file with mode: 0644]
doc/graphics/jpf-project.svg [new file with mode: 0644]
doc/graphics/listener-overview.svg [new file with mode: 0644]
doc/graphics/listeners.svg [new file with mode: 0644]
doc/graphics/mji-call.svg [new file with mode: 0644]
doc/graphics/mji-functions.svg [new file with mode: 0644]
doc/graphics/mji-mangling.svg [new file with mode: 0644]
doc/graphics/new-testing.svg [new file with mode: 0644]
doc/graphics/por-mark.svg [new file with mode: 0644]
doc/graphics/por-scheduling-relevance.svg [new file with mode: 0644]
doc/graphics/properties.svg [new file with mode: 0644]
doc/graphics/report.svg [new file with mode: 0644]
doc/graphics/states-mc.svg [new file with mode: 0644]
doc/graphics/states-testing.svg [new file with mode: 0644]
doc/graphics/sw-model-checking-2.svg [new file with mode: 0644]
doc/graphics/sw-model-checking.svg [new file with mode: 0644]
doc/index.md [new file with mode: 0644]
doc/install/build.md [new file with mode: 0644]
doc/install/eclipse-jpf.md [new file with mode: 0644]
doc/install/eclipse-plugin.md [new file with mode: 0644]
doc/install/eclipse-plugin/update.md [new file with mode: 0644]
doc/install/eclipse-plugin/update/features.md [new file with mode: 0644]
doc/install/eclipse-plugin/update/plugins.md [new file with mode: 0644]
doc/install/index.md [new file with mode: 0644]
doc/install/netbeans-jpf.md [new file with mode: 0644]
doc/install/netbeans-plugin.md [new file with mode: 0644]
doc/install/repo_shell.md [new file with mode: 0644]
doc/install/repositories.md [new file with mode: 0644]
doc/install/requirements.md [new file with mode: 0644]
doc/install/site-properties.md [new file with mode: 0644]
doc/install/snapshot.md [new file with mode: 0644]
doc/intro/classification.md [new file with mode: 0644]
doc/intro/index.md [new file with mode: 0644]
doc/intro/race_example.md [new file with mode: 0644]
doc/intro/random_example.md [new file with mode: 0644]
doc/intro/testing_vs_model_checking.md [new file with mode: 0644]
doc/intro/what_is_jpf.md [new file with mode: 0644]
doc/jpf-core/AssertionProperty.md [new file with mode: 0644]
doc/jpf-core/ErrorTraceGenerator.md [new file with mode: 0644]
doc/jpf-core/ExceptionInjector.md [new file with mode: 0644]
doc/jpf-core/IdleFilter.md [new file with mode: 0644]
doc/jpf-core/index.md [new file with mode: 0644]
doc/papers/chicago-author-date.csl [new file with mode: 0644]
doc/papers/index.md [new file with mode: 0644]
doc/papers/references.bib [new file with mode: 0644]
doc/user/api.md [new file with mode: 0644]
doc/user/application_types.md [new file with mode: 0644]
doc/user/components.md [new file with mode: 0644]
doc/user/config.md [new file with mode: 0644]
doc/user/config/random.md [new file with mode: 0644]
doc/user/index.md [new file with mode: 0644]
doc/user/output.md [new file with mode: 0644]
doc/user/run.md [new file with mode: 0644]
doc/user/run_eclipse.md [new file with mode: 0644]
doc/user/run_eclipse_plugin.md [new file with mode: 0644]
doc/user/run_nb.md [new file with mode: 0644]
doc/user/run_nb_plugin.md [new file with mode: 0644]
eclipse/AntBuilder.launch [new file with mode: 0644]
eclipse/run-JPF.launch [new file with mode: 0644]
eclipse/test-JPF.launch [new file with mode: 0644]
eclipse/update-JPF-siteproperties.launch [new file with mode: 0644]
jpf.properties [new file with mode: 0644]
nbproject/ide-file-targets.xml [new file with mode: 0644]
nbproject/project.xml [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/FilterField.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/FilterFrame.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/JPFAttribute.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/JPFConfig.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/JPFOption.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/JPFOptions.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/MJI.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/NeverBreak.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/NoJPFExecution.java [new file with mode: 0644]
src/annotations/gov/nasa/jpf/annotation/NonShared.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/AnnotationProxyBase.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/BoxObjectCaches.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/CachedROHttpConnection.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/ConsoleOutputStream.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/EventProducer.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/FinalizerThread.java [new file with mode: 0644]
src/classes/gov/nasa/jpf/SerializationConstructor.java [new file with mode: 0644]
src/classes/java/io/File.java [new file with mode: 0644]
src/classes/java/io/FileDescriptor.java [new file with mode: 0644]
src/classes/java/io/FileInputStream.java [new file with mode: 0644]
src/classes/java/io/FileOutputStream.java [new file with mode: 0644]
src/classes/java/io/InputStreamReader.java [new file with mode: 0644]
src/classes/java/io/OutputStreamWriter.java [new file with mode: 0644]
src/classes/java/io/RandomAccessFile.java [new file with mode: 0644]
src/classes/java/lang/Class.java [new file with mode: 0644]
src/classes/java/lang/ClassLoader.java [new file with mode: 0644]
src/classes/java/lang/InheritableThreadLocal.java [new file with mode: 0644]
src/classes/java/lang/Object.java [new file with mode: 0644]
src/classes/java/lang/StackTraceElement.java [new file with mode: 0644]
src/classes/java/lang/String.java [new file with mode: 0644]
src/classes/java/lang/System.java [new file with mode: 0644]
src/classes/java/lang/Thread.java [new file with mode: 0644]
src/classes/java/lang/ThreadGroup.java [new file with mode: 0644]
src/classes/java/lang/ThreadLocal.java [new file with mode: 0644]
src/classes/java/lang/Throwable.java [new file with mode: 0644]
src/classes/java/lang/annotation/Inherited.java [new file with mode: 0644]
src/classes/java/lang/annotation/Retention.java [new file with mode: 0644]
src/classes/java/lang/ref/Reference.java [new file with mode: 0644]
src/classes/java/lang/ref/ReferenceQueue.java [new file with mode: 0644]
src/classes/java/lang/ref/WeakReference.java [new file with mode: 0644]
src/classes/java/lang/reflect/AccessibleObject.java [new file with mode: 0644]
src/classes/java/lang/reflect/Constructor.java [new file with mode: 0644]
src/classes/java/lang/reflect/Field.java [new file with mode: 0644]
src/classes/java/lang/reflect/InvocationTargetException.java [new file with mode: 0644]
src/classes/java/lang/reflect/Method.java [new file with mode: 0644]
src/classes/java/net/URLClassLoader.java [new file with mode: 0644]
src/classes/java/nio/Buffer.java [new file with mode: 0644]
src/classes/java/nio/BufferUnderflowException.java [new file with mode: 0644]
src/classes/java/nio/ByteBuffer.java [new file with mode: 0644]
src/classes/java/nio/ByteOrder.java [new file with mode: 0644]
src/classes/java/nio/channels/FileChannel.java [new file with mode: 0644]
src/classes/java/nio/package-info.java [new file with mode: 0644]
src/classes/java/security/AccessController.java [new file with mode: 0644]
src/classes/java/security/MessageDigest.java [new file with mode: 0644]
src/classes/java/security/SecureClassLoader.java [new file with mode: 0644]
src/classes/java/text/DecimalFormat.java [new file with mode: 0644]
src/classes/java/text/Format.java [new file with mode: 0644]
src/classes/java/text/NumberFormat.java [new file with mode: 0644]
src/classes/java/text/SimpleDateFormat.java [new file with mode: 0644]
src/classes/java/util/Random.java [new file with mode: 0644]
src/classes/java/util/TimeZone.java [new file with mode: 0644]
src/classes/java/util/concurrent/BrokenBarrierException.java [new file with mode: 0644]
src/classes/java/util/concurrent/CyclicBarrier.java [new file with mode: 0644]
src/classes/java/util/concurrent/Exchanger.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicIntegerArray.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicLongArray.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicReferenceArray.java [new file with mode: 0644]
src/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java [new file with mode: 0644]
src/classes/java/util/function/Supplier.java [new file with mode: 0644]
src/classes/java/util/logging/FileHandler.java [new file with mode: 0644]
src/classes/java/util/regex/Matcher.java [new file with mode: 0644]
src/classes/java/util/regex/Pattern.java [new file with mode: 0644]
src/classes/org/junit/After.java [new file with mode: 0644]
src/classes/org/junit/AfterClass.java [new file with mode: 0644]
src/classes/org/junit/Before.java [new file with mode: 0644]
src/classes/org/junit/BeforeClass.java [new file with mode: 0644]
src/classes/org/junit/Ignore.java [new file with mode: 0644]
src/classes/org/junit/Test.java [new file with mode: 0644]
src/classes/sun/misc/AtomicLong.java [new file with mode: 0644]
src/classes/sun/misc/JavaAWTAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaIOAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaIODeleteOnExitAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaIOFileDescriptorAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaLangAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaNetAccess.java [new file with mode: 0644]
src/classes/sun/misc/JavaNioAccess.java [new file with mode: 0644]
src/classes/sun/misc/SharedSecrets.java [new file with mode: 0644]
src/classes/sun/misc/Unsafe.java [new file with mode: 0644]
src/classes/sun/net/www/protocol/http/Handler.java [new file with mode: 0644]
src/classes/sun/nio/ch/Interruptible.java [new file with mode: 0644]
src/classes/sun/reflect/ConstantPool.java [new file with mode: 0644]
src/classes/sun/reflect/annotation/AnnotationType.java [new file with mode: 0644]
src/examples/BoundedBuffer.java [new file with mode: 0644]
src/examples/BoundedBuffer.jpf [new file with mode: 0644]
src/examples/Crossing.java [new file with mode: 0644]
src/examples/Crossing.jpf [new file with mode: 0644]
src/examples/DiningPhil.java [new file with mode: 0644]
src/examples/DiningPhil.jpf [new file with mode: 0644]
src/examples/HelloWorld.java [new file with mode: 0644]
src/examples/HelloWorld.jpf [new file with mode: 0644]
src/examples/NumericValueCheck.java [new file with mode: 0644]
src/examples/NumericValueCheck.jpf [new file with mode: 0644]
src/examples/Racer.java [new file with mode: 0644]
src/examples/Racer.jpf [new file with mode: 0644]
src/examples/Rand.java [new file with mode: 0644]
src/examples/Rand.jpf [new file with mode: 0644]
src/examples/RobotManager-replay-nt.jpf [new file with mode: 0644]
src/examples/RobotManager-replay-ot.jpf [new file with mode: 0644]
src/examples/RobotManager.java [new file with mode: 0644]
src/examples/RobotManager.jpf [new file with mode: 0644]
src/examples/StopWatch.java [new file with mode: 0644]
src/examples/StopWatch.jpf [new file with mode: 0644]
src/examples/TestExample-coverage.jpf [new file with mode: 0644]
src/examples/TestExample.java [new file with mode: 0644]
src/examples/oldclassic-da.jpf [new file with mode: 0644]
src/examples/oldclassic.java [new file with mode: 0644]
src/examples/oldclassic.jpf [new file with mode: 0644]
src/main/gov/nasa/jpf/$coreTag.java [new file with mode: 0644]
src/main/gov/nasa/jpf/Config.java [new file with mode: 0644]
src/main/gov/nasa/jpf/ConfigChangeListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/Error.java [new file with mode: 0644]
src/main/gov/nasa/jpf/GenericProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFClassLoader.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFConfigException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFErrorException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFListenerException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFNativePeerException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/JPFShell.java [new file with mode: 0644]
src/main/gov/nasa/jpf/ListenerAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/Property.java [new file with mode: 0644]
src/main/gov/nasa/jpf/PropertyListenerAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/State.java [new file with mode: 0644]
src/main/gov/nasa/jpf/StateExtension.java [new file with mode: 0644]
src/main/gov/nasa/jpf/SystemAttribute.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/ClassFile.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/ClassFilePrinter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/ClassFileReader.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/ClassFileReaderAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/DirClassFileContainer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMAnnotationParser.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMByteCodePrinter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMByteCodeReader.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMByteCodeReaderAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMClassFileContainer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMClassInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMDirectCallStackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMInstructionFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMNativeStackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMStackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JVMSystemClassLoaderInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/JarClassFileContainer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/AALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/AASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ACONST_NULL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ANEWARRAY.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ARETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ARRAYLENGTH.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ATHROW.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ArrayLoadInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ArrayStoreInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/BALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/BASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/BIPUSH.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/CALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/CASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/CHECKCAST.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/D2F.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/D2I.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/D2L.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DADD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DCMPG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DCMPL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DCONST.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DDIV.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DIRECTCALLRETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DLOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DMUL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DNEG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DREM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DRETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DSTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DSUB.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP2.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X1.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X2.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP_X1.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DUP_X2.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/DoubleCompareInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/EXECUTENATIVE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/F2D.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/F2I.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/F2L.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FADD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FCMPG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FCMPL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FCONST.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FDIV.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FINISHCLINIT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FLOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FMUL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FNEG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FREM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FRETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FSTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/FSUB.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/GETFIELD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/GOTO.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/GOTO_W.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/GetHelper.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2B.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2C.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2D.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2F.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2L.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/I2S.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IADD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IAND.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ICONST.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IDIV.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFEQ.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFGE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFGT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFLE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFLT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFNE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFNONNULL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IFNULL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPEQ.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPNE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPEQ.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPNE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IINC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ILOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IMUL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INEG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INSTANCEOF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKECG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKECLINIT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKEINTERFACE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKESPECIAL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/INVOKEVIRTUAL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IOR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IREM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IRETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ISHL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ISHR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ISTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/ISUB.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IUSHR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IXOR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/IfInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/InstanceInvocation.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/InstructionFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JSR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JSR_W.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMArrayElementInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMInstanceFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitorAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMInvokeInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMLocalVariableInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMReturnInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/JVMStaticFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/L2D.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/L2F.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/L2I.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LADD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LAND.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LCMP.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LCONST.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LDC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LDC2_W.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LDC_W.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LDIV.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LLOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LMUL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LNEG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LOOKUPSWITCH.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LOR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LREM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LRETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LSHL.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LSHR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LSTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LSUB.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LUSHR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LXOR.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LockInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LongArrayLoadInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LongArrayStoreInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/LongReturn.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/MONITORENTER.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/MONITOREXIT.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/MULTIANEWARRAY.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/NATIVERETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/NEW.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/NEWARRAY.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/NOP.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/NewArrayInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/POP.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/POP2.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/PUTFIELD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/PutHelper.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/RET.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/RETURN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/RUNSTART.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/SALOAD.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/SASTORE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/SIPUSH.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/SWAP.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/StaticFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/SwitchInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/TABLESWITCH.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/VirtualInvocation.java [new file with mode: 0644]
src/main/gov/nasa/jpf/jvm/bytecode/WIDE.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/AssertionProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/BudgetChecker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/CGMonitor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/CGRemover.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/CallMonitor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ChoiceSelector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ChoiceTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/CoverageAnalyzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/DeadlockAnalyzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/DistributedSimpleDot.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/EndlessLoopDetector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ErrorTraceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ExceptionInjector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ExecTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/HeapTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/IdleFilter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/InsnCounter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/LockedStackDepth.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/LogConsole.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/MethodAnalyzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/MethodTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/NoStateCycles.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/NonSharedChecker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/NullTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/NumericValueChecker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/OOMEInjector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ObjectTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/OverlappingMethodAnalyzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/PathOutputMonitor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/Perturbator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/PreciseRaceDetector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/ReferenceLocator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/SearchStats.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/SimpleDot.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/SimpleIdleFilter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StackDepthChecker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StackTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StateCountEstimator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StateSpaceAnalyzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StateSpaceDot.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StateTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/StopWatchFuzzer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/TraceStorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/VarRecorder.java [new file with mode: 0644]
src/main/gov/nasa/jpf/listener/VarTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/perturb/GenericDataAbstractor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/perturb/IntOverUnder.java [new file with mode: 0644]
src/main/gov/nasa/jpf/perturb/OperandPerturbator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/ConsolePublisher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/Publisher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/PublisherExtension.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/PublisherExtensionAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/Reporter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/Statistics.java [new file with mode: 0644]
src/main/gov/nasa/jpf/report/XMLPublisher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/DFSearch.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/PathSearch.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/RandomSearch.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/Search.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/SearchListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/SearchListenerAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/SearchState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/Simulation.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/BFSHeuristic.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/DFSHeuristic.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/GlobalSwitchThread.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/HeuristicSearch.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/HeuristicState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/Interleaving.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/MinimizePreemption.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/MostBlocked.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/PreferThreads.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/PrioritizedState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/RandomHeuristic.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/SimplePriorityHeuristic.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/StaticPriorityQueue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/search/heuristic/UserHeuristic.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/GenPeer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/LogConsole.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/PrintEvents.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/Run.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/RunJPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/tool/RunTest.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ArrayByteQueue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ArrayIntSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ArrayObjectQueue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Attributable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/AvailableBufferedInputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BailOut.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BinaryClassSource.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BitArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BitSet1024.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BitSet256.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BitSet64.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/BitSetN.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ClassInfoFilter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/CloneableObject.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Cloner.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/CommitOutputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ConsoleStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ConstGrowth.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/CountDown.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/DevNullPrintStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/DynamicIntArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/DynamicObjectArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ElementCreator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ExpGrowth.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FeatureSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FieldSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FieldSpecMatcher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FileUtils.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FinalBitSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/FixedBitSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Growth.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/HashData.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/HashPool.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IdentityArrayObjectSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IdentityObjectSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ImmutableList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IndexIterator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/InstructionState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IntArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IntIterator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IntSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IntTable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/IntVector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Invocation.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/JPFLogger.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/JPFSiteUtils.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Left.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LimitedInputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LinkedObjectQueue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LocationSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LogHandler.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LogManager.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Loggable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/LongVector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/MethodInfoRegistry.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/MethodSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/MethodSpecMatcher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Misc.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/MutableInteger.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/MutableIntegerRestorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/OATHash.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjVector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjectConverter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjectList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjectQueue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ObjectSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PSIntMap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Pair.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PairPermutationGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PathnameExpander.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PermutationGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Predicate.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PrintStreamable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/PrintUtils.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Printable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Processor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/RandomPermutationGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/ReadOnlyObjList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Reflection.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/RepositoryEntry.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Result.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Right.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/RunListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/RunRegistry.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SimplePool.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SingleElementList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SortedArrayIntSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SortedArrayObjectSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Source.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SourceRef.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SparseClusterArray.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SparseIntVector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SparseObjVector.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SplitInputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/SplitOutputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StateExtensionClient.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StateExtensionListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StringExpander.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StringMatcher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StringSetMatcher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/StructuredPrinter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TotalPermutationGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Trace.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TraceElement.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/Transformer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TwoTypeComparator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TypeRef.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TypeSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/TypeSpecMatcher.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/UniqueRandomPermGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/UnsortedArrayIntSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/VarSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/WeakPool.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/automaton/Automaton.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/automaton/State.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/automaton/Transition.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/CheckEvent.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/ControlEvent.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/Event.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/EventChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/EventConstructor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/EventContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/EventForest.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/EventTree.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/NoEvent.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/PropagatingEventContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/SystemEvent.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/event/TestEventTree.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/AbstractValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/ArrayValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/BooleanValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/CGCall.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/CGCreator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/CGCreatorFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/Creator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/CreatorsFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/DoubleValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/JSONLexer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/JSONObject.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/JSONObjectValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/JSONParser.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/NullValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/StringValue.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/Token.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/json/Value.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/Alternative.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/ESParser.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/ElementProcessor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/Event.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/EventFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/EventGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/EventGeneratorFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/Repetition.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/Script.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/ScriptElement.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/ScriptElementContainer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/ScriptEnvironment.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/Section.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/SequenceInterpreter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/script/StringSetGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestJPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestMultiProcessJPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/test/TestJPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/util/test/TestMultiProcessJPF.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AbstractRestorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AbstractSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AbstractTypeAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AllRunnablesSyncPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Allocation.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AllocationContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AnnotationParser.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ApplicationContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ArrayAccess.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ArrayIndexOutOfBoundsExecutiveException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ArrayOffset.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/AtomicData.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Attributor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Backtracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BooleanArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BooleanChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BooleanFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BoxObjectCacheManager.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ByteArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ByteFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BytecodeAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/BytecodeTypeParameterAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/CharArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/CharFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/CheckExtendTransition.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ChoiceGeneratorBase.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ChoicePoint.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassChangeException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassFileContainer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassFileMatch.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassInfoException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassLoaderList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassParseException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClassPath.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClinitRequired.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ClosedMemento.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/CollapsePools.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ConstInsnPathTime.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DebugJenkinsStateSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DebugStateSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DefaultBacktracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DefaultFieldsFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DefaultMementoRestorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DelegatingScheduler.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DirectCallStackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DoubleArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DoubleChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DoubleFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DoubleSlotFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/DynamicElementInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ElementInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ExceptionHandler.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ExceptionInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ExceptionParameterAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FieldLockInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FieldLockInfoFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Fields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FieldsFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FinalizerThreadInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FloatArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FloatChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FloatFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FormalParameterAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FullStateSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GenericHeap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GenericSGOIDHeap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GenericSharednessPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GenericSignatureHolder.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GlobalSchedulingPoint.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/GlobalSharednessPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/HandlerContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/HashedAllocationContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Heap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/IncrementalChangeTracker.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/InfoObject.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Instruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/IntArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/IntChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/IntegerFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/IsEndStateProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/JPFOutputStream.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_vm_Verify.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/JenkinsStateSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/KernelState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LoadOnJPFRequired.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LocalVarInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LockSetThresholdFli.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LongArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LongChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/LongFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MJIEnv.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Memento.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MementoFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MementoRestorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MethodInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MethodLocator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Monitor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/MultiProcessVM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NamedFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NativeMethodInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NativePeer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NativeStackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NativeStateHolder.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NoJPFExec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NoOutOfMemoryErrorProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NoUncaughtExceptionsProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/NotDeadlockedProperty.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/OVHeap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/OVStatics.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ObjRef.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PSIMHeap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Path.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PathSharednessPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PersistentLockSetThresholdFli.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PersistentSingleLockThresholdFli.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PersistentTidSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PreciseAllocationContext.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PredicateMap.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/PriorityRunnablesSyncPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ReferenceArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ReferenceChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ReferenceFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ReferenceProcessor.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ReleaseAction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Restorable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/RestorableVMState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Scheduler.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SerializingStateSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SharednessPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ShortArrayFields.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ShortFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SingleLockThresholdFli.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SingleProcessVM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SingleSlotFieldInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StackFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StateRestorer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StateSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StateSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StaticElementInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Statics.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/StatisticFieldLockInfoFactory.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Step.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Storable.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SuperTypeAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SyncPolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SystemClassLoaderInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SystemState.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/SystemTime.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThreadChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThreadData.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThreadInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThreadInfoSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThreadList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThresholdFieldLockInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/ThrowsAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/TidSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/TimeModel.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Transition.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/TypeAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/TypeParameterAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/TypeParameterBoundAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Types.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/UncaughtException.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/VM.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/VMListener.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/VariableAnnotationInfo.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/Verify.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/ArrayElementInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/FieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/InstanceFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/InstanceInvokeInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/InstructionInterface.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/InvokeInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/LocalVariableInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/LookupSwitchInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/NewInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/ReadInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/ReadOrWriteInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/ReturnInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/ReturnValueInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/StaticFieldInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/StoreInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/TableSwitchInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/bytecode/WriteInstruction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/BreakGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/CompoundChoiceGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/DoubleSpec.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/DoubleThresholdGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/ExceptionThreadChoiceFromSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/ExposureCG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/FloatChoiceFromList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/IntChoiceFromList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/IntChoiceFromSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/IntIntervalGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/InvocationCG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/LongChoiceFromList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/NumberChoiceFromList.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/PermutationCG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/RandomIntIntervalGenerator.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/RandomOrderIntCG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/RandomOrderLongCG.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/ThreadChoiceFromSet.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/choice/TypedObjectChoice.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/Abstraction.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/AbstractionAdapter.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/AdaptiveSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/AmmendableFilterConfiguration.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/CFSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/DebugCFSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/DebugFilteringSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/DefaultFilterConfiguration.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/DynamicAbstractionSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/FieldAmmendmentByName.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/FilterConfiguration.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/FilterFrame.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/FilteringSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/FramePolicy.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IgnoreConstants.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IgnoreReflectiveNames.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IgnoreThreadNastiness.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IgnoreUtilSilliness.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/Ignored.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IgnoresFromAnnotations.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/IncludesFromAnnotations.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/TopFrameSerializer.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/UnfilterField.java [new file with mode: 0644]
src/main/gov/nasa/jpf/vm/serialize/UnknownJPFClass.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/AtomicFieldUpdater.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_AnnotationProxyBase.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_CachedROHttpConnection.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_ConsoleOutputStream.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_DelegatingTimeZone.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_EventProducer.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_FinalizerThread.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_test_MemoryGoal.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_tools_MethodTester.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_File.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_FileDescriptor.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_InputStreamReader.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectInputStream.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectOutputStream.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectStreamClass.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_OutputStreamWriter.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Boolean.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Byte.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Character.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_ClassLoader.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Double.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Float.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Integer.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Long.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Math.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Object.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Runtime.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Short.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_String.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuffer.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuilder.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringCoding.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Thread.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_ThreadLocal.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_Throwable.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Array.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Proxy.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_net_URLClassLoader.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_net_URLDecoder.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_net_URLEncoder.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_security_MessageDigest.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_Bidi.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormat.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormatSymbols.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormat.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormatSymbols.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_Format.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_text_SimpleDateFormat.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_Calendar.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_Date.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_Locale.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_Random.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_ResourceBundle.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_TimeZone.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_Exchanger.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicInteger.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerArray.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerFieldUpdater.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLong.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongArray.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongFieldUpdater.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceArray.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceFieldUpdater.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_logging_Level.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Matcher.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Pattern.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_sun_misc_VM.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_sun_net_www_protocol_http_Handler.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_Reflection.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_ReflectionFactory.java [new file with mode: 0644]
src/peers/gov/nasa/jpf/vm/LoggablePeer.java [new file with mode: 0644]
src/tests/TypeNameTest.java [new file with mode: 0644]
src/tests/classloader_specific_tests/Class1.java [new file with mode: 0644]
src/tests/classloader_specific_tests/Class2.java [new file with mode: 0644]
src/tests/classloader_specific_tests/Class3.java [new file with mode: 0644]
src/tests/classloader_specific_tests/Interface1.java [new file with mode: 0644]
src/tests/classloader_specific_tests/Interface2.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/ConfigTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestApp.jpf [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestCommon.jpf [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestIncludes.jpf [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestRequires.jpf [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestRequiresFail.jpf [new file with mode: 0644]
src/tests/gov/nasa/jpf/configTestSite.properties [new file with mode: 0644]
src/tests/gov/nasa/jpf/jvm/ClassInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/jvm/JVMStackFrameTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/jvm/MethodInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/jvm/NonResolvedClassInfo.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/HarnessTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/InstructionFactoryTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/JPF_gov_nasa_jpf_test_basic_MJITest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/ListenerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/MJITest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/TestJPFMainTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/basic/TestJPFNoMainTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/AtomicIntegerFieldUpdaterTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/AtomicLongFieldUpdaterTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/AtomicReferenceFieldUpdaterTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/CountDownLatchTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/ExchangerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/ExecutorServiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/concurrent/SemaphoreTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/io/BufferedInputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/io/FileIOStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/io/FileIOTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/io/FileTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/io/ObjectStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/BoxObjectCacheTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/ClassLoaderTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/ClassTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/CloneTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/RuntimeTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/StringTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/SystemTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/ref/WeakReferenceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/reflect/ConstructorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/reflect/FieldTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/lang/reflect/MethodTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/math/BigIntegerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/net/LoadUtility.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/net/URLClassLoaderTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/net/URLEncoderTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/text/DateFormatTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/text/DecimalFormatTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/java/text/SimpleDateFormatTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/AttrsTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/BreakTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/CGNotificationTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/CGRemoverTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/CGReorderTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/CascadedCGTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/ExceptionInjectorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/ExtendTransitionTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/FinalBreakTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/FinalFieldChoiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/IdleLoopTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/InvokeListenerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_AttrsTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_InvokeListenerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_NoJPFExecTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_RestorerTest$X.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/LocalVarInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/MethodListenerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/NoJPFExecTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/NullTrackerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/OOMEInjectorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/OVHeapTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/RecursiveLockTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/RestorerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/SearchMultipleTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/SharedPropagationTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/SharedRefTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/SkipInstructionTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/StackDepthCheckerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/StatelessTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/TraceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/TransitionLengthTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/UnlockNonSharedTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/basic/VerifyTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/CGCreatorFactoryTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/CrossingTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/DataChoiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/DynamicAbstractionTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/EventGeneratorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/JSONTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/NativeStateHolderTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/NumericValueCheckerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/ObjectStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/PerturbatorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/RandomTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/StopWatchFuzzerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/TimeModelTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/data/TypedObjectChoiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/AtomicTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/ClinitTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/DaemonTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/DeadlockTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/ExceptionalThreadChoiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/FinalizerThreadTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/HORaceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/JPF_gov_nasa_jpf_test_mc_threads_ExceptionalThreadChoiceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/MinimizePreemptionTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/MissedPathTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/NestedInitTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/OldClassicTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/RaceTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest-output [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/AnnotationTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/ArrayTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/AssertTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/CastTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/ClassInitTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/EndStateListener.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/EndStateTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/EnumTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/ExceptionHandlingTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/FieldTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/InitializeInterfaceClassObjectRefTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/JPFAttrAnnotationTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/LargeCodeTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/MethodTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/OutOfMemoryErrorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/basic/RecursiveClinitTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/ArrayTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/ConstructorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/FieldTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/MethodTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/ProxyTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/reflection/ReflectionTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/InterruptTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/JoinTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/LockedStackDepthTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/SuspendResumeTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/ThreadExceptionHandlerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/ThreadStopTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/ThreadTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/vm/threads/WaitTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/xerces/SAXParserTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/xerces/http%^^www.puppycrawl.com^dtds^configuration_1_3.dtd [new file with mode: 0644]
src/tests/gov/nasa/jpf/test/xerces/sun_checks.xml [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/ArrayIntSetTestBase.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/ArrayObjectQueueTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/AvailableBufferedInputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/BitSet1024Test.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/BitSet256Test.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/BitSet64Test.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/CommitOutputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/IdentityArrayObjectSetTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/IntTableTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/IntVectorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/LimitedInputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/LocationSpecTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/MethodSpecTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/OATHashTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/ObjVectorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/ObjectListTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/PSIntMapTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/PermutationGeneratorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SortedArrayIntSetTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SortedArrayObjectSetTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SparseClusterArrayTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SparseIntVectorTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SplitInputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/SplitOutputStreamTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/StringSetMatcherTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/UnsortedArrayIntSetTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/event/EventTreeTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/json/JSONLexerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/util/json/JSONParserTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/AnnotationInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/ClassLoaderInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/ElementInfoTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/SystemStateTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/TypesTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromListTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromSetTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/FinalizerThreadTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_MethodTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_NativePeerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/MethodTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/NativePeerTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/StringTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/ThreadTest.java [new file with mode: 0644]
src/tests/gov/nasa/jpf/vm/multiProcess/TypeSeparationTest.java [new file with mode: 0644]
src/tests/java8/DefaultMethodTest.java [new file with mode: 0644]
src/tests/java8/JPF_java8_DefaultMethodTest$G1.java [new file with mode: 0644]
src/tests/java8/LambdaTest.java [new file with mode: 0644]
src/tests/java8/TypeAnnotationTest.java [new file with mode: 0644]

diff --git a/.classpath b/.classpath
new file mode 100644 (file)
index 0000000..2721217
--- /dev/null
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+       <classpathentry kind="src" output="build/main" path="src/main"/>
+       <classpathentry kind="src" output="build/peers" path="src/peers"/>
+       <classpathentry kind="src" output="build/classes" path="src/classes"/>
+       <classpathentry kind="src" output="build/annotations" path="src/annotations"/>
+       <classpathentry kind="src" output="build/examples" path="src/examples"/>
+       <classpathentry kind="src" output="build/tests" path="src/tests"/>
+       <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
+       <classpathentry kind="output" path="build"/>
+</classpath>
diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..345194d
--- /dev/null
@@ -0,0 +1,17 @@
+.hg
+.hgignore
+local.properties
+.version
+.idea/workspace.xml
+.idea/fileColors.xml
+.idea/uiDesigner.xml
+nbproject/private/*
+build/*
+dist/*
+tmp/*
+trace
+activity*.png
+*.class
+*.orig
+*~
+*.DS_Store
diff --git a/.idea/.name b/.idea/.name
new file mode 100644 (file)
index 0000000..3836a03
--- /dev/null
@@ -0,0 +1 @@
+jpf-core
\ No newline at end of file
diff --git a/.idea/annotations.iml b/.idea/annotations.iml
new file mode 100644 (file)
index 0000000..f8666fe
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/annotations" />
+    <output-test url="file://$MODULE_DIR$/build/annotations" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/annotations">
+      <sourceFolder url="file://$MODULE_DIR$/src/annotations" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+</module>
diff --git a/.idea/artifacts/RunJPF.xml b/.idea/artifacts/RunJPF.xml
new file mode 100644 (file)
index 0000000..71c8dfa
--- /dev/null
@@ -0,0 +1,33 @@
+<component name="ArtifactManager">
+  <artifact type="jar" build-on-make="true" name="RunJPF">
+    <output-path>$PROJECT_DIR$/build</output-path>
+    <root id="archive" name="RunJPF.jar">
+      <element id="directory" name="META-INF">
+        <element id="file-copy" path="$PROJECT_DIR$/META-INF/RunJPF/MANIFEST.MF" />
+      </element>
+      <element id="directory" name="gov">
+        <element id="directory" name="nasa">
+          <element id="directory" name="jpf">
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/Config.class" />
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/Config$MissingRequiredKeyException.class" />
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/JPFClassLoader.class" />
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/JPFConfigException.class" />
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/JPFShell.class" />
+            <element id="directory" name="tool">
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/tool/Run.class" />
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/tool/RunJPF.class" />
+            </element>
+            <element id="directory" name="util">
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/JPFSiteUtils.class" />
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/FileUtils.class" />
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/StringMatcher.class" />
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/Pair.class" />
+            </element>
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/JPFException.class" />
+            <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/ConfigChangeListener.class" />
+          </element>
+        </element>
+      </element>
+    </root>
+  </artifact>
+</component>
\ No newline at end of file
diff --git a/.idea/artifacts/jpf.xml b/.idea/artifacts/jpf.xml
new file mode 100644 (file)
index 0000000..d43581a
--- /dev/null
@@ -0,0 +1,21 @@
+<component name="ArtifactManager">
+  <artifact type="jar" build-on-make="true" name="jpf">
+    <output-path>$PROJECT_DIR$/build</output-path>
+    <root id="archive" name="jpf.jar">
+      <element id="module-output" name="main" />
+      <element id="module-output" name="peers" />
+      <element id="module-output" name="annotations" />
+      <element id="directory" name="org">
+        <element id="directory" name="junit">
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/After.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/AfterClass.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/Before.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/BeforeClass.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/Ignore.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/Test$None.class" />
+          <element id="file-copy" path="$PROJECT_DIR$/build/classes/org/junit/Test.class" />
+        </element>
+      </element>
+    </root>
+  </artifact>
+</component>
\ No newline at end of file
diff --git a/.idea/artifacts/jpf_annotations.xml b/.idea/artifacts/jpf_annotations.xml
new file mode 100644 (file)
index 0000000..0e485a2
--- /dev/null
@@ -0,0 +1,8 @@
+<component name="ArtifactManager">
+  <artifact type="jar" build-on-make="true" name="jpf-annotations">
+    <output-path>$PROJECT_DIR$/build</output-path>
+    <root id="archive" name="jpf-annotations.jar">
+      <element id="module-output" name="annotations" />
+    </root>
+  </artifact>
+</component>
\ No newline at end of file
diff --git a/.idea/artifacts/jpf_classes.xml b/.idea/artifacts/jpf_classes.xml
new file mode 100644 (file)
index 0000000..42eabbb
--- /dev/null
@@ -0,0 +1,25 @@
+<component name="ArtifactManager">
+  <artifact type="jar" build-on-make="true" name="jpf-classes">
+    <output-path>$PROJECT_DIR$/build</output-path>
+    <root id="archive" name="jpf-classes.jar">
+      <element id="module-output" name="classes" />
+      <element id="module-output" name="annotations" />
+      <element id="directory" name="gov">
+        <element id="directory" name="nasa">
+          <element id="directory" name="jpf">
+            <element id="directory" name="vm">
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/vm/Verify.class" />
+            </element>
+            <element id="directory" name="util">
+              <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/TypeRef.class" />
+              <element id="directory" name="test">
+                <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/test/TestJPF.class" />
+                <element id="file-copy" path="$PROJECT_DIR$/build/main/gov/nasa/jpf/util/test/TestMultiProcessJPF.class" />
+              </element>
+            </element>
+          </element>
+        </element>
+      </element>
+    </root>
+  </artifact>
+</component>
\ No newline at end of file
diff --git a/.idea/classes.iml b/.idea/classes.iml
new file mode 100644 (file)
index 0000000..d41594e
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/classes" />
+    <output-test url="file://$MODULE_DIR$/build/classes" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/classes">
+      <sourceFolder url="file://$MODULE_DIR$/src/classes" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="annotations" />
+    <orderEntry type="module" module-name="main" />
+  </component>
+</module>
diff --git a/.idea/codeStyleSettings.xml b/.idea/codeStyleSettings.xml
new file mode 100644 (file)
index 0000000..7fb8ed0
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectCodeStyleSettingsManager">
+    <option name="PER_PROJECT_SETTINGS">
+      <value>
+        <XML>
+          <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+        </XML>
+      </value>
+    </option>
+    <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default (1)" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644 (file)
index 0000000..8e650cc
--- /dev/null
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <option name="DEFAULT_COMPILER" value="Javac" />
+    <resourceExtensions />
+    <wildcardResourcePatterns>
+      <entry name="!?*.java" />
+      <entry name="!?*.form" />
+      <entry name="!?*.class" />
+      <entry name="!?*.groovy" />
+      <entry name="!?*.scala" />
+      <entry name="!?*.flex" />
+      <entry name="!?*.kt" />
+      <entry name="!?*.clj" />
+    </wildcardResourcePatterns>
+    <annotationProcessing>
+      <profile default="true" name="Default" enabled="false">
+        <processorPath useClasspath="true" />
+      </profile>
+    </annotationProcessing>
+    <bytecodeTargetLevel target="1.8" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644 (file)
index 0000000..e7bedf3
--- /dev/null
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+  <settings default="" />
+</component>
\ No newline at end of file
diff --git a/.idea/encodings.xml b/.idea/encodings.xml
new file mode 100644 (file)
index 0000000..d821048
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
\ No newline at end of file
diff --git a/.idea/examples.iml b/.idea/examples.iml
new file mode 100644 (file)
index 0000000..1d41ef7
--- /dev/null
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/examples" />
+    <output-test url="file://$MODULE_DIR$/build/examples" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/examples">
+      <sourceFolder url="file://$MODULE_DIR$/src/examples" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="annotations" />
+    <orderEntry type="module" module-name="main" />
+    <orderEntry type="module" module-name="peers" />
+  </component>
+</module>
diff --git a/.idea/main.iml b/.idea/main.iml
new file mode 100644 (file)
index 0000000..c393d6a
--- /dev/null
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/main" />
+    <output-test url="file://$MODULE_DIR$/build/main" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/main">
+      <sourceFolder url="file://$MODULE_DIR$/src/main" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="annotations" />
+  </component>
+</module>
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644 (file)
index 0000000..4bda5bf
--- /dev/null
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ClientPropertiesManager">
+    <properties class="javax.swing.AbstractButton">
+      <property name="hideActionText" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JComponent">
+      <property name="html.disable" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JEditorPane">
+      <property name="JEditorPane.w3cLengthUnits" class="java.lang.Boolean" />
+      <property name="JEditorPane.honorDisplayProperties" class="java.lang.Boolean" />
+      <property name="charset" class="java.lang.String" />
+    </properties>
+    <properties class="javax.swing.JList">
+      <property name="List.isFileList" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JPasswordField">
+      <property name="JPasswordField.cutCopyAllowed" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JSlider">
+      <property name="Slider.paintThumbArrowShape" class="java.lang.Boolean" />
+      <property name="JSlider.isFilled" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JTable">
+      <property name="Table.isFileList" class="java.lang.Boolean" />
+      <property name="JTable.autoStartsEdit" class="java.lang.Boolean" />
+      <property name="terminateEditOnFocusLost" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JToolBar">
+      <property name="JToolBar.isRollover" class="java.lang.Boolean" />
+    </properties>
+    <properties class="javax.swing.JTree">
+      <property name="JTree.lineStyle" class="java.lang.String" />
+    </properties>
+    <properties class="javax.swing.text.JTextComponent">
+      <property name="caretAspectRatio" class="java.lang.Double" />
+      <property name="caretWidth" class="java.lang.Integer" />
+    </properties>
+  </component>
+  <component name="EntryPointsManager">
+    <entry_points version="2.0" />
+  </component>
+  <component name="ProjectLevelVcsManager" settingsEditedManually="false">
+    <OptionsSetting value="true" id="Add" />
+    <OptionsSetting value="true" id="Remove" />
+    <OptionsSetting value="true" id="Checkout" />
+    <OptionsSetting value="true" id="Update" />
+    <OptionsSetting value="true" id="Status" />
+    <OptionsSetting value="true" id="Edit" />
+    <ConfirmationsSetting value="0" id="Add" />
+    <ConfirmationsSetting value="0" id="Remove" />
+  </component>
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" default="false" assert-keyword="true" jdk-15="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644 (file)
index 0000000..769a3f3
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/annotations.iml" filepath="$PROJECT_DIR$/.idea/annotations.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/classes.iml" filepath="$PROJECT_DIR$/.idea/classes.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/examples.iml" filepath="$PROJECT_DIR$/.idea/examples.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/main.iml" filepath="$PROJECT_DIR$/.idea/main.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/peers.iml" filepath="$PROJECT_DIR$/.idea/peers.iml" />
+      <module fileurl="file://$PROJECT_DIR$/.idea/tests.iml" filepath="$PROJECT_DIR$/.idea/tests.iml" />
+    </modules>
+  </component>
+</project>
\ No newline at end of file
diff --git a/.idea/peers.iml b/.idea/peers.iml
new file mode 100644 (file)
index 0000000..23675dd
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/peers" />
+    <output-test url="file://$MODULE_DIR$/build/peers" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/peers">
+      <sourceFolder url="file://$MODULE_DIR$/src/peers" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="main" />
+    <orderEntry type="module" module-name="annotations" />
+  </component>
+</module>
diff --git a/.idea/runConfigurations/run_example_jpf.xml b/.idea/runConfigurations/run_example_jpf.xml
new file mode 100644 (file)
index 0000000..78a37b9
--- /dev/null
@@ -0,0 +1,25 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="run-example-jpf" type="ContextRunConfiguration" factoryName="ContextRun">
+    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+    <option name="MAIN_CLASS_NAME" value="gov.nasa.jpf.JPF" />
+    <option name="VM_PARAMETERS" value="-Xmx1024m -ea" />
+    <option name="PROGRAM_PARAMETERS" value="$FilePath$" />
+    <option name="WORKING_DIRECTORY" value="" />
+    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+    <option name="ALTERNATIVE_JRE_PATH" value="" />
+    <option name="ENABLE_SWING_INSPECTOR" value="false" />
+    <option name="ENV_VARIABLES" />
+    <option name="PASS_PARENT_ENVS" value="true" />
+    <module name="examples" />
+    <envs />
+    <RunnerSettings RunnerId="Debug">
+      <option name="DEBUG_PORT" value="" />
+      <option name="TRANSPORT" value="0" />
+      <option name="LOCAL" value="true" />
+    </RunnerSettings>
+    <RunnerSettings RunnerId="Run" />
+    <ConfigurationWrapper RunnerId="Debug" />
+    <ConfigurationWrapper RunnerId="Run" />
+    <method />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/runConfigurations/run_test.xml b/.idea/runConfigurations/run_test.xml
new file mode 100644 (file)
index 0000000..fa0eace
--- /dev/null
@@ -0,0 +1,19 @@
+<component name="ProjectRunConfigurationManager">
+  <configuration default="false" name="run-test" type="ContextRunConfiguration" factoryName="ContextRun">
+    <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+    <option name="MAIN_CLASS_NAME" value="gov.nasa.jpf.tool.RunTest" />
+    <option name="VM_PARAMETERS" value="-Xmx1024m -ea" />
+    <option name="PROGRAM_PARAMETERS" value="$FileClass$ $SelectedText$" />
+    <option name="WORKING_DIRECTORY" value="" />
+    <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="false" />
+    <option name="ALTERNATIVE_JRE_PATH" value="" />
+    <option name="ENABLE_SWING_INSPECTOR" value="false" />
+    <option name="ENV_VARIABLES" />
+    <option name="PASS_PARENT_ENVS" value="true" />
+    <module name="tests" />
+    <envs />
+    <RunnerSettings RunnerId="Run" />
+    <ConfigurationWrapper RunnerId="Run" />
+    <method />
+  </configuration>
+</component>
\ No newline at end of file
diff --git a/.idea/scopes/scope_settings.xml b/.idea/scopes/scope_settings.xml
new file mode 100644 (file)
index 0000000..922003b
--- /dev/null
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+  <state>
+    <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+  </state>
+</component>
\ No newline at end of file
diff --git a/.idea/tests.iml b/.idea/tests.iml
new file mode 100644 (file)
index 0000000..ac9f595
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="JAVA_MODULE" version="4">
+  <component name="NewModuleRootManager" inherit-compiler-output="false">
+    <output url="file://$MODULE_DIR$/build/tests" />
+    <output-test url="file://$MODULE_DIR$/build/tests" />
+    <exclude-output />
+    <content url="file://$MODULE_DIR$/src/tests">
+      <sourceFolder url="file://$MODULE_DIR$/src/tests" isTestSource="false" />
+    </content>
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+    <orderEntry type="module" module-name="annotations" />
+    <orderEntry type="module" module-name="main" />
+    <orderEntry type="module" module-name="classes" />
+    <orderEntry type="module" module-name="peers" />
+  </component>
+</module>
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644 (file)
index 0000000..e161617
--- /dev/null
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="hg4idea" />
+  </component>
+</project>
\ No newline at end of file
diff --git a/.project b/.project
new file mode 100644 (file)
index 0000000..d43fdf8
--- /dev/null
+++ b/.project
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+       <name>jpf-core</name>
+       <comment></comment>
+       <projects>
+       </projects>
+       <buildSpec>
+    <buildCommand>
+      <name>org.eclipse.jdt.core.javabuilder</name>
+      <arguments>
+      </arguments>
+    </buildCommand>
+               <buildCommand>
+                       <name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
+                       <arguments>
+                               <dictionary>
+                                       <key>LaunchConfigHandle</key>
+                                       <value>&lt;project&gt;/eclipse/AntBuilder.launch</value>
+                               </dictionary>
+                       </arguments>
+               </buildCommand>
+       </buildSpec>
+       <natures>
+               <nature>org.eclipse.jdt.core.javanature</nature>
+       </natures>
+</projectDescription>
diff --git a/LICENSE-2.0.txt b/LICENSE-2.0.txt
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/META-INF/RunJPF/MANIFEST.MF b/META-INF/RunJPF/MANIFEST.MF
new file mode 100644 (file)
index 0000000..a270c0c
--- /dev/null
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: gov.nasa.jpf.tool.RunJPF
+
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..8fbfa95
--- /dev/null
+++ b/README
@@ -0,0 +1,36 @@
+                          Java PathFinder README
+                          ======================
+
+========General Information about JPF ===================
+
+All the latest developments, changes, documentation can
+be found at: 
+
+http://babelfish.arc.nasa.gov/trac/jpf/wiki
+
+
+========Building and Installing =========================
+
+If you are having problems installing and running JPF
+please look at the documentation on the wiki at:
+
+http://babelfish.arc.nasa.gov/trac/jpf/wiki/install/start
+
+A lot of the commonly problems during the install and build
+process have been documented on the wiki. Please make sure
+that the the issue you are running into is not addressed
+there; if is not then feel free to contact us at
+java-pathfinder@googlegroups.com
+
+
+======Documentation======================================
+
+There is a constant effort to update and add JPF
+documentation on the wiki. If you would like to contribute
+in that, please contact us at
+java-pathfinder@googlegroups.com
+
+
+
+Happy Verification
+-- the Java PathFinder team
diff --git a/bin/javajpf b/bin/javajpf
new file mode 100755 (executable)
index 0000000..cc3fe37
--- /dev/null
@@ -0,0 +1,10 @@
+# shell script to start arbitrary classes through a JPFClassLoader
+
+JPF_HOME=`dirname "$0"`/..
+
+if test -z "$JVM_FLAGS"; then
+  JVM_FLAGS="-Xmx1024m -ea"
+fi
+
+
+java -ea -cp "$JPF_HOME/build/main:$JPF_HOME/build/peers:$JPF_HOME/build/annotations:$JPF_HOME/build/tests:$JPF_HOME/lib/bcel.jar:$JPF_HOME/lib/junit-4.10.jar" gov.nasa.jpf.Main -a "$@"
diff --git a/bin/jpf b/bin/jpf
new file mode 100755 (executable)
index 0000000..6db4727
--- /dev/null
+++ b/bin/jpf
@@ -0,0 +1,13 @@
+#!/bin/bash
+#
+# unix shell script to run jpf
+#
+
+JPF_HOME=`dirname "$0"`/..
+
+if test -z "$JVM_FLAGS"; then
+  JVM_FLAGS="-Xmx1024m -ea"
+fi
+
+java $JVM_FLAGS -jar "$JPF_HOME/build/RunJPF.jar" "$@"
+
diff --git a/bin/jpf.bat b/bin/jpf.bat
new file mode 100755 (executable)
index 0000000..a891c21
--- /dev/null
@@ -0,0 +1,13 @@
+REM
+REM overly simplified batch file to start JPF from a command prompt
+REM
+
+@echo off
+
+REM Set the JPF_HOME directory
+set JPF_HOME=%~dp0..
+
+set JVM_FLAGS=-Xmx1024m -ea
+
+java %JVM_FLAGS% -jar "%JPF_HOME%\build\RunJPF.jar" %*
+
diff --git a/bin/print_class b/bin/print_class
new file mode 100755 (executable)
index 0000000..31534ef
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+JPF_HOME=`dirname "$0"`/..
+
+java -classpath "$JPF_HOME/build/jpf.jar" gov.nasa.jpf.jvm.ClassFilePrinter "$@"
diff --git a/bin/print_class.bat b/bin/print_class.bat
new file mode 100755 (executable)
index 0000000..643790a
--- /dev/null
@@ -0,0 +1,8 @@
+
+@echo off
+
+REM Set the JPF_HOME directory
+set JPF_HOME=%~dp0..
+
+java -classpath "%JPF_HOME%\build\jpf.jar" gov.nasa.jpf.classfile.ClassFilePrinter %*
+
diff --git a/bin/print_events b/bin/print_events
new file mode 100755 (executable)
index 0000000..3c95eb7
--- /dev/null
@@ -0,0 +1,5 @@
+#!/bin/sh
+
+JPF_HOME=`dirname "$0"`/..
+
+java -classpath "$JPF_HOME/build/jpf.jar" gov.nasa.jpf.tool.PrintEvents "$@"
diff --git a/bin/test b/bin/test
new file mode 100755 (executable)
index 0000000..c2d1c5d
--- /dev/null
+++ b/bin/test
@@ -0,0 +1,13 @@
+#!/bin/bash
+#
+# unix shell script to run jpf
+#
+
+JPF_HOME=`dirname "$0"`/..
+
+if test -z "$JVM_FLAGS"; then
+  JVM_FLAGS="-Xmx1024m -ea"
+fi
+
+java $JVM_FLAGS -jar "$JPF_HOME/build/RunTest.jar" "$@"
+
diff --git a/bin/test.bat b/bin/test.bat
new file mode 100755 (executable)
index 0000000..c4351c3
--- /dev/null
@@ -0,0 +1,13 @@
+REM
+REM overly simplified batch file to start JPF tests from a command prompt
+REM
+
+@echo off
+
+REM Set the JPF_HOME directory
+set JPF_HOME=%~dp0..
+
+set JVM_FLAGS=-Xmx1024m -ea
+
+java %JVM_FLAGS% -jar "%JPF_HOME%\build\RunTest.jar" %*
+
diff --git a/build.properties b/build.properties
new file mode 100644 (file)
index 0000000..6602f96
--- /dev/null
@@ -0,0 +1,22 @@
+#JPF core build info
+#Fri Jan 13 13:32:58 PST 2012
+
+revision=647\:b8b86ac8f503
+
+date.tip=2012-01-13 13\:30 -0800
+
+author=Peter Mehlitz <Peter.C.Mehlitz@nasa.gov>
+
+repository=file\://flyer/Users/pmehlitz/projects/eclipse/jpf-core
+
+upstream=http\://babelfish.arc.nasa.gov/hg/jpf/jpf-core
+
+java.version=1.6.0_26
+
+os.arch=x86_64
+
+os.name=Mac OS X
+
+os.version=10.5.8
+
+user.country=US
diff --git a/build.xml b/build.xml
new file mode 100644 (file)
index 0000000..ca73885
--- /dev/null
+++ b/build.xml
@@ -0,0 +1,456 @@
+<?xml version="1.0" ?>
+
+<!--
+  build.xml - the JPF core build script
+              using Ant (http://jakarta.apache.org/ant)
+  public targets:
+
+    build (default)   compile classes and build JPF jar files
+    compile           compile JPF and its specific (modeled) environment libraries
+    test              run all JPF tests
+    clean             remove the files that have been generated by the build process
+    buildinfo         create buildinfo properties file
+-->
+
+<project name="jpf-core" default="build" basedir=".">
+
+  <!-- ===================== ===== COMMON SECTION ========================== -->
+
+  <!-- 
+    local props have to come first, because Ant properties are immutable
+    NOTE: this file is local - it is never in the repository!
+  -->
+  <property file="local.properties"/>
+  <property environment="env"/>
+  
+  <!-- compiler settings -->
+  <property name="debug"         value="on"/>
+  <property name="deprecation"   value="on"/>
+
+  <uptodate property="build_uptodate" targetfile="build/main/gov/nasa/jpf/build.properties" srcfile="build.properties"/>
+
+
+  <!-- generic classpath settings -->
+  <path id="lib.path">
+    <pathelement location="build/main"/>
+    <pathelement location="build/peers"/>
+    <pathelement location="build/annotations"/>
+    <fileset dir=".">
+         <include name="lib/*.jar"/>
+    </fileset>
+  </path>
+
+
+  <!-- init: common initialization -->
+  <target name="-init">
+    <tstamp/>
+
+    <mkdir dir="build"/>               <!-- the build root -->
+    
+    <!-- the things that have to be in the classpath of whatever runs Ant -->
+    <available property="have_javac" classname="com.sun.tools.javac.Main"/>
+    <fail unless="have_javac">no javac was found __or__ check http://babelfish.arc.nasa.gov/trac/jpf/wiki/install/build for possible solutions</fail>
+
+
+    <available file="src/main"        type="dir" property="have_main"/>
+    <available file="src/annotations" type="dir" property="have_annotations"/>
+    <available file="src/peers"       type="dir" property="have_peers"/>
+    <available file="src/classes"     type="dir" property="have_classes"/>
+    <available file="src/tests"       type="dir" property="have_tests"/>
+    <available file="src/examples"    type="dir" property="have_examples"/>
+
+    <available file=".hg"             type="dir" property="have_hg"/>
+
+    <!-- for the core, it's a fail if any of these is missing -->
+    <fail unless="have_main">no src/main</fail>
+    <fail unless="have_annotations">no src/annotations</fail>
+    <fail unless="have_peers">no src/peers</fail>
+    <fail unless="have_classes">no src/classes</fail>
+    <fail unless="have_tests">no src/tests</fail>
+    <fail unless="have_examples">no src/examples</fail>
+
+    <condition property="have_java8">
+        <equals arg1="${ant.java.version}" arg2="1.8"/>
+    </condition>
+
+  </target>
+
+
+  <!-- ======================= COMPILE SECTION ============================= -->
+    
+  <!-- public compile -->
+  <target name="compile" depends="-init,-compile-annotations,-compile-main,-compile-peers,-compile-classes,-compile-tests,-compile-examples"
+          description="compile all JPF core sources" >
+         <copy file="build.properties" todir="build/main/gov/nasa/jpf" failonerror="false"/>
+  </target>
+
+  <target name="-compile-annotations" if="have_annotations">
+    <mkdir dir="build/annotations"/>
+    <javac srcdir="src/annotations" destdir="build/annotations" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}" classpath=""/>
+  </target>
+
+  <target name="-compile-main" if="have_main">
+    <mkdir dir="build/main"/>
+    <javac srcdir="src/main" destdir="build/main" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}" classpathref="lib.path">
+       <!--
+        <compilerarg value="-XDenableSunApiLintControl"/>
+        <compilerarg value="-Xlint:all"/>
+        -->
+    </javac>
+
+  </target>
+  
+  <target name="-compile-peers" if="have_peers" depends="-compile-main" >
+    <mkdir dir="build/peers"/>
+    <javac srcdir="src/peers" destdir="build/peers" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}" classpathref="lib.path">
+       <compilerarg value="-XDenableSunApiLintControl"/>
+       <compilerarg value="-Xlint:all,-sunapi,-serial"/>       
+     </javac>
+  </target>
+  
+  <target name="-compile-classes" if="have_classes" depends="-compile-annotations,-compile-main" >
+    <mkdir dir="build/classes"/>
+    <javac srcdir="src/classes" destdir="build/classes" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}">
+      <compilerarg value="-XDenableSunApiLintControl"/>
+      <compilerarg value="-Xlint:all,-sunapi"/>   
+      <classpath>
+        <path refid="lib.path"/>
+        <pathelement location="build/annotations"/>
+      </classpath>
+    </javac>
+  </target>
+  
+  <target name="-compile-tests" if="have_tests" depends="-compile-annotations,-compile-main,-compile-classes">
+    <mkdir dir="build/tests"/>
+
+    <javac sourcepath="" srcdir="src/tests" destdir="build/tests" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}"
+           includes="*,gov/nasa/jpf/**,classloader_specific_tests/**">
+      <compilerarg value="-XDenableSunApiLintControl"/>
+      <compilerarg value="-Xlint:all,-sunapi,-serial,-rawtypes,-unchecked"/>
+
+      <include name="*gov/nasa/jpf/**"/>
+      <include name="classloader_specific_tests/**"/>
+      <include name="java8/**" if="have_java8"/>
+
+      <classpath>
+        <path refid="lib.path"/>
+        <pathelement location="build/annotations"/>
+        <pathelement location="build/classes"/>
+      </classpath>       
+    </javac>
+  </target>
+
+  <target name="-compile-examples" if="have_examples" depends="-compile-annotations,-compile-main">
+    <mkdir dir="build/examples" />
+    <javac srcdir="src/examples" destdir="build/examples" includeantruntime="false"
+           debug="${debug}" deprecation="${deprecation}"
+           classpathref="lib.path"/>
+  </target>
+
+  
+  <!-- ======================= MISC SECTION ================================ -->
+  
+  <target name="-version" if="have_hg">
+    <exec executable="hg" outputproperty="version" searchpath="true" failonerror="false" failifexecutionfails="false">
+      <arg value="identify"/>
+      <arg value="-n"/> 
+    </exec>
+       
+    <!-- .version is in .hgignore -->
+    <echo message="${version}${line.separator}" file=".version"/>
+  </target>
+       
+  <!-- build jars -->
+  <target name="build" depends="-cond-clean,compile,-version"
+        description="generate the core JPF jar files" >
+
+    <copy file=".version" todir="build/main/gov/nasa/jpf" failonerror="false"/>
+
+    <jar jarfile="build/jpf-classes.jar">
+      <fileset dir="build/classes"/>
+      <fileset dir="build/annotations"/>
+
+      <fileset dir="build/main">
+          <!-- we need this one in case a SUT uses the Verify API -->
+          <include name="gov/nasa/jpf/vm/Verify.class"/>
+
+          <!-- these are required if we run TestJPF derived test classes -->
+          <include name="gov/nasa/jpf/JPFShell.class"/>
+          <include name="gov/nasa/jpf/util/TypeRef.class"/>
+          <include name="gov/nasa/jpf/util/test/TestJPF.class"/>
+          <include name="gov/nasa/jpf/util/test/TestMultiProcessJPF.class"/>
+          <include name="gov/nasa/jpf/util/test/TestJPFHelper.class"/>
+      </fileset>
+    </jar>
+
+    <jar jarfile="build/jpf.jar" index="true">
+      <fileset dir="build/main"/>
+      <fileset dir="build/peers"/>
+      <!-- this is redundant, but if JPF is executed from java.class.path it wouldn't find annotations -->
+      <fileset dir="build/annotations"/>
+      
+      <!-- this is for annotations used by JPF regression tests, which can also be executed outside of junit -->
+      <fileset dir="build/classes">
+          <include name="org/junit/*.class"/>
+      </fileset>
+      
+      <manifest>
+        <attribute name="Built-By" value="${user.name}"/>
+        <attribute name="Implementation-Vendor" value="NASA Ames Research Center"/>
+        <attribute name="Implementation-Title" value="Java Pathfinder core system"/>
+        <attribute name="Implementation-Version" value="${jpf.version}"/>
+      </manifest>
+    </jar>
+
+    <!-- optional jar that contains annotations to be used in non-JPF dependent apps -->
+    <jar jarfile="build/jpf-annotations.jar">
+      <fileset dir="build/annotations"/>
+    </jar>
+
+    <!-- this jar is needed to test classloaders, it is used by URLClassLoaderTest -->
+    <jar jarfile="build/classloader_specific_tests.jar">
+      <fileset dir="build/tests">
+        <include name="classloader_specific_tests/*.class"/>
+         </fileset>
+    </jar>
+
+    <jar jarfile="build/RunJPF.jar">
+      <fileset dir="build/main">
+        <include name="gov/nasa/jpf/tool/Run.class"/>
+        <include name="gov/nasa/jpf/tool/RunJPF.class"/>
+        <include name="gov/nasa/jpf/Config.class"/>
+        <include name="gov/nasa/jpf/ConfigChangeListener.class"/>
+        <include name="gov/nasa/jpf/Config$MissingRequiredKeyException.class"/>
+        <include name="gov/nasa/jpf/JPFClassLoader.class"/>
+        <include name="gov/nasa/jpf/JPFShell.class"/>
+        <include name="gov/nasa/jpf/JPFException.class"/>
+        <include name="gov/nasa/jpf/JPFConfigException.class"/>
+        <include name="gov/nasa/jpf/JPFTargetException.class"/>
+        <include name="gov/nasa/jpf/util/JPFSiteUtils.class"/>
+        <include name="gov/nasa/jpf/util/FileUtils.class"/>
+        <include name="gov/nasa/jpf/util/StringMatcher.class"/>
+        <include name="gov/nasa/jpf/util/Pair.class"/>        
+      </fileset>
+      
+      <manifest>
+        <attribute name="Built-By" value="${user.name}"/>
+        <attribute name="Implementation-Vendor" value="NASA Ames Research Center"/>
+        <attribute name="Implementation-Title" value="Java Pathfinder core launch system"/>
+        <attribute name="Implementation-Version" value="${jpf.version}"/>
+        <attribute name="Main-Class" value="gov.nasa.jpf.tool.RunJPF"/>
+      </manifest>
+    </jar>
+
+    <jar jarfile="build/RunTest.jar">
+      <fileset dir="build/main">
+        <include name="gov/nasa/jpf/tool/Run.class"/>
+        <include name="gov/nasa/jpf/tool/RunTest.class"/>
+        <include name="gov/nasa/jpf/tool/RunTest$Failed.class"/>
+        <include name="gov/nasa/jpf/Config.class"/>
+        <include name="gov/nasa/jpf/ConfigChangeListener.class"/>
+        <include name="gov/nasa/jpf/Config$MissingRequiredKeyException.class"/>
+        <include name="gov/nasa/jpf/JPFClassLoader.class"/>
+        <include name="gov/nasa/jpf/JPFException.class"/>
+        <include name="gov/nasa/jpf/JPFConfigException.class"/>
+        <include name="gov/nasa/jpf/util/JPFSiteUtils.class"/>
+        <include name="gov/nasa/jpf/util/FileUtils.class"/>
+        <include name="gov/nasa/jpf/util/StringMatcher.class"/>
+        <include name="gov/nasa/jpf/util/DevNullPrintStream.class"/>
+      </fileset>
+      <manifest>
+        <attribute name="Built-By" value="${user.name}"/>
+        <attribute name="Implementation-Vendor" value="NASA Ames Research Center"/>
+        <attribute name="Implementation-Title" value="Java Pathfinder test launch system"/>
+        <attribute name="Implementation-Version" value="${jpf.version}"/>
+        <attribute name="Main-Class" value="gov.nasa.jpf.tool.RunTest"/>
+      </manifest>
+    </jar>
+
+  </target>
+
+
+  <!-- public clean: cleanup from previous tasks/builds -->
+  <target name="clean"
+          description="remove all build artifacts and temporary files">
+    <delete dir="build" failonerror="false"/>
+    <delete dir="tmp" failonerror="false"/>
+    <delete>
+      <fileset dir="." includes="**/*~" defaultexcludes="no" />
+      <fileset dir="." includes="**/*.bak" defaultexcludes="no" />
+      <fileset dir="." includes="**/error.xml" />
+    </delete>
+  </target>
+
+  <target name="-cond-clean" unless="build_uptodate"
+          description="remove all build artifacts and temporaries if build.properties has been changed">
+    <antcall target="clean"/>
+  </target>
+
+
+  <!-- generate buildinfo file  -->
+  <target name="buildinfo" description="create buildinfo properties">
+
+    <!-- make this fail if there are uncommitted changes -->
+    <exec executable="hg" outputproperty="uncommitted_changes" failifexecutionfails="true">
+      <arg value="status"/>
+    </exec>
+    <condition property="have_uncommitted_changes">
+      <length string="${uncommitted_changes}" trim="true" when="greater" length="0"/>
+    </condition>
+<!--
+    <fail if="have_uncommitted_changes">hg status shows uncommitted changes:
+      ${uncommitted_changes}
+    </fail>
+-->
+    <exec executable="hg" outputproperty="hg.tip.id" failifexecutionfails="false">
+      <arg value="tip"/>
+      <arg value="--template"/>
+      <arg value="{rev}:{node|short}\n"/>
+    </exec>
+    <exec executable="hg" outputproperty="hg.author" failifexecutionfails="false">
+      <arg value="tip"/>
+      <arg value="--template"/>
+      <arg value="{author}\n"/>
+    </exec>
+    <exec executable="hg" outputproperty="hg.tip.date" failifexecutionfails="false">
+      <arg value="tip"/>
+      <arg value="--template"/>
+      <arg value="{date|isodate}\n"/>
+    </exec>
+    <exec executable="hg" outputproperty="hg.paths.default" failifexecutionfails="false">
+      <arg value="showconfig"/>
+      <arg value="paths.default"/>
+    </exec>
+    
+    <exec executable="hostname" failifexecutionfails="false" outputproperty="env.COMPUTERNAME"/>
+    <property name="hostname" value="${env.COMPUTERNAME}"/>  <!-- Windows doesn't have hostname -->
+
+    <!-- it seems the 'propertyfile' task just appends -->
+    <delete file="build.properties" failonerror="false"/>
+
+    <propertyfile file="build.properties" comment="JPF core build info">
+      <entry key="revision" value="${hg.tip.id}"/>
+      <entry key="date.tip" value="${hg.tip.date}"/>
+
+      <entry key="author" value="${hg.author}"/>
+      <entry key="repository" value="file://${hostname}${basedir}"/>
+      <entry key="upstream" value="${hg.paths.default}"/>
+
+      <entry key="java.version" value="${java.version}"/>
+
+      <entry key="os.arch" value="${os.arch}"/>
+      <entry key="os.name" value="${os.name}"/>
+      <entry key="os.version" value="${os.version}"/>
+      <entry key="user.country" value="${user.country}"/>
+
+    </propertyfile>
+
+  </target>
+
+  <target name="dist" description="build binary distribution">
+    <delete file="build/${ant.project.name}*.zip"/>
+       
+    <exec executable="hg" outputproperty="hg.tip.rev" failifexecutionfails="false">
+      <arg value="tip"/>
+      <arg value="--template"/>
+      <arg value="-r{rev}"/>
+    </exec>
+       
+    <!-- 2do this seems stupid - there needs to be a better way to re-base (zip basedir fails miserably) -->
+    <zip destfile="build/${ant.project.name}${hg.tip.rev}.zip" update="false" excludes="*">
+      <zipfileset file="jpf.properties"  prefix="${ant.project.name}"/>
+      <zipfileset file="build.properties"  prefix="${ant.project.name}"/>
+      <zipfileset dir="lib"  prefix="${ant.project.name}/lib"/>
+      <zipfileset dir="bin"  prefix="${ant.project.name}/bin" filemode="754"/>
+      <zipfileset dir="build" includes="*.jar" prefix="${ant.project.name}/build"/>
+    </zip>
+  </target>
+
+  <target name="src-dist" description="build source distribution">
+    <delete file="build/${ant.project.name}*-src.zip"/>
+    
+    <exec executable="hg" outputproperty="hg.tip.rev" failifexecutionfails="false">
+      <arg value="tip"/>
+      <arg value="--template"/>
+      <arg value="-r{rev}"/>
+    </exec>
+       
+    <zip destfile="build/${ant.project.name}${hg.tip.rev}-src.zip" update="false" excludes="*" whenempty="skip">
+      <zipfileset file="jpf.properties"  prefix="${ant.project.name}"/>
+      <zipfileset file="build.properties"  prefix="${ant.project.name}"/>
+      <zipfileset file="build.xml"  prefix="${ant.project.name}"/>
+       <zipfileset file="LICENSE-2.0.txt"  prefix="${ant.project.name}"/>
+      <zipfileset file="README"  prefix="${ant.project.name}"/>
+      <zipfileset dir="src" prefix="${ant.project.name}/src"/>
+      <zipfileset dir="lib"  prefix="${ant.project.name}/lib" erroronmissingdir="false"/>
+      <zipfileset dir="bin"  prefix="${ant.project.name}/bin" filemode="754"/>
+      <zipfileset dir="tools" prefix="${ant.project.name}/tools" erroronmissingdir="false"/>
+
+      <!-- IDE related configuration files -->
+      <zipfileset file=".project"  prefix="${ant.project.name}"/>
+      <zipfileset file=".classpath"  prefix="${ant.project.name}"/>
+      <zipfileset dir="eclipse" prefix="${ant.project.name}/eclipse"/>
+
+      <zipfileset dir="nbproject" prefix="${ant.project.name}/nbproject"/>
+    </zip>
+  </target>
+
+  <!-- ======================= TEST SECTION ================================ -->
+
+
+
+  <target name="test" depends="build"
+          description="run core regression tests" if="have_tests">
+          
+    <!-- note this can be directly set in local.properties, which overrides this setting -->
+    <property name="junit.home" value="${env.JUNIT_HOME}"/>
+    
+    <condition property="junit.usefile">
+      <!-- don't set if this is running from within an IDE that collects output -->
+      <not>
+        <isset property="netbeans.home"/>  
+      </not>
+    </condition>
+    
+    <junit printsummary="on" showoutput="off" 
+           haltonfailure="no" logfailedtests="true" failureproperty="test.failed" 
+           dir="${basedir}" fork="yes" forkmode="perTest" maxmemory="1024m" outputtoformatters="true">
+      <formatter type="plain" usefile="${junit.usefile}"/>
+
+      <assertions>
+        <enable/>
+      </assertions>
+
+      <classpath>
+        <path refid="lib.path"/>
+        <pathelement location="build/tests"/>
+        <pathelement location="build/classes"/>
+        <pathelement location="build/annotations"/>
+        
+        <fileset dir="${junit.home}">
+          <include name="**/*.jar"/>
+        </fileset>  
+      </classpath>
+
+      <batchtest todir="build/tests">
+        <fileset dir="build/tests">
+          <exclude name="**/JPF_*.class"/>
+          <include name="**/*Test.class"/>
+               
+          <exclude name="**/SplitInputStreamTest.class"/>
+        </fileset>
+      </batchtest>
+
+    </junit>
+
+    <fail if="test.failed" />
+   
+  </target>
+
+  
+</project>
diff --git a/doc/devel/attributes.md b/doc/devel/attributes.md
new file mode 100644 (file)
index 0000000..69c7f83
--- /dev/null
@@ -0,0 +1,18 @@
+# The Attribute System #
+
+While JPF stores values for operands, local variables and fields very similar to a normal VM, it also features a storage extension mechanism that lets you associate arbitrary objects with stack slots (operands and locals), fields, and whole objects (ElementInfos). The attribute objects can be set/used in [native peers](mji) or [listeners](listener) to add state stored/restored information that automatically follows the data flow.
+
+Note that JPF does not restore attribute object values upon backtracking per default, only attribute references. If you need to make sure attribute values are restored, you have to use copy-on-write and then store back when accessing and modifying such attributes. 
+
+![Figure: JPF Attribute System](../graphics/attributes.svg){align=center width=650}
+
+JPF provides an API to set/access these attributes, which is located in `gov.nasa.jpf.vm.Fields` (for field attributes) and `gov.nasa.jpf.vm.StackFrame` (for local variables and operands). Once set, the VM copies the attributes each time it reads/writes the associated field or stackframe slot. 
+
+## Usage ##
+
+For example, such attributes can be used to represent symbolic values or numeric error bounds. It should be noted though that attributes impose additional runtime costs, which is also why we don't treat normal, concrete values just as a special kind of attribute (normal values are still stored separately as builtin types like `int`). The upside of this is that your attributes coexist with normal, concrete values, which for instance allows things like mixed symbolic and concrete execution.
+
+> **Note:** JPF now can associate attributes not only with fields of an object, but with the object as a whole. See the `gov.nasa.jpf.vm.ElementInfo` API for details.
+
+> **Note:** while there is an API to set/retrieve attributes based on type, there is no implementation
+yet that allows multiple attributes to be stored.
\ No newline at end of file
diff --git a/doc/devel/bytecode_factory.md b/doc/devel/bytecode_factory.md
new file mode 100644 (file)
index 0000000..6fb8c2c
--- /dev/null
@@ -0,0 +1,70 @@
+# Bytecode Factories #
+Normally, a VM defines the semantics of it's programming language. In case of Java, the corresponding instruction set represents a multi-threaded stack machine, where values are kept on the heap, or inside of local and/or operand slots within stack frames. The effect of Java bytecode instructions with respect to heap, locals and operands are described in [Sun's Java virtual machine specifications](http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html/).
+JPF is different. The VM of JPF and its associated constructs like `ThreadInfo`, `ClassInfo`, `ElementInfo` etc. provide all the necessary means to implement a normal Java interpreter. However, JPF delegates the use of these means to the instructions. Every bytecode that gets executed by JPF is represented by a corresponding `Instruction` object, which normally gets instantiated during class load time. The `Instruction` classes of the standard execution mode can be found in package `gov.nasa.jpf.jvm.bytecode`.
+
+When it comes to executing a bytecode, the VM simply calls the `execute()` method of this `Instruction` instance. The implementation of this method defines the execution semantics.
+
+The trick is now that JPF uses a configurable [abstract factory](http://en.wikipedia.org/wiki/Abstract_factory_pattern) to choose and instantiate the `Instruction` classes. By providing your own concrete `InstructionFactory`, together with a set of related `Instruction` classes, you can change the execution semantics of Java.
+
+![Figure: Bytecode Factories](../graphics/bc-factory.svg){align=center width=850}
+
+## Usages ##
+
+Why would it be useful to change the standard semantics? One reason is to extend normal semantics with additional checks. For example, this is performed by the JPF extension jpf-numeric which overrides numeric bytecode classes with versions that check for over-/underflow and silent NaN propagation (among other things). A much more involved example is the JPF extension jpf-symbc, which implements a symbolic execution mode for Java, e.g. to automatically generate test cases based on the program structure of an application. It does so by overriding branch instructions, turning them into state space branches represented by their own [ChoiceGenerators](choicegenerator), collecting the path conditions on the way, and feeding them to an external SAT solver.
+
+## Implementation ##
+
+Since there is a large number of Java bytecode instructions, it would be tedious having to implement all 250+ Instruction classes in order to override just a couple of them. You can reduce the effort in three ways:
+
+
+### GenericInstructionFactory ###
+
+
+Using the `GenericInstructionFactory` as a base class for your `InstructionFactory`. This only requires you to specify an alternative package where your bytecode classes reside, together with the set of bytecodes that should be overridden. The resulting code can be quite short, as can be seen in the *numeric* extension example:
+
+~~~~~~~~ {.java}
+public class NumericInstructionFactory extends GenericInstructionFactory {
+  // which bytecodes do we replace
+  static final String[] BC_NAMES = {
+    "DCMPG", "DCMPL",  "DADD", "DSUB", "DMUL", "DDIV",
+    "FCMPG", "FCMPL",  "FADD", "FSUB", "FMUL", "FDIV",
+                       "IADD", "ISUB", "IMUL", "IDIV",  "IINC", 
+                       "LADD", "LSUB", "LMUL", "LDIV"   
+  };
+  
+  // where do they reside
+  protected static final String BC_PREFIX = "gov.nasa.jpf.numeric.bytecode.";
+  
+  // what classes should use them
+  protected static final String[] DEFAULT_EXCLUDES = { "java.*", "javax.*" };
+  
+  public  NumericInstructionFactory (Config conf){    
+    super(conf, BC_PREFIX, BC_NAMES, null, DEFAULT_EXCLUDES);
+    
+    NumericUtils.init(conf);
+  }
+}
+~~~~~~~~
+
+
+### Super Delegation ###
+
+You can derive your overriding bytecode classes from the ones in `gov.nasa.jpf.jvm.bytecode`. If you just want to add some checks before or after performing the "normal" operation, you can use the standard `Instruction` classes as base classes, and call `super.execute(..)` from within your derived classes. 
+
+
+### Attributes ###
+
+As your execution semantics get more complex, you probably need to store and restore additional information that is associated with variables. JPF provides an automatically managed [attribute system](attributes) for this purpose. You can attach objects to locals, operands and fields, and JPF takes care of propagating these attribute objects whenever it manipulates stackframes or heap objects.
+
+
+## Configuration ##
+
+
+Configuring your bytecode factory just requires one JPF property, e.g.
+
+~~~~~~~~ {.bash}
+vm.insn_factory.class = gov.nasa.jpf.numeric.NumericInstructionFactory
+~~~~~~~~
+
+which can be either done from the command line or from within a *.jpf property file
diff --git a/doc/devel/choicegenerator.md b/doc/devel/choicegenerator.md
new file mode 100644 (file)
index 0000000..e8272eb
--- /dev/null
@@ -0,0 +1,212 @@
+# ChoiceGenerators #
+
+The goal of every model checker is to check if certain properties hold in states of the system under test. The way that choices are computed is a fundamental part of model checking, since they determine which states are checked. We refer to the mechanism used by JPF to capture choices as ChoiceGenerators.
+
+ChoiceGenerators can be approached from an application perspective, or from the JPF implementation perspective. In this section, we cover both perspectives.
+
+## Motivation ##
+
+Whenever the model checker reaches non-determinism in code, it needs to compute choices. Non-determinism can be due to thread scheduling or non-deterministic data acquisitions. Here, we present an example including data non-determinism to justify our implementation approach. JPF provides support for "random" data acquisition, using the interface `gov.nasa.jpf.jvm.Verify`.
+
+~~~~~~~~ {.java}
+...
+boolean b = Verify.getBoolean(); // evaluated by JPF for both `true` and `false`
+...
+~~~~~~~~
+
+This worked nicely for small sets of choice values (such as `{true,false}` for boolean), but the mechanism for enumerating all choices from a type specific interval becomes already questionable for large intervals (e.g. `Verify.getInt(0,10000)`), and fails completely if the data type does not allow finite choice sets at all (such as floating point types):
+
+![Figure 1: Motivation behind ChoiceGenerator](../graphics/cg-motivation.svg){align=center width=750}
+
+To handle this case, we have to leave the ideal world of model checking (that considers all possible choices), and make use of what we know about the real world - we have to use heuristics to make the set of choices finite and manageable. However, heuristics are application and domain specific, and it would be a bad idea to hardcode them into the test drivers we give JPF to analyze. This leads to a number of requirements for the JPF choice mechanism:
+
+  * choice mechanisms have to be decoupled (i.e. thread choices should be independent of data choices, double choices from int choices etc.)
+  * choice sets and enumeration should be encapsulated in dedicated, type specific objects. The VM should only know about the most basic types, and otherwise use a generic interface to obtain choices
+  * selection of classes representing (domain specific) heuristics, and parametrization of ChoiceGenerator instances should be possible at runtime, i.e. via JPF's configuration mechanism (properties)
+
+The diagram shown above depicts this with an example that uses a "randomly" chosen velocity value of type double. As an example heuristic we use a threshold model, i.e. we want to know how the system reacts below, at, and above a certain application specific value (threshold). We reduce an infinite set of choices to only three "interesting" ones. Of course, "interesting" is quite subjective, and we probably want to play with the values (delta, threshold, or even used heuristic) efficiently, without having to rebuild the application each time we run JPF.
+
+The code example does not mention the used `ChoiceGenerator` class (`DoubleThresholdGenerator`) at all, it just specifies a symbolic name `"velocity"`, which JPF uses to look up an associated class name from its configuration data (initialized via property files or the command line - see Configuring JPF Runtime Options). But it doesn't stop there. Most heuristics need further parameterization (e.g. threshold, delta), and we provide that by passing the JPF configuration data into the `ChoiceGenerator` constructors (e.g. the `velocity.threshold` property). Each `ChoiceGenerator` instance knows its symbolic name (e.g. `"velocity"`), and can use this name to look up whatever parameters it needs.
+
+## The JPF Perspective ##
+
+Having such a mechanism is nice to avoid test driver modification. But it would be much nicer to consistently use the same mechanism not just for data acquisition choices, but also scheduling choices (i.e. functionality that is not controlled by the test application). JPF's ChoiceGenerator mechanism does just this, but in order to understand it from an implementation perspective we have to take one step back and look at some JPF terminology:
+
+![Figure 2: States, Transitions and Choices](../graphics/cg-ontology.svg){align=center width=650}
+
+
+*State* is a snapshot of the current execution status of the application (mostly thread and heap states), plus the execution history (path) that lead to this state. Every state has a unique id number. State is encapsulated in the `SystemState` instance (almost, there is some execution history which is just kept by the JVM object). This includes three components:
+
+  * KernelState - the application snapshot (threads, heap)
+  * trail - the last Transition (execution history)
+  * current and next ChoiceGenerator - the objects encapsulating the choice enumeration that produces different transitions (but not necessarily new states)
+
+*Transition* is the sequence of instructions that leads from one state to the next. There is no context switch within a transition, it's all in the same thread. There can be multiple transitions leading out of one state (but not necessarily to a new state).
+
+*Choice* is what starts a new transition. This can be a different thread (i.e. scheduling choice), or different "random" data value.
+
+In other words, possible existence of choices is what terminates the last transition, and selection of a choice value precludes the next transition. The first condition corresponds to creating a new `ChoiceGenerator`, and letting the `SystemState` know about it. The second condition means to query the next choice value from this `ChoiceGenerator` (either internally within the VM, or in an instruction or native method).
+
+## How it comes to Life ##
+With this terminology, we are ready to have a look at how it all works. Let's assume we are in a transition that executes a `getfield` bytecode instruction (remember, JPF executes Java bytecode), and the corresponding object that owns this field is shared between threads. For simplicity's sake, let's further assume there is no synchronization when accessing this object, (or we have turned off the property `vm.sync_detection`). Let's also assume there are other runnable threads at this point. Then we have a choice - the outcome of the execution might depend on the order in which we schedule threads, and hence access this field. There might be a data race.
+
+![Figure 3: ChoiceGenerator Sequence](../graphics/cg-sequence.svg){align=center width=550}
+
+Consequently, when JPF executes this `getfield` instruction, the `gov.nasa.jpf.jvm.bytecode.GETFIELD.execute()` method does three things:
+
+  1. create a new `ChoiceGenerator` (`ThreadChoiceGenerator` in this case), that has all runnable threads at this point as possible choices
+  2. registers this `ChoiceGenerator` via calling `SystemState.setNextChoiceGenerator()`
+  3. schedules itself for re-execution (just returns itself as the next instruction to execute within the currently running thread)
+
+At this point, JPF ends this transition (which is basically a loop inside `ThreadInfo.executeStep()`), stores a snapshot of the current State, and then starts the next transition (let's ignore the search and possible backtracks for a moment). The `ChoiceGenerator` created and registered at the end of the previous transition becomes the new current `ChoiceGenerator`. Every state has exactly one current `ChoiceGenerator` object that is associated with it, and every transition has exactly one choice value of this `ChoiceGenerator` that kicks it off. Every transition ends in an instruction that produces the next `ChoiceGenerator`.
+
+The new transition is started by the `SystemState` by setting the previously registered `ChoiceGenerator` as the current one, and calling its `ChoiceGenerator.advance()` method to position it on its next choice. Then the `SystemState` checks if the current `ChoiceGenerator` is a scheduling point (just a `ThreadChoiceGenerator` used to encapsulate threads scheduling), and if so, it gets the next thread to execute from it (i.e. the `SystemState` itself consumes the choice). Then it starts the next transition by calling `ThreadInfo.executeStep()` on it.
+
+The `ThreadInfo.executeStep()` basically loops until an Instruction.execute() returns itself, i.e. has scheduled itself for re-execution with a new `ChoiceGenerator`. When a subsequent `ThreadInfo.executeStep()` re-executes this instruction (e.g. `GETFIELD.execute()`), the instruction notices that it is the first instruction in a new transition, and hence does not have to create a `ChoiceGenerator` but proceeds with it's normal operations.
+
+If there is no next instruction, or the Search determines that the state has been seen before, the VM backtracks. The `SystemState` is restored to the old state, and checks for not-yet-explored choices of its associated ChoiceGenerator by calling `ChoiceGenerator.hasMoreChoices()`. If there are more choices, it positions the `ChoiceGenerator` on the next one by calling `ChoiceGenerator.advance()`. If all choices have been processed, the system backtracks again (until it's first `ChoiceGenerator` is done, at which point we terminate the search).
+
+![Figure 4: ChoiceGenerator Implementation](../graphics/cg-impl.svg){align=center width=850}
+
+The methods that create `ChoiceGenerators` have a particular structure, dividing their bodies into two parts:
+
+  1. *top half* - (potentially) creates and registers a new `ChoiceGenerator`. This marks the end of a transition
+  2. *bottom half* - which does the real work, and might depend on acquiring a new choice value. This is executed at the beginning of the next transition
+
+To determine which branch you are in, you can call `ThreadInfo.isFirstStepInsn()`. This will return `true` if the currently executed instruction is the first one in the transition, which corresponds to the *bottom half* mentioned above.
+
+The only difference between scheduling choices and data acquisition choices is that the first ones are handled internally by the VM (more specifically: used by the `SystemState` to determine the next thread to execute), and the data acquisition is handled in the bottom half of `Instruction.execute()`, native method, or listener callback method (in which case it has to acquire the current `ChoiceGenerator` from the `SystemState`, and then explicitly call `ChoiceGenerator.getNextChoice()` to obtain the choice value). For a real example, look at the `JPF.gov_nasa_jpf_jvm_Verify.getBoolean()` implementation.
+
+As an implementation detail, creation of scheduling points are delegated to a `Scheduler` instance, which encapsulates a scheduling policy by providing a consistent set of `ThreadChoiceGenerators` for the fixed number of instructions that are scheduling relevant (`monitor_enter`, synchronized method calls, `Object.wait()` etc.). Clients of this `Scheduler` therefore have to be aware of that the policy object might not return a new `ChoiceGenerator`, in which case the client directly proceeds with the bottom half execution, and does not break the current transition.
+
+The standard classes and interfaces for the ChoiceGenerator mechanism can be found in package `gov.nasa.jpf.vm`, and include:
+
+  * `ChoiceGenerator`
+  * `BooleanChoiceGenerator`
+  * `IntChoiceGenerator`
+  * `DoublechoiceGenerator`
+  * `ThreadChoiceGenerator`
+  * `SchedulingPoint`
+  * `SchedulerFactory`
+  * `DefaultSchedulerFactory`
+
+Concrete implementations can be found in package `gov.nasa.jpf.vm.choice`, and include classes like:
+
+  * `IntIntervalGenerator`
+  * `IntChoiceFromSet`
+  * `DoubleChoiceFromSet`
+  * `DoubleThresholdGenerator`
+  * `ThreadChoiceFromSet`
+
+As the number of useful generic heuristics increases, we expect this package to be expanded.
+
+
+## Cascaded ChoiceGenerators ##
+There can be more than one `ChoiceGenerator` object associated with a transition. Such ChoiceGenerators are referred to as *cascaded*, since they give us a set of choice combinations for such transitions. 
+
+For example, assume that we want to create a listener that perturbs certain field values, i.e. it replaces the result operand that is pushed by a `getfield` instruction. This is easy to do from a listener, but the VM (more specifically our on-the-fly [partial order reduction](partial_order_reduction)) might already create a `ThreadChoiceGenerator` (scheduling point) for this `getfield` if it refers to a shared object, and the instruction might cause a data race. Without cascaded `ChoiceGenerators` we could only have the perturbation listener **or** the race detection, but not both. This is clearly a limitation we want to overcome, since you might not even know when JPF - or some of the other [listeners](listener) or [bytecode_factories](bytecode_factory) - create `ChoiceGenerators` that would collide with the ones you want to create in your listener.
+
+Using cascaded ChoiceGenerators requires little more than what we have already seen above. It only involves changes to two steps:
+
+ 1. ChoiceGenerator creation - you need to identify `ChoiceGenerators` with a `String` id. We can't use the type of the `ChoiceGenerator` - or it's associated choice type - to identify a particular instance, since different listeners might use different `ChoiceGenerator` instances of same types for different purposes. Resolving through unique types would throw us back to where we would have to know about all the other `ChoiceGenerators` created by all the other JPF components. We can't use the associated instruction either, because the whole point is that we can have more than one `ChoiceGenerator` for each of them. So we have to give our `ChoiceGenerator` instances names when we create them, as in
+~~~~~~~~ {.java}
+...
+IntChoiceFromSet cg = new IntChoiceFromSet("fieldPerturbator", 42, 43);
+~~~~~~~~
+
+The name should be reasonably unique, describing the context in which this choice is used. Don't go with "generic" names like "myChoice". In case of doubt, use the method name that creates the `ChoiceGenerator`. The reason why we need the *id* in the first place is that we later-on want to be able to retrieve a specific instance. Which brings us to:
+
+ 2. ChoiceGenerator retrieval - at some point we want to process the choice (usually within the *bottom half* of the method that created the `ChoiceGenerator`), so we need to tell JPF all we know about the `ChoiceGenerator` instance, namely id and type. The simple `SystemState.getChoiceGenerator()` we used above will only give us the last registered one, which might or might not be the one we registered ourselves. Retrieval is done with a new method `SystemState.getCurrentChoiceGenerator(id,cgType)`, which in the above case would look like:
+~~~~~~~~ {.java}
+...
+IntChoiceFromSet cg = systemState.getCurrentChoiceGenerator("fieldPerturbator", IntChoiceFromSet.class);
+assert cg != null : "required IntChoiceGenerator not found";
+...
+~~~~~~~~
+
+This method returns `null` if there is no `ChoiceGenerator` of the specified id and type associated with the currently executed instruction. If this is the bottom half of a method that created the instance, this is most likely an error condition that should be checked with an assertion. If the retrieval is in another method, existence of such a `ChoiceGenerator` instance could be optional and you therefore have it checked in an `if (cg != null) {..}' expression.
+
+This is all there is to it in case you don't refer to a particular execution state of an instruction. As an example, assume that you want to add some int choices on top of each `Verify.getInt(..)` call. Your listener would look like this:
+
+~~~~~~~~ {.java}
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    SystemState ss = vm.getSystemState();
+
+    if (executedInsn instanceof EXECUTENATIVE) { // break on method call
+      EXECUTENATIVE exec = (EXECUTENATIVE) executedInsn;
+
+      if (exec.getExecutedMethodName().equals("getInt")){ // Verify.getInt(..) - this insn did create a CG on its own
+        if (!ti.isFirstStepInsn()){ // top half - first execution
+          IntIntervalGenerator cg = new IntIntervalGenerator("instrumentVerifyGetInt", 3,4);
+          ss.setNextChoiceGenerator(cg);
+          ...
+
+        } else { // bottom half - re-execution at the beginning of the next transition
+          IntIntervalGenerator cg = ss.getCurrentChoiceGenerator("instrumentVerifyGetInt", IntIntervalGenerator.class);
+          assert cg != null : "no 'instrumentVerifyGetInt' IntIntervalGenerator found";
+          int myChoice = cg.getNextChoice();
+          ... // process choice
+        }
+      }
+    }
+  }
+~~~~~~~~
+
+
+Sometimes what you do with your choice depends on the execution state of the instruction this `ChoiceGenerator` was created for, and you have to be aware of that the instruction might get re-executed (e.g. after processing the top half of another `ChoiceGenerator` creating method) before it has done what you depend on for your local choice processing. Consider our previous example of the field perturbation. Simply speaking, all we want to do in our listener is just swap operand stack values after a certain `getfield`. However, the partial order reduction of the VM might get in our way because it reschedules the instruction *before* it pushes the value if execution of this instruction might constitute a data race, and therefore required creation of a `ThreadChoiceGenerator` instance. What is worse is that the VM might do this conditionally - if there is only one runnable thread, there is no need for a scheduling point since there can't be a data race. Our own perturbator listener has to account for all that. Luckily, we can use `SystemState.getCurrentChoiceGenerator(id,type)` to unify all these cases, and we just have to restore execution state in case we want to re-execute the instruction ourselves. Here is an example:
+
+~~~~~~~~ {.java}
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    SystemState ss = vm.getSystemState();
+
+    if (executedInsn instanceof GETFIELD){
+      GETFIELD getInsn = (GETFIELD) executedInsn;
+      FieldInfo fi = getInsn.getFieldInfo();
+      if (fi.getName().equals("perturbedFieldName")){
+
+        IntChoiceFromSet cg = ss.getCurrentChoiceGenerator("fieldReplace", IntChoiceFromSet.class);
+        StackFrame frame = ti.getModifiableTopFrame();
+        if (cg == null){
+
+          // we might get here after a preceding rescheduling exec, i.e.
+          // partial execution (with successive re-execution), or after
+          // non-rescheduling exec has been completed (only one runnable thread).
+          // In the first case we have to restore the operand stack so that
+          // we can re-execute
+          if (!ti.willReExecuteInstruction()){
+            // restore old operand stack contents
+            frame.pop();
+            frame.push(getInsn.getLastThis());
+          }
+
+          cg = new IntChoiceFromSet("fieldReplace", 42, 43);
+          ss.setNextChoiceGenerator(cg);
+          ti.reExecuteInstruction();
+
+        } else {
+          int v = cg.getNextChoice();
+          int n = frame.pop();
+          frame.push(v);
+        }
+      }
+    }
+  }
+~~~~~~~~
+
+These examples show you that at the beginning of each transition, there is a choice value for all the cascaded `ChoiceGenerators` associated with it. If you would add `choiceGeneratorAdvanced()` notifications to your listener, you would also see that JPF processes all related choice combinations.
+
+If you really want to see the context, there are a number of additional methods in `SystemState` that might help you:
+
+ * `getChoiceGenerator()` - returns only the last registered one
+ * `getChoiceGenerators()` - returns an array of all `ChoiceGenerators` in the current execution path
+ * `getLastChoiceGeneratorOfType(cgType)` - returns the last registered `ChoiceGenerator` in the path that is of the specified type
+ * `getCurrentChoiceGenerators()` - returns array of all cascaded `ChoiceGenerators` associated with the current transition
+ * `getCurrentChoiceGenerator(id)` - returns last registered `ChoiceGenerator` of cascade with specified *id*
+ * `getCurrentChoiceGenerator(id,cgType)` - our workhorse: last registered `ChoiceGenerator` of cascade with specified *id* and *cgType* (which can be a supertype of the actual one)
+ * etc.
+
+How does the system detect if a `ChoiceGenerator` is cascaded or not? Very simple - within `SystemState.setNextChoiceGenerator(cg)`, we just check if `SystemState` already had a registered next `ChoiceGenerator`, and if so, we set a cascaded attribute for this one. Other than that, we just maintain normal linear `ChoiceGenerator` linkage, which is accessible through `ChoiceGenerator.getPreviousChoiceGenerator()`. If you want to iterate through a cascade yourself, use the `ChoiceGenerator.getCascadedParent()` method, which returns `null` if there is none. Just be aware of that the last registered `ChoiceGenerator` (i.e. what `SystemState.getChoiceGenerator()` returns) does *not* have the cascaded attribute set (i.e. `ChoiceGenerator.isCascaded()` returns `false`).    
\ No newline at end of file
diff --git a/doc/devel/coding_conventions.md b/doc/devel/coding_conventions.md
new file mode 100644 (file)
index 0000000..fe629e9
--- /dev/null
@@ -0,0 +1,41 @@
+# Coding Conventions #
+JPF is an open system. In order to keep the source format reasonably consistent, we strive to keep the following minimal set of conventions
+
+  * Two space indentation (no tabs)
+  * Opening brackets in same line (class declaration, method declaration, control statements)
+  * No spaces after opening '(', or before closing ')'
+  * Method declaration parameters indent on column
+  * All files start with copyright and license information
+  * All public class and method declarations have preceding Javadoc comments
+  * We use *camelCase* instead of *underscore_names* for identifiers
+  * Type names are upper case 
+
+The following code snippet illustrates these rules.
+
+~~~~~~~~ {.java}
+/* <copyright notice goes here>
+ * <license referral goes here>
+ */
+
+/**
+ * this is my class declaration example
+ */
+    
+public class MyClass {
+   
+  /**
+   * this is my public method example
+   */
+  public void foo (int arg1, int arg2,
+                   int arg3) {
+    if (bar) {
+      ..
+    } else {
+      ..
+    }
+  }
+   ..
+}
+~~~~~~~~
+
+We consider modularity to be of greater importance than source format. With its new configuration scheme, there is no need to introduce dependencies of core classes towards optional extensions anymore. If you add something that is optional, and does not seamlessly fit into an existing directory, keep it separate by adding new directories. The core JPF classes should not contain any additional dependencies to external code.
diff --git a/doc/devel/create_project.md b/doc/devel/create_project.md
new file mode 100644 (file)
index 0000000..1eeb9b0
--- /dev/null
@@ -0,0 +1,91 @@
+# Creating a New JPF Module #
+
+So what do you have to do to create a new JPF module? For a quick shortcut to setting up *most* things,  use the `create_project` script of the [jpf-template module](wiki:projects/jpf-template).  
+
+However, jpf-template cannot do everything for you, so see below for more information on how to finish setting up your new project.
+
+Several steps are involved:
+
+### 1. get familiar with the JPF configuration ###
+You need to understand how your project will be looked up and initialized during JPF startup, and the place to learn that is the [JPF configuration](../user/config) page. Once you know what *[site properties](../install/site-properties)* and *project properties* are, you can proceed.
+
+### 2. get familiar with the standard JPF project layout ###
+Although this is mostly convention, and you can deviate if you really need to, please try hard not to.
+
+You can get the details from the [JPF Runtime Modules](modules) page, but the essence is that each project has two (possible) major build artifacts:
+
+ * `jpf-<module>.jar` - executed by the host (platform) VM (contains main classes and peers)
+ * `jpf-<module>-classes.jar` - executed by JPF (contains modeled classes)
+
+Consequently, your sources are kept in `src/main`, `src/peers`, `src/classes`, `src/annotations`, `src/tests` and `src/examples`. You might only have some of these, but please provide regression tests so that people can check if your project works as expected. 
+
+All 3rd party code that is required at runtime goes into a `lib` directory.
+
+We keep potential annotations separate (and provide additional `jpf-<module>-annotations.jar`) so that external projects (systems under test) can use them without relying on all JPF classes to be in their `classpath`. The idea is that this jar does not contain any code which could alter the system under test behavior if you execute it outside of JPF. 
+
+<The `tools` directory contains 3rd party libraries and tools that are used at build-time. For convenience reasons, we usually copy the small `RunJPF.jar` from the jpf-core in here, so that you can easily run JPF from the command line without the need for platform specific scripts or links, but that is completely optional.>
+
+### 3. create a jpf.properties file ###
+Within the root directory of each JPF module a project properties file is needed which is named `jpf.properties`. It contains the path settings the host VM and JPF need to know about at runtime. It looks like this:
+
+~~~~~~~~ {.bash}
+# standard header
+<module-name> = ${config_path}
+
+# classpath elements for the host VM (java)
+<module-name>.native_classpath = build/<module-name>.jar;lib/...
+
+# classpath elements for JPF
+<module-name>.classpath = build/<module-name>-classes.jar;...
+
+# sources JPF should know about when creating traces etc.
+<module-name>.sourcepath = src/classes;...
+~~~~~~~~
+
+You can add other JPF properties, but be aware of that this is always processed during JPF startup if you add your module to the `extensions` list in your [site.properties](../install/site-properties), and might conflict with other JPF modules. For this reason you should only add your module to `extensions` if you know it will always be used.
+
+
+### 4. create your build.xml ###
+Our build process is [Ant](http://ant.apache.org/) based, hence we need a `build.xml` file. The standard targets are
+
+ * `clean`
+ * `compile`
+ * `build` (the default, creates the jars and hence depends on compile)
+ * `test` (run JUnit regression tests, depends on build)
+ * `dist` (creates a binary-only distribution) 
+
+If you stick to the general layout, you can use a template like the one attached to this page (of course you need to replace `<your-project-name>`).
+
+Please note how `site.properties` and `jpf.properties` can be used from within the `build.xml` (Ant understands a subset of the JPF property syntax), which means you don't have to explicitly add the jars of other JPF components you depend on (at least jpf-core). You can reference them symbolically like this:
+
+~~~~~~~~ {.xml}
+  <property file="${user.home}/.jpf/site.properties"/>
+  <property file="${jpf-core}/jpf.properties"/>
+  ..
+  <!-- generic classpath settings -->
+  <path id="lib.path">
+
+    <!-- our own classes and libs come first -->
+    <pathelement location="build/main"/>
+    ...
+    <fileset dir=".">
+           <include name="lib/*.jar"/>
+    </fileset>
+
+    <!-- add in what we need from the core -->
+    <pathelement path="${jpf-core.native_classpath}"/>
+  </path>
+  ...
+~~~~~~~~
+
+### 5. add your module to your site.properties ###
+This is optional, you only need to do this if you want to be able to run your JPF module outside its own directory. If so, add an entry to your [site properties file](../install/site-properties) that looks like this:
+
+~~~~~~~~ {.bash}
+...
+<module-name> = <path to your JPF extension module>
+...
+~~~~~~~~
+
+### 6. publish your repository ###
+You can publish this wherever you want ([sourceforge](http://sourceforge.net), [bitbucket](http://bitbucket.org), [google code](http://code.google.com), or [github](http://github.com) are suitable free site supporting Mercurial), or ask us to host it on the JPF server. If you decide to use a 3rd party hosting service, please let us/the JPF community know about it (e.g. by posting to the mailing list at [java-pathfinder@googlegroups.com](https://groups.google.com/forum/#!forum/java-pathfinder).
\ No newline at end of file
diff --git a/doc/devel/design.md b/doc/devel/design.md
new file mode 100644 (file)
index 0000000..60e0fd3
--- /dev/null
@@ -0,0 +1,52 @@
+# JPF Top-level Design #
+
+JPF was designed around two major abstractions: (1) the *VM*, and (2) the *Search* component.
+
+## Virtual Machine (VM) ##
+
+The VM is the state generator. By executing bytecode instructions, the VM generates state representations that can be
+
+  * checked for equality (if a state has been visited before)
+  * queried (thread states, data values etc.)
+  * stored
+  * restored
+
+The main VM parameterizations are classes that implement the state management (matching, storing, backtracking). Most of the execution scheme is delegated to `SystemState`, which in turn uses `Scheduler`  to generate scheduling sequences of interest.
+
+There are three key methods of the VM employed by the Search component:
+
+  * `forward` - generate the next state, report if the generated state has a successor. If yes, store on a backtrack stack for efficient restoration.
+  * `backtrack` - restore the last state on the backtrack stack
+  * `restoreState` - restore an arbitrary state (not necessarily on the backtrack stack)
+
+![Figure: JPF top-level design](../graphics/jpf-abstractions.svg){align=center width=720}
+
+## Search Strategy ##
+
+At any state, the Search component is responsible for selecting the next state from which the VM should proceed, either by directing the VM to generate the next state (`forward`), or by telling it to backtrack to a previously generated one (`backtrack`). The Search component works as a driver for the VM.
+
+The Search component can be configured to check for certain properties by evaluating property objects (e.g. `NotDeadlockedProperty`, `NoAssertionsViolatedProperty`).
+
+The object encapsulating this component includes a search method which implements a strategy used to traverse the state space. The state space exploration continues until it is completely explored, or a property violation is found.
+ The Search component can be configured to use different strategies, such as depth-first search (`DFSearch`), and priority-queue based search that can be parameterized to do various search types based on selecting the most interesting state out of the set of all successors of a given state (`HeuristicSearch`).
+
+## Package Structure ##
+
+The implementation of the JPF core is partitioned into the following packages:
+
+### `gov.nasa.jpf` ###
+The main responsibility of this package is configuration and instantiation of the core JPF objects, namely the Search and VM. The configuration itself is encapsulated by the `Config` class, which contains various methods to create objects or read values from a hierarchy of property files and command line options (see Configuring JPF Runtime Options). Beyond the configuration, the JPF object has little own functionality. It is mainly a convenience construct to start JPF from inside a Java application without having to bother with its complex configuration.
+
+### `gov.nasa.jpf.vm` ###
+This package constitutes the main body of the core code, including the various constructs that implement the Java state generator. Conceptually, the major class is VM, but again this class delegates most of the work to a set of second level classes that together implement the major functionality of JPF. These classes can be roughly divided into three categories:
+
+(1) class management - classes are encapsulated by `ClassInfo` which mostly includes invariant information about fields and methods captured by `FieldInfo` and `MethodInfo`, respectively.
+
+(2) object model - all object data in JPF is stored as integer arrays encapsulated by `Fields` objects. The execution specific lock state of objects is captured by `Monitor` instances. `Fields` and `Monitor` instances together form the objects, which are stored as `ElementInfo`. The heap contains a dynamic array of `ElementInfo` objects where the array indices being used as object reference values
+
+(3) bytecode execution - the execution of bytecode instructions is performed through a collaboration of `SystemState` and `ThreadInfo`, which is also delegated to policy objects implementing the partial order reduction (POR). It starts with the `VM` object calling `SystemState.nextSuccessor()`, which descends into `ThreadInfo.executeStep()` (together, these two methods encapsulate the on-the-fly POR), which in turn calls `ThreadInfo.executeInstruction()` to perform the bytecode execution.
+The actual execution is again delegated to bytecode specific Instruction instances that per default reside in a sub-package `gov.nasa.jpf.vm.bytecode` (the set of bytecode classes to use can be configured via a `InstructionFactory` class which allows the user to define a different execution semantics)
+
+### `gov.nasa.jpf.search` ### 
+This package is relatively small and mainly contains the `Search` class, which is an abstract base for search policies. The major method that encapsulates the policy is `Search.search()`, which is the VM driver (that calls the methods`forward`, `backtrack` and `restore`). This package also contains the plain-vanilla depth-first search policy `DFSearch`.
+More policies can be found in the sub-package `gov.nasa.jpf.search.heuristic`, which uses a `HeuristicSearch` class in conjunction with configurable heuristic objects to prioritize a queue of potential successor states.
diff --git a/doc/devel/eclipse_plugin_update.md b/doc/devel/eclipse_plugin_update.md
new file mode 100644 (file)
index 0000000..35f663f
--- /dev/null
@@ -0,0 +1,36 @@
+## Hosting an Eclipse plugin update site ##
+
+The first step is to create the local version of the update site.  For example, chapter 18, section 3 of "Eclipse Plug-ins, 3rd edition" will explain how to do this.
+  
+> **Tip:** do not attempt to put the update site in the code repository.  
+
+The plugin and feature files are treated like binaries and bad things will happen.  Here is a sample update site for the mango plugin.
+
+![Local Site](../graphics/localsite.jpg){align=center width=430}
+
+Now you will re-create this directory structure within the wiki.  For the purpose of this discussion, let's pin down the jpf site:
+
+~~~~~~~~ {.bash}
+JPFHOME=http://babelfish.arc.nasa.gov/trac/jpf
+~~~~~~~~
+
+Now chose a home directory, say `HOME`.  For the mango plugin, 
+
+~~~~~~~~ {.bash}
+HOME=wiki/projects/jpf-mango
+~~~~~~~~
+
+Whatever choice of `HOME` you make, the update site you advertise to the world will be `JPFHOME/raw-attachment/HOME/update/`.
+
+
+The `raw-attachment` segment is the *trick* that makes everything work out.  The next step is to create the directory structure for the mirrored update site.  Within `JPFHOME/HOME`, create a link to `JPFHOME/HOME/update`.  Now go to the update page and add the attachments artifacts.jar, content.jar, and site.xml from your local update site.  Create links within `JPFHOME/HOME/update` to `JPFHOME/HOME/update/features` and `JPFHOME/HOME/update/plugins`.  
+
+Attach your feature jar to the features page, and your plugin jar to the plugins page.  That's all there is to it.
+
+> **Tip:** when updating your update site, be sure to sync your plugin and feature with new, higher, revision numbers.  Now rebuild the local site.  Delete all the corresponding attachments in the wiki, and repopulate with the updated versions.
+
+> **Bonus tip:** Once everything is working, you can delete the link to the update site.  This will prevent your visitors from accidentally going to an uninteresting page.  You can always access this page directly from the browser by entering `JPFHOME/HOME/update`.
+
+
+
+
diff --git a/doc/devel/embedded.md b/doc/devel/embedded.md
new file mode 100644 (file)
index 0000000..0ddd1c3
--- /dev/null
@@ -0,0 +1,27 @@
+# Embedded JPF #
+JPF can also be used embedded, i.e. called from another Java application. A basic code sequence to start JPF looks like this:
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.Config;
+
+void runJPF (String[] args) {
+   ..
+   MyListener listener = new MyListener(..);
+
+   // [optionally] if you pass through command line args, 
+   // 'null' any consumed args not to be JPF-processed
+   listener.filterArgs( args);
+   ..
+
+   Config config = JPF.createConfig( args);
+   // set special config key/value pairs here..
+
+   JPF jpf = new JPF( config);
+   jpf.addListener( listener);
+   jpf.run();
+   ..
+}
+~~~~~~~~
+
+Of course, you can also call `gov.nasa.jpf.JPF.main(args)` from within your application, if you don't need to control JPF's configuration or process it's output. 
diff --git a/doc/devel/index.md b/doc/devel/index.md
new file mode 100644 (file)
index 0000000..8394c9c
--- /dev/null
@@ -0,0 +1,23 @@
+# JPF Developer Guide #
+
+From the previous two sections, you have learned that JPF has one recurring, major theme: it is not a monolithic system, but rather a configured collection of components that implement different functions like state space search strategies, report generation and much more. Being adaptive is JPF's answer to the scalability problem of software model checking.
+
+This not only makes JPF a suitable system for research, but chances are that if are you serious enough about JPF application, you sooner or later end up extending it. This section includes the following topics which describe the different mechanisms that can be used to extend JPF.
+
+ * [Top-level design](design)
+ * Key mechanisms, such as 
+     - [ChoiceGenerators](choicegenerator)
+     - [Partial order reduction](partial_order_reduction)
+     - [Slot and field attributes](attributes)
+ * Extension mechanisms, such as
+     - [Listeners](listener)
+     - [Search Strategies](design)
+     - [Model Java Interface (MJI)](mji)
+     - [Bytecode Factories](bytecode_factory)
+ * Common utility infrastructures, such as
+     - [Logging system](loggin)
+     - [Reporting system](report)
+ * [Running JPF from within your application](embedded)
+ * [Writing JPF tests](jpf_tests)
+ * [Coding conventions](coding_conventions)
+ * [Hosting an Eclipse plugin update site](eclipse_plugin_update) 
\ No newline at end of file
diff --git a/doc/devel/jpf_tests.md b/doc/devel/jpf_tests.md
new file mode 100644 (file)
index 0000000..a445c5a
--- /dev/null
@@ -0,0 +1,148 @@
+# Writing JPF Tests #
+
+As a complex runtime system for (almost) arbitrary Java programs, it goes without saying that JPF needs a lot of regression tests. You can find these under the `src/tests` directories in (hopefully) all JPF projects. All tests follow the same scheme, which is motivated by the need to run tests in a number of different ways:
+
+  1. as part of the Ant-based build system, i.e. from build.xml
+  1. as explicitly invoked JUnit tests
+  1. by directly running JPF on the test application (i.e. without JUnit, either as a JPF `shell` or via RunTest.jar)
+  1. by running the test application on a normal JVM
+
+The rationale for this is to support various levels of inspection and debugging. 
+
+Each test conceptually consists of a test driver (e.g. executed under JUnit) which starts JPF from within its `@Test` annotated methods, and a class that is executed by JPF in order to check the verification goals. For convenience reasons, jpf-core provides infrastructure that enables you to implement both parts in the same class. This can be confusing at first - **the test class is used to start JPF on itself**.
+
+![Figure: Unit Testing in JPF](../graphics/new-testing.svg){align=center width=870}
+
+The `main()` method of `TestJPF` derived classes always look the same and can be safely copied between tests:
+
+~~~~~~~~ {.java}
+public static void main(String[] testMethods){
+  runTestsOfThisClass(testMethods);
+}
+~~~~~~~~
+
+This method serves two purposes. First, it is used to start the test outside JUnit, either on all `@Test` annotated instance methods, or just on the ones which names are provided as arguments. Second, it serves as the entry point for JPF when it executes the class. In this case, `TestJPF` takes care of invoking JPF on the test class and providing the name of the test method this was executed from.
+
+Other than that, test classes just consist of (almost) normal `@Test` annotated JUnit test methods, which all share the same structure
+
+~~~~~~~~ {.java}
+import org.junit.Test;
+
+@Test public void testX () {
+  if (verifyNoPropertyViolation(JPF_ARGS){
+    .. code to verify by JPF
+  }
+}
+~~~~~~~~
+
+The trick is the call to `verifyNoPropertyViolation()`, or any of the other `verifyXX()` methods of `TestJPF`. If executed by the host VM, i.e. from JUnit, it starts JPF on the same class and the containing method, and returns `false`. This means the corresponding `if` block is **not** executed by the host VM.
+
+When JPF is invoked, the argument to the main() method is set to the method name from which JPF got invoked, which causes `runTestsOfThisMethod()` to execute exactly this method again, but this time under JPF. Instead of re-executing the same `TestJPF.verifyX()` method again (and becoming infinitely recursive), we use a native peer `JPF_gov_nasa_jpf_util_test_TestJPF` which intercepts the `verifyX()` call and simply returns true, i.e. this time *only* the `if` block gets executed.
+
+The rest of the host VM executed `TestJPF.verifyX()` checks the results of the JPF run, and accordingly throws an `AssertionError` in case it does not correspond to the expected result. The most common goals are
+
+ * `verifyNoPropertyViolation` - JPF is not supposed to find an error
+ * `verifyPropertyViolation` - JPF is supposed to find the specified property violation
+ * `verifyUnhandledException` - JPF is supposed to detect an unhandled exception of the specified type
+ * `verifyAssertionError` - same for AssertionErrors
+ * `verifyDeadlock` - JPF is supposed to find a deadlock
+
+Each of these methods actually delegate running JPF to a corresponding method whose name does not start with 'verify..'. These workhorse methods expect explicit specification of the JPF arguments (including SUT main class name and method names), but they return JPF objects, and therefore can be used for more sophisticated JPF inspection (e.g. to find out about the number of states).
+
+`TestJPF` also provides some convenience methods that can be used within test methods to find out which environment the code is executed from:
+
+ * `isJPFRun()` - returns true if the code is executed under JPF
+ * `isJUnitRun()` - returns true if the code is executed under JUnit by the host VM
+ * `isRunTestRun()` - returns true if the code is executed by RunTest.jar
+
+Here is an example of a typical test method that uses some of these features:
+
+~~~~~~~~ {.java}
+  @Test public void testIntFieldPerturbation() {
+
+    if (!isJPFRun()){ // run this outside of JPF
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.Perturbator",
+                                  "+perturb.fields=data",
+                                  "+perturb.data.class=.perturb.IntOverUnder",...
+                                  "+perturb.data.delta=1")){
+      // run this under JPF
+      System.out.println("instance field perturbation test");
+
+      int d = data; // this should be perturbated
+      System.out.println("d = " + d);
+
+      Verify.incrementCounter(0);
+
+    } else { // run this outside of JPF
+      assert Verify.getCounter(0) == 3;
+    }
+  }
+~~~~~~~~
+
+## Running JPF tests from command line ##
+To run JPF tests from the command line, use the RunTest.jar either from `jpf-core/build`, or the one that is distributed with your project containing the tests (`tools/RunTest.jar` for JPF projects). This is an executable jar that expects the test class and (optional) method test names as arguments. If no method names are provided, all `@Test` annotated methods are executed. Most projects have a convenience script `bin/test` to execute RunTest.jar.
+
+~~~~~~~~ {.bash}
+> bin/test gov.nasa.jpf.test.mc.data.PerturbatorTest testIntFieldPerturbation
+
+......................................... testing testIntFieldPerturbation
+  running jpf with args: +listener=.listener.Perturbator +perturb.fields=data +perturb.data.class=.perturb.IntOverUnder +perturb.data.field=gov.nasa.jpf.test.mc.data.PerturbatorTest.data +perturb.data.delta=1 gov.nasa.jpf.test.mc.data.PerturbatorTest testIntFieldPerturbation
+JavaPathfinder v5.x - (C) RIACS/NASA Ames Research Center
+
+
+====================================================== system under test
+application: gov/nasa/jpf/test/mc/data/PerturbatorTest.java
+arguments:   testIntFieldPerturbation 
+
+====================================================== search started: 9/10/10 7:03 PM
+instance field perturbation test
+d = 43
+d = 42
+...
+====================================================== search finished: 9/10/10 7:03 PM
+......................................... testIntFieldPerturbation: Ok
+
+......................................... execution of testsuite: gov.nasa.jpf.test.mc.data.PerturbatorTest SUCCEEDED
+.... [1] testIntFieldPerturbation: Ok
+......................................... tests: 1, failures: 0, errors: 0
+~~~~~~~~
+
+## Running JPF tests under JUnit ##
+
+This is the preferred way to execute JPF regression tests, which is usually done from an Ant build.xml script containing a standard target such as
+
+~~~~~~~~ {.xml}
+  ...
+  <target name="test" depends="build" description="run core regression tests" if="have_tests">
+     ...
+     <junit printsummary="on" showoutput="off" haltonfailure="yes"
+            fork="yes" forkmode="perTest" maxmemory="1024m" outputtoformatters="true">
+       <classpath>
+         <path refid="lib.path"/>
+         <pathelement location="build/tests"/>
+         ...
+       </classpath>
+       <batchtest todir="build/tests">
+          <fileset dir="build/tests">
+            <exclude name="**/JPF_*.class"/>
+            <include name="**/*Test.class"/>
+          </fileset>
+      </batchtest>
+    </junit>
+  </target>
+~~~~~~~~
+
+Most JPF projects have build.xml files you can use as examples.
+
+Please note this means that you should not have any inner classes, interfaces, annotation types etc. with a name ending with `"Test"` since JUnit would interpret these as test cases and most likely complain about missing constructors and `main()` methods.
+
+## Debugging tests ##
+
+Typically, JPF tests are only executed from within an IDE if they fail and need to be debugged. 
+
+Under NetBeans, this can be done by selecting the test class, and then executing the *Debug File* command from the context menu. This will pop up a dialog that lets you enter a specific test method to debug. This method requires a properly set up ide-file-target.xml, which comes with most JPF projects.
+
+Under Eclipse, you can select the test class and then execute **Debug As..** -> **Java Application**.
diff --git a/doc/devel/listener.md b/doc/devel/listener.md
new file mode 100644 (file)
index 0000000..98af48c
--- /dev/null
@@ -0,0 +1,332 @@
+# Listeners #
+Listeners are perhaps the most important extension mechanism of JPF. They provide a way to observe, interact with and extend JPF execution with your own classes. Since listeners are dynamically configured at runtime, they do not require any modification to the JPF core. Listeners are executed at the same level like JPF, so there is hardly any limit of what you can do with them.
+
+![Figure 1: JPF Listeners](../graphics/listener-overview.svg){align=center width=800}
+
+The general principle is simple: JPF provides an observer pattern implementation that notifies registered observer instances about certain events at the search level and the VM level. These notifications cover a broad spectrum of JPF operations, from low level events like `instructionExecuted` to high level events like `searchFinished`. Each notification is parameterized with the corresponding source (either the `Search` or the `VM` instance), which can be then used by the notified listener to obtain more information about the event and the JPF's internal state.
+
+Configuration is usually done with the `listener` property, either from the command line, or a .jpf property file. Listeners can also be associated with annotations, to be automatically loaded whenever JPF encounters such an annotation. Applications can use the `@JPFConfig` annotation to explicitly specify JPF listeners. Lastly, if JPF is used in an embedded mode, listeners can be registered with an API.
+
+
+## Listener Types ##
+
+
+There are two basic listener interfaces, depending on corresponding event sources: `SearchListeners` and `VMListeners`. Since these interfaces are quite large, and listeners often need to implement both, we also provide "adapter" classes, i.e. implementors that contain all required method definitions with empty method bodies. Concrete listeners that extend these adapters therefore only have to override the notification methods they are interested in.
+
+The adapter classes are used for the majority of listener implementations, especially since they also support two other interfaces/extension mechanisms that are often used in conjunction with `Search` and `VMListeners`: 
+
+ 1. `Property` - to define program properties
+ 2. `PublisherExtension` - to produce output within [the JPF reporting system](report)
+
+`ListenerAdapter` is the bare adapter implementation for `SearchListener`, `VMListener` and `PublisherExtension`. This is what is mostly used to collect information during JPF execution (e.g. `CoverageAnalyzer` and `DeadlockAnalyzer`).
+
+`PropertyListenerAdapter` is used in case the listener implements a program property, i.e. it can terminate the search process. A prominent example of this category is `PreciseRaceDetector`.
+
+![Figure 2: Listener Types](../graphics/listeners.svg){align=center width=800}
+
+Choosing the right type for your listener is important, since JPF automatically registers listeners (and properties) based on this type. You can bypass and directly implement single listener interfaces, but then you also have to do the proper registrations.
+
+Usually, the notification alone is not enough, and the listener needs to acquire more information from JPF. For this purpose, we provide either the `Search` or the `VM` instance as notification arguments, and the listener has to use these as "Facades" to query or interact JPF. It therefore matters to implement the listener within the right package.
+
+
+## SearchListener ##
+
+
+`SearchListener` instances are used to monitor the state space search process, e.g. to create graphical representations of the state-graph. They provide notification methods for all major Search actions.
+
+~~~~~~~~ {.java}
+package gov.nasa.jpf.search;
+public interface SearchListener extends JPFListener {
+  void searchStarted (Search search);  
+  void stateAdvanced (Search search);       // got next state
+  void stateProcessed (Search search);      // state is fully explored
+  void stateBacktracked (Search search);    // state was backtracked one step (same path)
+  void stateStored (Search search);         // somebody stored the state
+  void stateRestored (Search search);       // previously generated state was restored (any path)
+  void propertyViolated (Search search);    // JPF encountered a property violation
+  void searchConstraintHit (Search search); // e.g. max search depth
+  void searchFinished (Search search);
+}}
+~~~~~~~~
+
+
+For the standard depth first search (`gov.nasa.jpf.search.DFSearch`), listener implementations can assume the following notification model:
+
+
+![Figure 3: Depth first listener notifications](../graphics/DFSListener.svg){align=center width=500}
+
+The most frequently used notifications are:
+
+`stateAdvanced` - to store additional, backtrackable state information in an associative array
+
+`stateBacktracked` - to restore additional state information
+
+`searchFinished` - to process listener results
+
+
+## VMListener ##
+
+
+This is a fat interface, reflecting various VM operations
+
+~~~~~~~~ {.java}
+package gov.nasa.jpf.jvm;
+public interface VMListener extends JPFListener {
+  //--- basic bytecode execution
+  void executeInstruction (JVM vm);  // JVM is about to execute the next instruction
+  void instructionExecuted (JVM vm); // JVM has executed an instruction
+
+  //--- thread operations (scheduling)
+  void threadStarted (JVM vm);       // new Thread entered run()
+  void threadBlocked (JVM vm);       // thread waits to acquire a lock
+  void threadWaiting (JVM vm);       // thread is waiting for signal
+  void threadNotified (JVM vm);      // thread got notified
+  void threadInterrupted (JVM vm);   // thread got interrupted
+  void threadTerminated (JVM vm);    // Thread exited run()
+  void threadScheduled (JVM vm);     // new thread was scheduled by JVM
+
+  //--- class management
+  void classLoaded (JVM vm);         // new class was loaded
+
+  //--- object operations
+  void objectCreated (JVM vm);       // new object was created
+  void objectReleased (JVM vm);      // object was garbage collected
+  void objectLocked (JVM vm);        // object lock acquired
+  void objectUnlocked (JVM vm);      // object lock released
+  void objectWait (JVM vm);          // somebody waits for object lock
+  void objectNotify (JVM vm);        // notify single waiter for object lock
+  void objectNotifyAll (JVM vm);     // notify all waiters for object lock
+
+  void gcBegin (JVM vm);             // start garbage collection
+  void gcEnd (JVM vm);               // garbage collection finished
+
+  void exceptionThrown (JVM vm);     // exception was thrown
+
+  //--- ChoiceGenerator operations  
+  void choiceGeneratorSet (JVM vm);  // new ChoiceGenerator registered
+  void choiceGeneratorAdvanced (JVM vm); // new choice from current ChoiceGenerator
+  void choiceGeneratorProcessed (JVM vm); // current ChoiceGenerator processed all choices  
+}
+~~~~~~~~
+
+
+The most commonly used methods are the instruction notifications:
+
+`executeInstruction` - is called before a bytecode instruction gets executed by the VM. The listener can even use this to skip and/or replace this instruction, which is useful for non-invasive instrumentation.
+
+`instructionExecuted` - is the post-execution notification, which is suitable to keep track of execution results (method invocations, assigned field values, branch results etc.)
+
+
+## Example ##
+
+The following example is a slightly abbreviated form our race detector. The basic idea is simple: every time we encounter a new scheduling point (i.e. new `ThreadChoiceGenerator` object) that is due to a field access on a shared object, we check if any of the other runnable threads is currently accessing the same field on the same object. If at least one operation is a `putfield`, we have a potential race.
+
+The example shows three aspects that are quite typical: 
+
+  1. listeners often use only a small number of notification methods
+
+  2. they often do not require a huge amount of code (most expensive operations are performed by the `VM` and the `Search` objects)
+
+  3. sometimes you have to dig deep into JPF internal constructs, to extract things like `ThreadInfo`, `FieldInfo` and `ChoiceGenerator` instances
+
+~~~~~~~~ {.java}
+public class PreciseRaceDetector extends PropertyListenerAdapter {
+  FieldInfo raceField;
+  ...
+  //--- the Property part
+  public boolean check(Search search, JVM vm) {
+    return (raceField == null);
+  }
+
+  //--- the VMListener part
+  public void choiceGeneratorSet(JVM vm) {
+    ChoiceGenerator<?> cg = vm.getLastChoiceGenerator();
+
+    if (cg instanceof ThreadChoiceFromSet) {
+      ThreadInfo[] threads = ((ThreadChoiceFromSet)cg).getAllThreadChoices();
+      ElementInfo[eiCandidates = new ElementInfo[threads.length](]);
+      FieldInfo[fiCandidates = new FieldInfo[threads.length](]);
+
+      for (int i=0; i<threads.length; i++) {
+        ThreadInfo ti = threads[i];
+        Instruction insn = ti.getPC();
+        
+        if (insn instanceof FieldInstruction) { // Ok, its a get/putfield
+          FieldInstruction finsn = (FieldInstruction)insn;
+          FieldInfo fi = finsn.getFieldInfo();
+
+          if (StringSetMatcher.isMatch(fi.getFullName(), includes, excludes)) {
+            ElementInfo ei = finsn.peekElementInfo(ti);
+
+            // check if we have seen it before from another thread
+            int idx=-1;
+            for (int j=0; j<i; j++) {
+              if ((ei ## eiCandidates[&& (fi ## fiCandidates[j](j])))) {
+                idx = j;
+                break;
+              }
+            }
+
+            if (idx >= 0){ // yes, we have multiple accesses on the same object/field
+              Instruction otherInsn = threads[idx].getPC();
+              if (isPutInsn(otherInsn) || isPutInsn(insn)) {
+                raceField = ((FieldInstruction)insn).getFieldInfo();
+                ..
+                return;
+              }
+            } else {
+              eiCandidates[i] = ei;
+              fiCandidates[i] = fi;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  public void executeInstruction (JVM jvm) {
+    if (raceField != null) {  // we're done, report as quickly as possible
+      ThreadInfo ti = jvm.getLastThreadInfo();
+      ti.breakTransition();
+    }
+  }
+}
+~~~~~~~~
+
+## Instantiation ##
+
+Explicit instantiation of a listener (e.g. from a JPF shell) can be done in any way. If the listener is specified as a JPF property, it's class either needs to have a default constructor, or a constructor that takes a single `gov.nasa.jpf.Config` argument. The `Config` object that is passed into this constructor by JPF is the same that was used for the initialization of JPF itself. This is the preferred method if the listener has to be parameterized. In case of the `PreciseRaceDetector` example, this can be used to filter relevant fields with regular expressions:
+
+~~~~~~~~ {.java}
+public class PreciseRaceDetector extends PropertyListenerAdapter {
+  ...
+  StringSetMatcher includes = null;
+  StringSetMatcher excludes = null;
+  
+  public PreciseRaceDetector (Config conf) {
+    includes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.include"));
+    excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.exclude"));
+  }
+  ...
+  public void choiceGeneratorSet(JVM vm) {
+    ...
+        FieldInfo fi =..
+        if (StringSetMatcher.isMatch(fi.getFullName(), includes, excludes))
+     ...
+  }
+~~~~~~~~
+
+
+## Configuration ##
+
+Listener configuration can be done in a number of ways: via JPF properties from the command line or a .jpf file, via JPF APIs from a JPF shell (a program invoking JPF), or from the system under test by using Java annotations (i.e. without code modification).
+
+Since listeners are executed by the host VM, they have to be in the `CLASSPATH` (`jpf-core.native_classpath` property).
+
+
+### command line ###
+
+
+the *listener* property can be used to specify a colon separated list of listener class names:
+
+~~~~~~~~ {.bash}
+bin/jpf ... +listener=x.y.MyFirstListener,x.z.MySecondListener ...
+~~~~~~~~
+
+
+### .jpf property file ###
+
+
+If you have several listeners and/or a number of other JPF options, it is more convenient to add the `listener` property to a .jpf file:
+
+~~~~~~~~ {.bash}
+# Racer-listener.jpf - JPF mode property file to detect data races in jpftest.Racer
+target = jpftest.Racer
+listener=gov.nasa.jpf.tools.PreciseRaceDetector
+~~~~~~~~
+
+
+### Autoload Annotations ###
+
+
+Consider your system under test is marked up with a Java annotation that represent properties. For example, you can use the `@NonNull` annotation to express that a method is not allowed to return a `null` value:
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.NonNull; 
+  ...
+  @NonNull X computeX (..) {
+    //.. some complex computation
+  }
+  ...
+~~~~~~~~
+
+You can use .jpf property files (or the command line, if you love to type) to tell JPF that it should automatically load and register a corresponding listener (e.g. `NonNullChecker`) if it encounters such a `@NonNull` annotation during class loading:
+
+~~~~~~~~ {.bash}
+..
+listener.autoload = gov.nasa.jpf.NonNull,...
+listener.gov.nasa.jpf.NonNull = gov.nasa.jpf.tools.NonNullChecker
+...
+~~~~~~~~
+
+
+### @JPFConfig annotation (SuT) ###
+
+
+You can also explicitly direct JPF to load the listener from within your application by using the `@JPFConfig` annotation:
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.JPFConfig;
+..
+// set JPF properties via properties at class load time
+@JPFConfig ({"listener+=.tools.SharedChecker", ..})
+public class TestNonShared implements Runnable {
+  ...
+}
+~~~~~~~~
+
+However, this is not recommended outside JPF tests - the application would run, but not compile without JPF.
+
+
+### Verify API (SuT) ###
+
+
+A less often used method is to set listeners is to use the `gov.nasa.jpf.vm.Verify` API from within your application. With this, you can control the exact load time of the listener (but be aware of backtracking). With this, the above example would become
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.vm.Verify;
+..
+public class TestNonShared implements Runnable {
+  ...
+  public static void main (String[] args){
+    
+    // set JPF properties programmatically
+    Verify.setProperties("listener+=.tools.SharedChecker", ...);
+    ..
+  }
+}
+~~~~~~~~
+
+This method should only be used in special cases (models written explicitly for JPF verification), since it does not run outside JPF.
+
+
+### JPF API (embedded mode) ###
+
+
+If JPF is explicitly started from within another application, listeners can be instantiated at will and configured via the `JPF.addListener(..)` API:
+
+~~~~~~~~ {.java}
+MyListener listener=new MyListener(..);
+..
+Config config = JPF.createConfig( args);
+JPF jpf = new JPF( config);
+jpf.addListener(listener);
+jpf.run();
+..
+~~~~~~~~
+
+Most listeners tend to fall into three major categories: 
+
+1. system class (e.g. for logging) - is usually configured via the default.properties.
+2. complex properties - is configured with an application specific mode property file.
+3. JPF debugging - is specified via the command line (`+key=value` overrides).
diff --git a/doc/devel/logging.md b/doc/devel/logging.md
new file mode 100644 (file)
index 0000000..3993fdf
--- /dev/null
@@ -0,0 +1,31 @@
+# The JPF Logging API #
+There is one simple rule: do not use `System.out` or `System.err` for any permanent logging
+
+
+Of course we all do this temporarily during debugging, but it really shouldn't stay in the code. The logging infrastructure is quite easy to use. Just declare a static `Logger` instance with an appropriate id (either package or logging topic) at the top of your class, and then use the `Logger` API to create output:
+
+~~~~~~~~ {.java}
+...
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+package x.y.z;
+
+class MyClass .. {
+  static Logger log = JPF.getLogger("x.y.z");
+  ...
+    log.severe("there was an error");
+  ...
+    log.warning("there was a problem");
+  ...
+    log.info("something FYI");
+  ...
+    if (log.isLoggable(Level.FINE)){    // (1) don't create garbage
+        log.fine("this is some detailed info about: " + something);
+    }
+  ...
+}
+~~~~~~~~
+
+Note that there is only one instance for each `Logger` ID, i.e. you can have a corresponding static field in all your relevant classes, and don't have to share the fields. Another aspect that is mostly important for the lower log levels (e.g. `FINE`) is that you should't concatenate log messages in operations that occur frequently, since the corresponding `StringBuilder` instances can cause performance degradations even if the log level is not set (the arguments still get evaluated). In this case, encapsulate the logging in `log.isLoggable(level){..}` blocks.
+
diff --git a/doc/devel/mercurial.md b/doc/devel/mercurial.md
new file mode 100644 (file)
index 0000000..b1b3051
--- /dev/null
@@ -0,0 +1,56 @@
+# Mercurial in 5 Minutes #\r
+\r
+[Mercurial](http://www.selenic.com/mercurial) is a *distributed version control system* (DVCS) of the same category such as the likes of [Git](http://git-scm.com/) or [Bazaar](http://bazaar.canonical.com/en/). If you know nothing else about DVCS, this means mostly one thing - **all repositories are created equal**. There is no such thing as a different repository format for public and private repositories. All repositories (that are synchronized) have the same, stored history information.\r
+\r
+Yes, this means you can finally synchronize your working repos with more than one "master" repository, e.g. with several local ones, closed workgroup repos, and a public master repo (that just happens to be designated as such).\r
+\r
+Each Mercurial repository consists of an (optional) working copy of your files, and a `.hg` directory in your repo root dir that holds all the version control info. Be careful not to delete the `.hg` dir, otherwise your repo is reduced to just a snapshot of your current working files.\r
+\r
+A central concept of DVCS is the *change set* - think of it as the diff of all files that have been changed when you do a commit. This is what DVCS store (e.g. Mercurial in the `.hg` dir) and compare between different repositories. One implication of this is that you can't do partial commits like in CVS or SVN (e.g. by just committing changes within a certain subdirectory). You always have to execute the Mercurial commands in the top dir of the repository you are working in.\r
+\r
+If you have previously worked with CVS or SVN, another difference you have to wrap your head around is that there are commands that work locally, and commands that interact with the remote repo. __The `commit` and `update` commands are only local__. The `push` and `pull` commands synchronize your local repo with an external one (like commit and update did in CVS/SVN).\r
+\r
+Here are the main commands to interact with Mercurial:\r
+\r
+![](mercurial.png)\r
+\r
+**hg clone <url>** - this is the first command that clones an external repository (either from `file:///...` or `http://...` URLs). It creates both a working copy and the `.hg` directory (with contents)\r
+\r
+**hg init** - is what you do if you create a local repository for which there is no external one yet. Just create your files, `cd` into the top directory, and execute the command (which will create the `.hg` for you)\r
+\r
+**hg pull [-u] <url>** - updates the repo you are currently in from the provided URL. Note that your working copy is only updated if you use the `-u` option\r
+\r
+**hg incoming <url>** - is the little brother of `pull`. It just tells you if there are changesets in the remote repository you would pull\r
+\r
+**hg status** - tells you if there are uncommitted changes in your working copy that have to be committed to the local `.hg` directory before you can `push` or `pull`\r
+\r
+**hg diff** - does a bit more, it also shows you a diff file with the uncommitted changes\r
+\r
+**hg add <file>** - adds a file to the repo\r
+\r
+**hg remove <file>** - removes a file from the repo\r
+\r
+**hg addremove** - adds all new files of the working copy that are not in the repository yet, and removes all which are not in the working copy anymore\r
+\r
+**hg commit** - saves changes in your working copy to your local `.hg`. Do this early and often, this is now just a local operation that doesn't change anything outside the repo you are working with\r
+\r
+**hg update** - would update your working copy from your local `.hg`. It is rarely used if you pull with the `-u` option\r
+\r
+**hg push <url>** - pushes the relevant changesets of your local `.hg` back to the external repo with the provided URL. Make sure you have no uncommitted changes (Mercurial would refuse to push), and - in case the external repo is shared - that you pulled/merged all changesets of the external repo before you push\r
+\r
+**hg outgoing <url>** - is the dry run version of a push, it tells you if there are changesets that would have to be pushed  \r
+\r
+**hg revert** - reverts your working copy to a previous version stored in your local `.hg`\r
+\r
+**hg heads** - tells you if there are branches in your repo, which usually happens if you pull from a changed external repo while having un-pushed local changes that are committed\r
+\r
+**hg merge** - gets rid of multiple heads. Make sure to get rid of these by merging as soon as you detect them, it only gets more difficult with more external and local changes. Once there are collisions, you are back to the CVS/SVN merge mess\r
+\r
+You can always obtain more information by executing\r
+~~~~~~~~\r
+#!sh\r
+> hg help [command]\r
+~~~~~~~~\r
+If you don't list a command, all available ones will be displayed.\r
+\r
+Commands that refer to external repos take URLs such as `http://babelfish.arc.nasa.gov/hg/jpf/jpf-core` as arguments.\r
diff --git a/doc/devel/mji.md b/doc/devel/mji.md
new file mode 100644 (file)
index 0000000..817cdd1
--- /dev/null
@@ -0,0 +1,214 @@
+# Model Java Interface (MJI) #
+## Purpose ##
+
+Even if it is just a Java application (i.e. solely consists of Java classes), JPF can be viewed as a Java Virtual Machine (JVM) in itself. The consequence is that (*.class) class files, and even the same files at times, are processed in two different ways in a JVM running JPF
+
+  * as ordinary Java classes managed and executed by the host JVM (standard Java library classes, JPF implementation classes)
+  * as "modeled" classes managed and processed (verified) by JPF
+
+Class lookup in both layers is based on the CLASSPATH environment variable / command line parameter, but this should not obfuscate the fact that we have to clearly distinguish between these two modes. In particular, JPF (i.e. the "Model" layer) has its own class and object model, which is completely different and incompatible to the (hidden) class and object models of the underlying host JVM executing JPF
+
+![Figure 1: JPF Layers](../graphics/jpf-layers.svg){align=center width=560}
+
+Each standard JVM supports a so called Java Native Interface (JNI), that is used to delegate execution from the Java level (i.e. JVM controlled bytecode) down into the (platform dependent) native layer (machine code). This is normally used to interface certain functionalities to the platform OS / architecture (e.g. I/O or graphics).
+
+Interestingly enough, there exists a analogous need to lower the "execution" level in JPF, from JPF controlled bytecode into JVM controlled bytecode. According to this analogy, the JPF specific interface is called Model Java interface (MJI).
+
+Even though MJI offers a wide range of applications, there are three major usages for delegating bytecode execution into the host JVM:
+
+ 1. Interception of native methods - without a abstraction lowering mechanism, JPF would be forced to completely ignore native methods, i.e. would fail on applications relying on the side effects of such methods, which is not acceptable (even if  many native methods indeed can be ignored if we restrict the set of verification targets)
+
+ 2. Interfacing of JPF system level functionality - some system level functions of standard library classes (esp. java.lang.Class, java.lang.Thread) have to be intercepted even if they are not native because they have to affect the JPF internal class, object and thread model (etc. loading classes, creating / starting threads). It should be noted that MJI can also be used to extend the functionality of JPF without changing its implementation.
+
+3. State space reduction - by delegating bytecode execution into the non-state-tracked host JVM, we can cut off large parts of the state space, provided that we know the corresponding method side effects are not relevant for property verification (e.g. `System.out.println(..)`)
+
+Besides these standard usages, there exist more exotic applications like collecting information about JPF state space exploration and making it available both to JPF and the verification target.
+
+
+## MJI Components ##
+
+
+The basic functionality of MJI consists of a mechanism to intercept method invocations, and delegate them by means of Java reflection calls to dedicated classes. There are two types of classes involved, residing at different layers:
+
+  * Model Classes - these classes execute by the VM of JPF, which might be completely unknown to the host JVM
+  * Native Peers - these classes, implemented by `NativePeer` subclasses, contain the implementations of the methods to intercept, and to execute by the host JVM
+
+As part of the JPF implementation, MJI automatically takes care of determining which method invocations have to be intercepted by looking up the corresponding native peer methods
+
+![Figure 2: MJI Functions](../graphics/mji-functions.svg){align=center width=600}
+
+This would not be very useful without being able to access the JPF object model (or other JPF intrinsics), from inside the native peer methods. Instead of requiring all native peers implementation to reside in a JPF internal package, there exists an interface class `MJIEnv` that provide access to the JPF internal structure in a controlled way. `NativePeer` classes  residing in `gov.nasa.jpf.vm` (i.e. the same package as `MJIEnv`) can reach all internal JPF features. Outside this package, the available API in `MJIEnv` is mostly restricted to the access JPF object (getting and setting values).
+
+![Figure 3: MJI Call Sequence](../graphics/mji-call.svg){align=center width=580}
+
+Before a native peer method can be used, JPF has to establish the correspondence between the model class and the native peer. This takes place at load time of the model class. MJI uses a special name mangling scheme to lookup native peers, using the model class package name and class name to deduce the native peer class name.
+
+![Figure 3: MJI name mangling](../graphics/mji-mangling.svg){align=center width=560}
+
+Since the model class package is encoded in the native peer name, the package of the native peer can be chosen freely. In analogy to JNI, native peers methods names include the signature of the model method by encoding its parameter types. If there is no potential ambiguity, i.e. mapping from native peer methods to model class methods is unique, signature encoding is not required.
+
+Each native peer, which is an instance of a `NativePeer` subclass, is associated with exactly one `ClassInfo` instance.
+All the native peer methods to be intercepted have to be `public` and annotated with `gov.nasa.jpf.annotation.MJI`. 
+Moreover, MJI requires them to have two parameters: 
+
+  * An instance of `MJIEnv` which can be used to access JPF internal constructs
+  * An integer which is a handle for the corresponding JPF `this` object (or the `java.lang.Class` object in case of a static method) including the method to be intercepted
+
+See [Mangling for MJI](mji/mangling) for more details and examples of mangling.
+
+Going beyond the JNI analogy, MJI can also be used to intercept
+
+  * non-native methods (i.e. the lookup process is driven by the methods found in the native peer, not the `native` attributes in the model class. This can be particularly useful in case the class is used from both as a model class and a JVM class (e.g. `gov.nasa.jpf.vm.Verify`), using a method body that directly refers to the native peer
+  * class initialization (the corresponding native peer method has to be named `$clinit(MJIEnv env, int clsRef)`)
+  * constructors (the corresponding native peer method has to be named `$init__<sig>(MJIEnv env,int objRef, <ctor-params>)` and the normal signature mangling rules apply)
+
+
+It is important to note that type correspondence does NOT include references. All references (object types) on the JPF side are transformed in handles (int values) on the JVM side. The passed in `MJIEnv` parameter has to be used to convert/analyze the JPF object. Since MJI per default uses the standard Java reflection call mechanism, there is a significant speed penalty (lookup, parameter conversion etc.), which again is a analogy to JNI.
+
+Even if it is not directly related to MJI, it should be mentioned that some JPF specific model classes cannot be loaded via the CLASSPATH (e.g. `java.lang.Class`), since they contain JPF based code that is not compatible with the host JVM (e.g. relying on native methods that refer to JPF functionality). Such classes should be kept in separate directories / jars that are specified with the JPF command line option `-jpf-bootclasspath` or `-jpf-classpath`. This is mostly the case for system classes. On the other hand, model classes don't have to be JPF specific. It is perfectly fine to provide a native peer for a standard Java class (e.g. `java.lang.Character`), if only certain methods from that standard class needs to be intercepted. Native peers can contain any number of non-"native" methods and fields, but those should not be annotated with `@MJI` to avoid lookup problems.
+
+## Tools ##
+
+To ease the tedious process of manually mangle method names, MJI includes a tool to automatically create skeletons of native peers from a given Model class, called `GenPeer`. The translation process uses Java reflection, i.e. the model class needs to be in the CLASSPATH and is specified in normal dot notation (i.e. not as a file).
+
+![Figure 4: The GenPeer tool](../graphics/genpeer.svg){align=center width=470}
+
+There exist a number of command line options that can be displayed by calling `GenPeer` without arguments. `GenPeer` per default writes to stdout, i.e. the output has to be redirected into a file.
+
+Since method invocation by reflection used to be a slow operation, we previously also had a tool called `GenPeerDispatcher`, which used method signature hash-codes to explicitly dispatch native peer methods. With recent improvements in JVM performance, this tool became obsolete.
+
+
+## Example ##
+
+
+The following example is an excerpt of a JPF regression test, showing how to intercept various different method types, and using `MJIEnv` to access JPF objects.
+
+
+### Model class (JPF side) ###
+
+
+This is executed by JPF, which means it needs to be in JPF's  `vm.classpath` setting. 
+
+~~~~~~~~ {.java}
+public class TestNativePeer {
+    static int sdata;
+    
+    static {
+      // only here to be intercepted
+    }
+    
+    int idata;
+    
+    TestNativePeer (int data) {
+      // only here to be intercepted
+
+    }
+  
+    public void testClInit () {
+      if (sdata != 42) {
+        throw new RuntimeException("native 'clinit' failed");
+      }
+    }
+
+    public void testInit () {
+      TestNativePeer t = new TestNativePeer(42);
+
+      if (t.idata != 42) {
+        throw new RuntimeException("native 'init' failed");
+      }
+    }
+
+    native int nativeInstanceMethod (double d, char c, boolean b, int i);
+
+    public void testNativeInstanceMethod () {
+
+      int res = nativeInstanceMethod(2.0, '?', true, 40);
+      if (res != 42) {
+        throw new RuntimeException("native instance method failed");
+      }
+    }
+    
+    native long nativeStaticMethod (long l, String s);
+
+    public void testNativeStaticMethod () {
+      long res =  nativeStaticMethod(40, "Blah");
+
+      if (res != 42) {
+        throw new RuntimeException("native instance method failed");
+
+      }
+    }
+    
+    native void nativeException ();
+    
+    public void testNativeException () {
+      try {
+        nativeException();
+      } catch (UnsupportedOperationException ux) {
+        String details = ux.getMessage();
+
+        if ("caught me".equals(details)) {
+          return;
+        } else {
+          throw new RuntimeException("wrong native exception details: " +
+                                     details);
+        }
+      } catch (Throwable t) {
+        throw new RuntimeException("wrong native exception type: " +
+                                   t.getClass());
+      }
+
+      throw new RuntimeException("no native exception thrown");
+    }
+  }
+~~~~~~~~
+
+
+### Native Peer class (host VM side) ###
+
+
+This is executed by the host VM (i.e. at the same level like JPF itself), so make sure it is in your CLASSPATH.
+
+~~~~~~~~ {.java}
+public class JPF_gov_nasa_jpf_vm_TestNativePeer {
+
+    @MJI
+    public static void $clinit (MJIEnv env, int rcls) {
+      env.setStaticIntField(rcls, "sdata", 42);
+    }
+
+    @MJI
+    public static void $init__I (MJIEnv env, int robj, int i) {
+      env.setIntField(robj, "idata", i);
+    }
+
+    // preferably use full signature mangling
+    @MJI
+    public static int nativeInstanceMethod__DCZI__I (MJIEnv env, int robj,
+                                            double d, char c,
+                                            boolean b, int i) {
+      if ((d ## 2.0) && (c  ## '?') && b) {
+        return i + 2;
+      }
+      return 0;
+    }
+
+    //..but it also works without, if there is no overloading (don't be lazy!) 
+    @MJI
+    public static long nativeStaticMethod (MJIEnv env, int rcls,
+                                           long l, int stringRef) {
+      String s = env.getStringObject(stringRef);
+      if ("Blah".equals(s)) {
+        return l + 2;
+      }
+      return 0;
+    }
+
+    @MJI
+    public static void nativeException____V (MJIEnv env, int robj) {
+      env.throwException("java.lang.UnsupportedOperationException",
+                         "caught me");
+    }
+  }
+~~~~~~~~
+
diff --git a/doc/devel/mji/mangling.md b/doc/devel/mji/mangling.md
new file mode 100644 (file)
index 0000000..5e2847a
--- /dev/null
@@ -0,0 +1,200 @@
+# Mangling for MJI #
+## Mangling Methods ##
+Suppose your method looks like 
+
+~~~~~~~~ {.java}
+T1 foo(T2 x, T3 y, ...)
+~~~~~~~~
+
+where the `Ti` are Java types.
+
+If `T1` is a primitive type or `void`, then the mangled MJI method looks
+like
+
+~~~~~~~~ {.java}
+public static T1 foo__MT2MT3...__MT1(...)
+~~~~~~~~
+
+where the `MTi` are the mangled versions of the `Ti`.  
+Mangling of types is described in the Mangling Types section below. 
+Note that `T1` appears twice, once not mangled (`T1`) and once mangled
+(`MT1`).  The `__` is two consecutive underscores: `_` followed by
+`_`.
+
+As a not-so-special case, if `foo` has no arguments, then the mangled method
+will have four consecutive underscores:
+
+~~~~~~~~ {.java}
+  `T1 foo()`[[br]]
+~~~~~~~~
+
+goes to
+
+~~~~~~~~ {.java}
+  `public static T1 foo____MT1(...)`
+~~~~~~~~
+
+If `T1` is not a primitive type, then the mangled `MJI` method looks like 
+
+~~~~~~~~ {.java}
+  `public static int foo__MT2MT3...__MT1`
+~~~~~~~~
+
+where the `MTi` are as above.  Note that `T1` only appears once in this
+case.  The method's return type is `int`.  As before, a method with no
+arguments gets mangled to something with four consecutive underscores.
+
+Also, the use of generics is ignored when mangling names.
+
+
+## Mangling Constructors ##
+Constructors are treated as methods named `$init` with return type `void`.
+
+
+## Mangling Static Initializers ##
+Static initializers are treated as methods named `$clinit` with no
+arguments and return type `void`.  Thus, their MJI versions always
+have the mangled signature:
+
+~~~~~~~~ {.java}
+public static void $clinit____V (MJIEnv env, int clsObjRef)
+~~~~~~~~
+
+or the equivalent unmangled signature:
+
+~~~~~~~~ {.java}
+public static void $clinit (MJIEnv env, int clsObjRef)
+~~~~~~~~
+
+
+## Mangling Types ##
+  - Convert primitives and `void` as follows
+
+    |Java Type|Mangled Type|
+    | ------- |:---:|
+    |`boolean`|`Z`|
+    |`byte`   |`B`|
+    |`char`   |`C`|
+    |`short`  |`S`|
+    |`int`    |`I`|
+    |`long`   |`J`|
+    |`float`  |`F`|
+    |`double` |`D`|
+    |`void`   |`V`|
+
+  - Convert a non-array reference type `T` in package `x.y`
+    (e.g. `java.lang.String`) as follows
+    - `x.y.T`   --> `Lx_y_T_2`
+    - Example: `java.lang.String` --> `Ljava_lang_String_2`
+
+  - Convert an array of primitive type `T`
+    (e.g. `byte[]`) as follows:
+    - `T[]` --> `_3MT`  where `MT` is the mangled version of `T`
+      (e.g. for `T=byte`, `MT=B`)
+    - Example: `byte[]` --> `_3B`
+
+  - Convert an array of reference type `T` in package `x.y`
+    (e.g. `java.lang.String[]`) as follows:
+    - `x.y.T[]` --> `_3Lx_y_T_2`
+    - Example: `java.lang.String[]` --> `_3Ljava_lang_String_2`
+
+
+## Method Examples ##
+
+ `void` return type, single primitive argument:
+
+~~~~~~~~ {.java}
+  public static void resetCounter(int id)
+-->
+  public static final void resetCounter__I__V(MJIEnv env, int objref, int id)
+~~~~~~~~
+
+ Primitive return type, no arguments:
+
+~~~~~~~~ {.java}
+  public native boolean isArray()
+-->
+  public static boolean isArray____Z(MJIEnv env, int objref)
+~~~~~~~~
+
+ Primitive return type, single primitive argument:
+
+~~~~~~~~ {.java}
+  public static double abs(double a)
+-->
+  public static double abs__D__D(MJIEnv env, int clsObjRef, double a)
+~~~~~~~~
+
+ Primitive return type, two primitive arguments:
+
+~~~~~~~~ {.java}
+  public static long min(long a, long b)
+--> 
+  public static long min__JJ__J(MJIEnv env, int clsObjRef, long a, long b)
+~~~~~~~~
+
+
+ `void` return type, arguments include an array of a primitive type:
+
+~~~~~~~~ {.java}
+  public native void write (byte[] buf, int off, int len);
+-->
+  public static void write___3BII__V(MJIEnv env, int objref,
+                                     int bufRef, int off, int len)
+~~~~~~~~
+
+
+ `void` return type, argument is an array of a reference type: 
+
+~~~~~~~~ {.java}
+   public static void print(String s)
+-->
+  public static void print___3Ljava_lang_String_2__V(MJIEnv env, int clsRef, int argsRef)
+~~~~~~~~
+
+ Array of reference types returned, no arguments: 
+~~~~~~~~ {.java}
+  public native Annotation[] getAnnotations()
+-->
+  public static int getAnnotations_____3Ljava_lang_annotation_Annotation_2(MJIEnv env, int robj)
+~~~~~~~~
+   Notice there are 5 underscores before the `3L`: two marking the
+   arguments, two marking the return type, and one from the `_3`
+   signalling an array.
+
+ Array of reference types using generics returned, no arguments:
+
+~~~~~~~~ {.java}
+  public native Class<?>[] getParameterTypes()
+-->
+  public static int getParameterTypes_____3Ljava_lang_Class_2(MJIEnv env, int objref)
+~~~~~~~~
+    
+Note: the use of generics is ignored in the mangling.
+
+
+
+## Constructor Examples ##
+
+Constructors are treated as though they were methods named `$init`
+returning `void`, so the method examples above should also be helpful
+for constructors.  Here are a few more examples.
+
+In the class `ConsoleOutputStream`:
+
+~~~~~~~~ {.java}
+  public ConsoleOutputStream()
+-->
+  public static void $init____V(MJIEnv env, int objref)
+~~~~~~~~
+
+In the class `AtomicLongFieldUpdater`:
+
+~~~~~~~~ {.java}
+  protected AtomicLongFieldUpdater(Class<T> objClass, String fieldName)
+-->
+  public static void $init__Ljava_lang_Class_2Ljava_lang_String_2__V
+                         (MJIEnv env, int objRef,
+                          int tClsObjRef, int fNameRef)
+~~~~~~~~
diff --git a/doc/devel/modules.md b/doc/devel/modules.md
new file mode 100644 (file)
index 0000000..45e84d7
--- /dev/null
@@ -0,0 +1,21 @@
+# JPF Runtime Modules #
+
+JPF is partitioned into separate projects that all follow the same directory layout and build process. Modules can be distributed as source or binary distributions. Binary distributions are just slices through the directory tree of a source distribution that preserve the permanent build artifact, i.e. both distribution forms are runtime-compatible.
+
+![Figure: JPF Modules](../graphics/jpf-project.svg){align=center width=750}
+The main artifacts are the *.jar files created and stored in the `build` directory. We can divide this into classes that are executed by the host VM (i.e. have to be in JPF's `native_classpath` setting), and classes that are executed by JPF itself (i.e. have to be in JPF's `classpath` setting). The first category includes [listeners](listener) and [native peers](mji), the second one model classes (compiled from `src/classes`) and annotations, i.e. the system under test code.
+
+The build process is [Ant](http://ant.apache.org/) based, which means every source distribution comes with a build.xml script that implements the basic build targets `clean`, `compile`, `build` and `test`.
+
+We do not include required 3rd party runtime and build libraries in the project distributions.
+The `compile` Ant target uses the standard `javac` command which requires a full JDK installation. `test` generally executes a JUnit based regression test suite. Both JUnit and Ant libraries are also need to be installed.
+
+<included in the jpf-core distribution, which also contains the minimal RunAnt.jar executable jar which can be distributed with other JPF projects to use the jpf-core provided 3rd party build tools.>
+
+<The `lib` directory contains 3rd party libraries that are required at runtime of the project (like bcel.jar in jpf-core).>
+
+<tools contains programs and libraries that are used by the build process (like ant.jar and junit.jar in jpf-core).>
+
+For convenience reasons, JPF modules come with corresponding NetBeans and Eclipse configurations, i.e. can be directly opened as projects within these IDEs.
+
diff --git a/doc/devel/partial_order_reduction.md b/doc/devel/partial_order_reduction.md
new file mode 100644 (file)
index 0000000..7077a48
--- /dev/null
@@ -0,0 +1,67 @@
+# On-the-fly Partial Order Reduction #
+
+The number of different scheduling combinations is the prevalent factor for the state space size of concurrent programs. Fortunately, for most practical purposes it is not necessary to explore all possible instruction interleavings for all threads. The number of scheduling induced states can be significantly reduced by grouping all instruction sequences in a thread that cannot have effects outside this thread itself, collapsing them into a single transition. This technique is called partial order reduction (POR), and typically results in more than 70% reduction of state spaces.
+
+JPF employs an on-the-fly POR that does not rely on user instrumentation or static analysis. JPF automatically determines at runtime which instructions have to be treated as state transition boundaries. If POR is enabled (configured via `vm.por` property), a forward request to the VM executes all instructions in the current thread until one of the following conditions is met:
+
+ - the next instruction is scheduling relevant
+ - the next instruction yields a "nondeterministic" result (i.e. simulates random value data acquisition)
+
+Detection of both conditions are delegated to the instruction object itself (`Instruction.execute()`), passing down information about the current VM execution state and threading context. If the instruction is a transition breaker, it creates a ChoiceGenerator and schedules itself for re-execution.
+
+~~~~~~~~ {.java}
+executeStep () {
+  ..
+  do {
+    if ((nextPc # executeInstruction()) #= pc) {
+      break;
+    } else {
+      pc = nextPc;
+    }
+    ..
+  } while (pc != null);
+  ..
+}
+~~~~~~~~
+
+Each bytecode instruction type corresponds to a concrete gov.nasa.jpf.Instruction subclass that determines scheduling relevance based on the following factors:
+
+ * **Instruction Type** - due to the stack based nature of the JVM, only about 10% of the Java bytecode instructions are scheduling relevant, i.e. can have effects across thread boundaries. The interesting instructions include direct synchronization (`monitorEnter`, `monitorexit`, `invokeX` on synchronized methods), field access (`putX`, `getX`), array element access (`Xaload`, `Xastore`), and invoke calls of certain Thread (`start()`, `sleep()`, `yield()`, `join()`) and Object methods (`wait()`, `notify()`).
+ * **Object Reachability** - besides direct synchronization instructions, field access is the major type of interaction between threads. However, not all putX / getX instructions have to be considered, only the ones referring to objects that are reachable by at least two threads can cause data races. While reachability analysis is an expensive operation, the VM already performs a similar task during garbage collection, which is extended to support POR.
+ * **Thread and Lock Information** - even if the instruction type and the object reachability suggest scheduling relevance, there is no need to break the current transition in case there is no other runnable thread. In addition, lock acquisition and release (`monitorenter`, `monitorexit`) do not have to be considered as transition boundaries if there they happen recursively - only the first and the last lock operation can lead to rescheduling.
+
+![Figure 1: Scheduling Relevance Filters](../graphics/por-scheduling-relevance.svg){align=center width=650}
+
+While JPF uses these informations to automatically deduce scheduling relevance, there exist three mechanisms to explicitly control transition boundaries (i.e. potential thread interleavings)
+
+ 1. `Attributor` - a configurable concrete class of this type is used by JPF during class loading to determine object, method and field attributes of selected classes and class sets. The most important attributes with respect to POR are method atomicity and scheduling relevance levels: (a) never relevant, (b) always scheduling relevant, (c) only relevant in the context of other runnables. (d) only relevant of top-level lock. The default Attributor executes all java code atomically, which is can be too aggressive (i.e. can cause `BlockedAtomicExceptions`).
+
+ 2. `VMListener` - a listener can explicitly request a reschedule by calling `ThreadInfo.yield()` in response of a instruction execution notification.
+
+ 3. `Verify` - a class that serves as an API to communicate between the test application and JPF, and contains `beginAtomic()`, `endAtomic()` functions to control thread interleaving
+
+The main effort of JPFs POR support relates to extending its precise mark and sweep collector. POR reachability is a subset of collector reachability, hence the mechanism piggybacks on the mark phase object traversal. It is complicated by the fact that certain reference chains exist only in the (hidden) VM implementation layer. For instance, every thread has a reference to its ThreadGroup, and the ThreadGroup objects in turn have references to all included threads, hence - from a garbage collection perspective - all threads within a group are mutually reachable. If the application under test does not use Java reflection and runtime queries like thread enumeration, POR reachability should follow accessibility rules as closely as possible. While JPF's POR does not yet support protected and private access modifiers, it includes a mechanism to specify that certain fields should not be used to promote POR reachability. This attribute is set via the configured Attributor at class load time.
+
+![Figure 2: Mark Phase of Reachability Analysis](../graphics/por-mark.svg){align=center width=460}
+
+With this mechanism, calculating POR reachability becomes a straight forward approach that is divided into two phases. Phase 1 non-recursively marks all objects of the root set (mostly static fields and thread stacks), recording the id of the referencing thread. In case an object is reachable from a static field, or from two threads, it's status is set to shared. Phase 2 recursively traverses all heap objects, propagating either a set shared status or the referencing thread id through all reference fields that are not marked as reachability firewalls. Again, if the traversal hits an object that is already marked as referenced by another thread, it promotes the object status to shared, and from there propagates the shared status instead of the thread id.
+
+To further reduce irrelevant context switches, JPF can check for lock protection to determine if a field access is scheduling relevant. If the property `vm.por.sync_detection` is set to true, JPF looks for potential lock candidates when analyzing `GET_x/SET_x` instructions. The policy of detecting lock candidates is configurable with the property `vm.por.fieldlockinfo.class`, the default `gov.nasa.jpf.jvm.StatisticFieldLockInfo` just defers the decision by recording the set of locks held when executing the field instruction, computing the set intersection at subsequent accesses. If the set does not get empty for a configurable number of field accesses, the field is marked as lock protected, and henceforth not treated as transition boundary. If however the set should afterwards become empty, a warning like
+
+~~~~~~~~ {.bash}
+Warning: unprotected field access of: Event@70.count in thread: "Thread-1" oldclassic.java:107
+   sync-detection assumed to be protected by: Event@70
+   found to be protected by: {}
+   >>> re-run with 'vm.por.sync_detection=false' or exclude field from checks <<<
+~~~~~~~~
+
+is issued, and the field access is again treated as transition breaker.
+
+The set of fields to be analyzed can be specified with the properties `vm.por.include_fields` and `vm.por.exclude_fields`. Per default, fields of system library classes are not analyzed (this is esp. useful to avoid context switches for global objects like `System.out`).
+
+Even with all these optimizations, some unwanted transition breakers are likely to remain. This is mostly due to two constraints:
+
+ * JPF only considers reachability, not accessibility
+ * write once, read multiple conditions cannot be detected a priori for fields that are not final, or inside immutable objects (like `java.lang.String`)
+
+Especially the last issue might be subject to further enhancements
diff --git a/doc/devel/report.md b/doc/devel/report.md
new file mode 100644 (file)
index 0000000..aecfd6b
--- /dev/null
@@ -0,0 +1,70 @@
+# The JPF Report API #
+The JPF report system consists of three major components: 
+
+  - the `Reporter`
+  - any number of format specific `Publisher` objects
+  - any number of tool-, property-, Publisher-specific `PublisherExtension` objects
+
+Here is the blueprint:
+
+![Figure: JPF Report System](../graphics/report.svg){align=center width=800}
+
+The `Reporter` is the data collector. It also manages and notifies `Publisher` extensions when a certain output phase is reached. The `Publishers` are the format (e.g. text, XML) specific output producers, the most prominent one being the `ConsolePublisher` (for normal, readable text output on consoles). `PublisherExtensions` can be registered for specific `Publishers` at startup time, e.g. from Listeners implementing properties or analysis modes such as `DeadlockAnalyzer`. This is so common that the `ListenerAdapter` actually implements all the required interface methods so that you just have to override the ones you are interested in.
+
+Configuration is quite easy, and involves only a handful of JPF properties that are all in the report category. The first property specifies the Reporter class itself, but is not likely to be redefined unless you have to implement different data collection modes.
+
+~~~~~~~~ {.bash}
+report.class=gov.nasa.jpf.report.Reporter
+~~~~~~~~
+
+The next setting specifies a list of Publisher instances to use, using symbolic names:
+
+~~~~~~~~ {.bash}
+report.publisher=console,xml
+~~~~~~~~
+
+Each of these symbolic names has to have a corresponding class name defined:
+
+~~~~~~~~ {.bash}
+report.console.class=gov.nasa.jpf.report.ConsolePublisher
+~~~~~~~~
+
+Finally, we have to specify for each symbolic publisher name and output phase what topics should be processed in which order, e.g.
+
+~~~~~~~~ {.bash}
+report.console.property_violation=error,trace,snapshot
+~~~~~~~~
+
+Again, the order of these topics matters, and gives you complete control over the report format. As usual, please refer to `defaults.properties` for default values.
+
+Publisher classes can have their own, additional properties. For instance, the `ConsolePublisher` implementation can be further configured with respect to the information that is included in traces (bytecodes, method names etc.), and to redirect output (file, socket). Please refer to the constructor of this class for further details.
+
+~~~~~~~~ {.bash}
+# save report to file
+report.console.file=My_JPF_report
+~~~~~~~~
+
+All of the involved core classes and interfaces reside in the `gov.nasa.jpf.report` package. The most common way to extend the system is to use your own `PublisherExtension` implementation, which involves two steps:
+
+  - implement the required phase and format specific methods
+  - register the extension for a specific Publisher class
+
+
+The `DeadlockAnalyzer` (which is a listener to analyze concurrency defects) can be used as an example of how to do this:
+
+~~~~~~~~ {.java}
+public class DeadlockAnalyzer extends ListenerAdapter {
+  ...
+  public DeadlockAnalyzer (Config config, JPF jpf){
+    jpf.addPublisherExtension(ConsolePublisher.class, this);  // (1)
+    ...
+  }
+  ...
+  public void publishPropertyViolation (Publisher publisher) { // (2)
+    PrintWriter pw = publisher.getOut();
+    publisher.publishTopicStart("thread ops " + publisher.getLastErrorId());
+    
+    ... // use 'pw' to generate the output
+  }
+}
+~~~~~~~~
diff --git a/doc/graphics/DFSListener.svg b/doc/graphics/DFSListener.svg
new file mode 100644 (file)
index 0000000..1684827
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="62 54 384 228" width="32pc" height="19pc" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2005-10-21 18:50:44 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="588.0188" height="768.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id26_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id27_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id28_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id29_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id30_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id31_Graphic" filter="url(#Shadow)"/></g><g id="id26_Graphic"><path d="M 91.05 67 L 155.45 67 C 163.0676 67 169.25 71.704 169.25 77.5 C 169.25 83.296 163.0676 88 155.45 88 L 91.05 88 C 83.4324 88 77.25 83.296 77.25 77.5 C 77.25 71.704 83.4324 67 91.05 67" fill="white"/><path d="M 91.05 67 L 155.45 67 C 163.0676 67 169.25 71.704 169.25 77.5 C 169.25 83.296 163.0676 88 155.45 88 L 91.05 88 C 83.4324 88 77.25 83.296 77.25 77.5 C 77.25 71.704 83.4324 67 91.05 67" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id27_Graphic"><path d="M 352.55 67 L 416.95 67 C 424.5676 67 430.75 71.704 430.75 77.5 C 430.75 83.296 424.5676 88 416.95 88 L 352.55 88 C 344.9324 88 338.75 83.296 338.75 77.5 C 338.75 71.704 344.9324 67 352.55 67" fill="white"/><path d="M 352.55 67 L 416.95 67 C 424.5676 67 430.75 71.704 430.75 77.5 C 430.75 83.296 424.5676 88 416.95 88 L 352.55 88 C 344.9324 88 338.75 83.296 338.75 77.5 C 338.75 71.704 344.9324 67 352.55 67" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id28_Graphic"><path d="M 136.475 118 L 211.025 118 C 219.8432 118 227 122.704 227 128.5 C 227 134.296 219.8432 139 211.025 139 L 136.475 139 C 127.6568 139 120.5 134.296 120.5 128.5 C 120.5 122.704 127.6568 118 136.475 118" fill="white"/><path d="M 136.475 118 L 211.025 118 C 219.8432 118 227 122.704 227 128.5 C 227 134.296 219.8432 139 211.025 139 L 136.475 139 C 127.6568 139 120.5 134.296 120.5 128.5 C 120.5 122.704 127.6568 118 136.475 118" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id29_Graphic"><path d="M 231.1125 183 L 301.6375 183 C 309.9796 183 316.75 187.704 316.75 193.5 C 316.75 199.296 309.9796 204 301.6375 204 L 231.1125 204 C 222.7704 204 216 199.296 216 193.5 C 216 187.704 222.7704 183 231.1125 183" fill="white"/><path d="M 231.1125 183 L 301.6375 183 C 309.9796 183 316.75 187.704 316.75 193.5 C 316.75 199.296 309.9796 204 301.6375 204 L 231.1125 204 C 222.7704 204 216 199.296 216 193.5 C 216 187.704 222.7704 183 231.1125 183" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id30_Graphic"><path d="M 247.55 148.5 L 311.95 148.5 C 319.5676 148.5 325.75 153.204 325.75 159 C 325.75 164.796 319.5676 169.5 311.95 169.5 L 247.55 169.5 C 239.9324 169.5 233.75 164.796 233.75 159 C 233.75 153.204 239.9324 148.5 247.55 148.5" fill="white"/><path d="M 247.55 148.5 L 311.95 148.5 C 319.5676 148.5 325.75 153.204 325.75 159 C 325.75 164.796 319.5676 169.5 311.95 169.5 L 247.55 169.5 C 239.9324 169.5 233.75 164.796 233.75 159 C 233.75 153.204 239.9324 148.5 247.55 148.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id31_Graphic"><path d="M 202.55 231 L 284.45 231 C 294.1376 231 302 235.704 302 241.5 C 302 247.296 294.1376 252 284.45 252 L 202.55 252 C 192.8624 252 185 247.296 185 241.5 C 185 235.704 192.8624 231 202.55 231" fill="white"/><path d="M 202.55 231 L 284.45 231 C 294.1376 231 302 235.704 302 241.5 C 302 247.296 294.1376 252 284.45 252 L 202.55 252 C 192.8624 252 185 247.296 185 241.5 C 185 235.704 192.8624 231 202.55 231" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 131.82329 138.16666 C 132.88209 140.61086 134.72058 143.048855 135 145.5 C 135.27942 147.951145 134.72904 151.04185 133.5 152.875 C 132.27096 154.70815 129.874775 156.1667 127.625 156.5 C 125.375225 156.8333 121.604006 156.31236 120 154.875 C 118.395994 153.43764 117.02568 150.964375 118 147.875 C 118.33188 146.82268 119.04462 145.594735 119.94362 144.28092" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 300.2699 236.20079 C 302.39772 235.01331 304.5007 234.23864 306.654 232.638 C 308.8073 231.03736 312.13694 229.05675 313.191 226.596 C 314.24506 224.13525 314.7705 221.5729 312.979 217.872 C 312.15253 216.16467 310.59337 214.11944 308.80859 211.9702" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(89.5 71.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".25927734" y="10" textLength="68.481445">searchStarted</tspan></text><text transform="translate(349.5 70.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".203125" y="10" textLength="74.59375">searchFinished</tspan></text><text transform="translate(127 121.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="4.6135254" y="10" textLength="72.77295">stateAdvanced</tspan></text><text transform="translate(241 152.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".044433594" y="10" textLength="47.68994">propertyV</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="47.541016" y="10" textLength="32.41455">iolated</tspan></text><text transform="translate(224.5 187.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".31274414" y="10" textLength="84.37451">stateBacktracked</tspan></text><text transform="translate(194.5 234.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".203125" y="10" textLength="96.59375">searchConstraintHit</tspan></text><path d="M 168.67215 76.98571 L 180.57215 76.98571 L 190.47357 76.98571 C 195.94198 76.98571 200.375 81.41873 200.375 86.88714 L 200.375 106.1 L 200.375 108.1" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 226.33107 127.98571 L 238.23107 127.98571 L 272.23107 127.98571 C 277.75392 127.98571 282.23107 123.50856 282.23107 117.98571 L 282.23107 87.5 C 282.23107 81.977153 286.70823 77.5 292.23107 77.5 L 326.85 77.5 L 328.85 77.5" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 200.375 139 L 200.375 150.9 L 200.375 154.54757 C 200.375 156.56207 202.00807 158.19515 204.02257 158.19515 L 221.89 158.19515 L 223.89 158.19515" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 325.17215 158.48571 L 337.07215 158.48571 L 375.7735 158.48571 C 381.29635 158.48571 385.7735 154.00856 385.7735 148.48571 L 385.7735 99.9 L 385.7735 97.9" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 316.11719 192.98571 L 328.01719 192.98571 L 388.22082 192.98571 C 393.74367 192.98571 398.22082 188.50856 398.22082 182.98571 L 398.22082 99.9 L 398.22082 97.9" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 301.26512 240.98571 L 313.16512 240.98571 L 400.5722 240.98571 C 406.09504 240.98571 410.5722 236.50856 410.5722 230.98571 L 410.5722 99.9 L 410.5722 97.9" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 173.23588 139 L 173.23588 150.9 L 173.23588 182.69515 C 173.23588 188.218 177.71303 192.69515 183.23588 192.69515 L 204.1438 192.69515 L 206.1438 192.69515" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 146.75778 139 L 146.75778 150.9 L 146.75778 230.69515 C 146.75778 236.218 151.23493 240.69515 156.75778 240.69515 L 173.15087 240.69515 L 175.15087 240.69515" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 242.93519 252 L 242.93519 263.9 L 242.93519 267.45 C 242.93519 269.41061 241.3458 271 239.38519 271 L 109 271 C 103.47715 271 99 266.52285 99 261 L 99 132.5183 C 99 129.854546 101.1594 127.69515 103.82315 127.69515 L 108.646305 127.69515 L 110.646305 127.69515" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 226.71194 203.16666 C 227.59954 205.40255 229.22284 207.77799 229.375 209.875 C 229.52716 211.97201 228.85404 214.12516 227.625 215.75 C 226.39596 217.37484 224.22894 219.08339 222 219.625 C 219.77106 220.16661 215.89567 220.14572 214.25 219 C 212.60433 217.85428 210.99047 215.69356 212.125 212.75 C 212.54779 211.65307 213.48253 210.31699 214.64302 208.87548" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/app-types.svg b/doc/graphics/app-types.svg
new file mode 100644 (file)
index 0000000..f845707
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="20 24 747 537" width="747pt" height="537pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2015-01-05 22:17:29 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><filter id="Shadow_2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker_2" viewBox="-9 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M -8 0 L 0 3 L 0 -3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Lucida Grande" font-size="11" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="15" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-800" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="black"><g><path d="M 5.5999994 0 L 0 -2.0999998 L 0 2.0999998 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Times New Roman" font-size="20" panose-1="2 2 8 3 7 5 5 2 3 4" units-per-em="1000" underline-position="-108.88672" underline-thickness="95.214844" slope="0" x-height="456.54297" cap-height="662.10938" ascent="891.1133" descent="-216.3086" font-weight="bold"><font-face-src><font-face-name name="TimesNewRomanPS-BoldMT"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -2 4 4" markerWidth="4" markerHeight="4" color="black"><g><path d="M 1.6 0 L 0 -.6 L 0 .6 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>App Types</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id50_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id65_Graphic" filter="url(#Shadow)"/></g><g filter="url(#Shadow_2)"><path d="M 327.333 77.9986 L 474.333 77.9986 C 480.40813 77.9986 485.333 82.92347 485.333 88.9986 L 485.333 311.5916 C 485.333 317.66673 480.40813 322.5916 474.333 322.5916 L 327.333 322.5916 C 321.25787 322.5916 316.333 317.66673 316.333 311.5916 L 316.333 88.9986 C 316.333 82.92347 321.25787 77.9986 327.333 77.9986 Z" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/></g><g id="id50_Graphic"><path d="M 282.2563 114.6481 C 282.2563 109.43795 281.63467 108.79335 277.09383 103.6371 L 277.04586 103.58375 C 272.48103 98.3736 272.43356 98.3736 267.74881 98.3736 C 261.51097 98.3736 233.309 98.3736 233.309 98.3736 L 233.309 153.3736 L 282.2563 153.3736 L 282.2563 114.6481 Z" fill="white"/><path d="M 282.2563 114.6481 C 282.2563 109.43795 281.63467 108.79335 277.09383 103.6371 L 277.04586 103.58375 C 272.48103 98.3736 272.43356 98.3736 267.74881 98.3736 C 261.51097 98.3736 233.309 98.3736 233.309 98.3736 L 233.309 153.3736 L 282.2563 153.3736 L 282.2563 114.6481 Z M 282.2563 114.3797 C 282.2563 109.43795 282.20833 109.43795 272.43356 109.43795 L 272.43356 109.43795 C 272.43356 98.4275 272.43356 98.3736 268.03564 98.3736" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><line x1="31.2632" y1="385.317" x2="755.896" y2="385.317" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="16,9,1,9"/><line x1="300.567" y1="128.5" x2="506.433" y2="128.5" marker-end="url(#Arrow_Marker)" marker-start="url(#Arrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(118.333 406.626)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="133.21924">non-functional properties</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="110.08594"> unhandled exceptions</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="37" textLength="41.561523">     (incl. </tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="40.95996" y="37" textLength="73.95996">AssertionError)</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="51" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="51" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="51" textLength="52.58838"> deadlocks</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="65" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="65" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="65" textLength="29.95459"> races</tspan></text><text transform="translate(118.333 318.229)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="119.8291">restricted choice types</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="111.91211"> scheduling sequences</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="38" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="38" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="38" textLength="88.04297"> java.util.Random </tspan></text><text transform="translate(118.333 478.959)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="107.572266">improved inspection</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="93.5376"> coverage statistics</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="38" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="38" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="38" textLength="97.22217"> exact object counts</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="52" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="52" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="52" textLength="78.87451"> execution costs</tspan></text><text transform="translate(36.33314 309.333) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="15" textLength="72.53174">constraints</tspan></text><text transform="translate(38.9582 487.959) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="15" textLength="40.86914">benefi</tspan><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="40.86914" y="15" textLength="11.6674805">ts</tspan></text><text transform="translate(527.5 202)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="152.81836">restricted application models</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="26.280762"> UML</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="37.952148" y="24" textLength="73.981445"> statemachines</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="38" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="38" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="38" textLength="151.620605"> does not run w/o JPF libraries </tspan></text><text transform="translate(118.666 200.667)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="72.12842">runtime costs</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="130.23291"> order of magnitude slower</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="38" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="38" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="38" textLength="109.430664"> state storage memory</tspan></text><text transform="translate(118.333 244.97)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="127.13379">standard library support</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="116.77832"> java.net, javax.swing, ..</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="37" textLength="151.01367">     (needs abstraction models) </tspan></text><text transform="translate(528.197 437.293)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="158.89307">functional (domain) properties</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="120.430664"> built-in into JPF libraries</tspan></text><text transform="translate(118.666 288.728)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="160.74072">functional property impl. costs</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="123.49756"> listeners, MJI knowledge</tspan></text><text transform="translate(528.197 467.96)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="6.7192383">fl</tspan><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="6.7192383" y="10" textLength="94.166016">exible state space</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="73.36914"> domain specifi</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="85.44336" y="24" textLength="45.853027">c choices</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="37" textLength="63.572266">     (e.g. UML</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="63.169434" y="37" textLength="91.58789"> &quot;enabling events&quot;)</tspan></text><text transform="translate(528.864 511.294)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="163.80762">runtime costs &amp; library support</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="149.80518"> usually not a problem, domain</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="37" textLength="147.96289">     libs can control state space</tspan></text><text transform="translate(142 114.334)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="57.481445">runs on any</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="23" textLength="22">JVM</tspan></text><text transform="translate(597.018 112.38)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="44.63379">runs only</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="23" textLength="50.746094">under JPF</tspan></text><text transform="translate(528.197 406.626)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="102.066895">low modeling costs</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="165.08594"> statemachine w/o layout hassle,..</tspan></text><text transform="translate(527.833 247.333)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="131.40918">initial domain impl. costs</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="12.074219" y="24" textLength="126.55371"> domain libs can be tricky </tspan></text><text transform="translate(408 85.8125)" fill="black"><tspan font-family="Lucida Grande" font-size="11" font-weight="500" x="0" y="11" textLength="65.5542">&quot;sweet spot&quot;</tspan></text><text transform="translate(345.41 172.334)" fill="red"><tspan font-family="Helvetica" font-size="11" font-weight="bold" fill="red" x="0" y="10" textLength="93.51074">annotate program</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="24" textLength="67.251465"> requirements</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="38" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="38" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="38" textLength="89.86914"> sequences (UML)</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="52" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="52" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="52" textLength="79.46533"> contracts (PbC)</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="66" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="66" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="66" textLength="26.286133"> tests</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="79" textLength="23.22461">    …</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="92" textLength="3.0561523"> </tspan></text><text transform="translate(345.41 259.291)" fill="red"><tspan font-family="Helvetica" font-size="11" font-weight="bold" fill="red" x="0" y="10" textLength="87.41992">analyze program</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="24" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="24" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="24" textLength="72.13379"> symbolic exec</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="38" textLength="15.280762">     </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="15.280762" y="38" textLength="11">→</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="26.280762" y="38" textLength="45.251465"> test data</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="0" y="52" textLength="6.1123047">  </tspan><tspan font-family="Lucida Grande" font-size="11" font-weight="500" fill="red" x="6.1123047" y="52" textLength="5.961914">■</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="red" x="12.074219" y="52" textLength="105.77295"> thread safety / races </tspan></text><path d="M 443.077 271.319 C 448.50012 269.28287 455.12626 268.07605 459.348 265.21 C 463.56974 262.34395 467.8014 258.03911 468.41 254.121 C 469.0186 250.20289 466.9996 245.38813 463 241.699 C 460.3534 239.25783 455.78244 237.2113 451.19748 235.16785" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(241.566 140.124)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="10" textLength="29.453125">*.class</tspan></text><g id="id65_Graphic"><path d="M 424.2383 117.1085 C 424.2383 111.89835 423.61667 111.25375 419.07583 106.0975 L 419.02786 106.04415 C 414.46303 100.834 414.41556 100.834 409.7308 100.834 C 403.49297 100.834 375.291 100.834 375.291 100.834 L 375.291 155.834 L 424.2383 155.834 L 424.2383 117.1085 Z" fill="white"/><path d="M 424.2383 117.1085 C 424.2383 111.89835 423.61667 111.25375 419.07583 106.0975 L 419.02786 106.04415 C 414.46303 100.834 414.41556 100.834 409.7308 100.834 C 403.49297 100.834 375.291 100.834 375.291 100.834 L 375.291 155.834 L 424.2383 155.834 L 424.2383 117.1085 Z M 424.2383 116.8401 C 424.2383 111.89835 424.19033 111.89835 414.41556 111.89835 L 414.41556 111.89835 C 414.41556 100.8879 414.41556 100.834 410.01764 100.834" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(385.548 142.585)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="10" textLength="25.014648">*.java</tspan></text><text transform="translate(381.548 115.758)" fill="blue"><tspan font-family="Times New Roman" font-size="20" font-weight="bold" fill="blue" x="0" y="18" textLength="33.046875">@V</tspan></text><path d="M 574.3423 117.1085 C 574.3423 111.89835 573.72067 111.25375 569.17983 106.0975 L 569.13186 106.04415 C 564.56703 100.834 564.51956 100.834 559.8348 100.834 C 553.59697 100.834 525.395 100.834 525.395 100.834 L 525.395 155.834 L 574.3423 155.834 L 574.3423 117.1085 Z" fill="white"/><path d="M 574.3423 117.1085 C 574.3423 111.89835 573.72067 111.25375 569.17983 106.0975 L 569.13186 106.04415 C 564.56703 100.834 564.51956 100.834 559.8348 100.834 C 553.59697 100.834 525.395 100.834 525.395 100.834 L 525.395 155.834 L 574.3423 155.834 L 574.3423 117.1085 Z M 574.3423 116.8401 C 574.3423 111.89835 574.29433 111.89835 564.51956 111.89835 L 564.51956 111.89835 C 564.51956 100.8879 564.51956 100.834 560.12164 100.834" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><ellipse cx="543.737" cy="113.0895" rx="5.3750086" ry="4.0625065" fill="yellow"/><ellipse cx="543.737" cy="113.0895" rx="5.3750086" ry="4.0625065" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><ellipse cx="556.987" cy="126.2145" rx="5.3750086" ry="4.0625065" fill="red"/><ellipse cx="556.987" cy="126.2145" rx="5.3750086" ry="4.0625065" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><ellipse cx="538.987" cy="135.2145" rx="5.3750086" ry="4.0625065" fill="blue"/><ellipse cx="538.987" cy="135.2145" rx="5.3750086" ry="4.0625065" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 549.51266 113.920245 C 550.629 114.080814 551.84554 113.92592 552.862 114.402 C 553.87846 114.87808 555.03463 115.563874 555.612 116.777 C 555.80122 117.17458 555.9176 117.662774 555.9969 118.19702" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 559.38672 130.37215 C 560.37838 132.09026 562.65776 134.17099 562.362 135.527 C 562.06624 136.88301 560.5531 138.39227 557.612 138.509 C 555.40905 138.596435 551.6817 137.6995 548.14292 136.92421" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 538.68525 130.65814 C 538.5775 129.031255 537.41597 126.75709 538.362 125.777 C 539.30803 124.79691 542.22674 124.81438 544.362 124.777 C 545.42796 124.75834 546.5616 124.8872 547.71222 125.05311" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(535.842 142.467)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="10" textLength="25.014648">*.java</tspan></text><text transform="translate(198.618 42.3331)" fill="black"><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="15" textLength="88.374023">JPF unaware</tspan><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="33" textLength="63.354492">programs</tspan></text><text transform="translate(354.033 33.4506)" fill="black"><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="15" textLength="84.22119">JPF enabled</tspan><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="33" textLength="63.354492">programs</tspan></text><text transform="translate(498.368 43.1018)" fill="black"><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="15" textLength="101.74072">JPF dependent</tspan><tspan font-family="Helvetica" font-size="15" font-style="italic" font-weight="500" x="0" y="33" textLength="63.354492">programs</tspan></text></g></g></svg>
diff --git a/doc/graphics/attributes.svg b/doc/graphics/attributes.svg
new file mode 100644 (file)
index 0000000..2aa20a4
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="98 7 445 412" width="445pt" height="412pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-16 18:21:11 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><font-face font-family="Helvetica" font-size="8" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="8" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><filter id="Shadow_2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation=".75079507"/><feOffset in="blur" result="offset" dx=".5" dy=".5"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><filter id="Shadow_3" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation=".75079507"/><feOffset in="blur" result="offset" dx="1" dy="1"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="black"><g><path d="M 4.7999992 0 L 0 -1.7999997 L 0 1.7999997 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier New" font-size="9" panose-1="2 7 6 9 2 2 5 2 4 4" units-per-em="1000" underline-position="-232.91016" underline-thickness="100.097656" slope="0" x-height="443.35938" cap-height="591.79688" ascent="832.51953" descent="-300.29297" font-weight="bold"><font-face-src><font-face-name name="CourierNewPS-BoldMT"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="black"><g><path d="M 4 0 L 0 -1.5 L 0 1.5 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledBall_Marker" viewBox="-4 -3 5 6" markerWidth="5" markerHeight="6" color="black"><g><circle cx="-1.4999993" cy="0" r="1.4999987" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier New" font-size="10" panose-1="2 7 6 9 2 2 5 2 4 4" units-per-em="1000" underline-position="-232.91016" underline-thickness="100.097656" slope="0" x-height="443.35938" cap-height="591.79688" ascent="832.51953" descent="-300.29297" font-weight="bold"><font-face-src><font-face-name name="CourierNewPS-BoldMT"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-6 -3 7 6" markerWidth="7" markerHeight="6" color="black"><g><path d="M -4.8000002 0 L 0 1.8000001 L 0 -1.8000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1090.9091" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1200" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_4" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="#404040"><g><path d="M 4 0 L 0 -1.5 L 0 1.5 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledBall_Marker_2" viewBox="-4 -3 5 6" markerWidth="5" markerHeight="6" color="#404040"><g><circle cx="-1.4999993" cy="0" r="1.4999987" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><path d="M 129.05398 137.779 L 521.01013 137.779 C 526.53298 137.779 531.01013 142.25615 531.01013 147.779 C 531.01013 147.78732 531.01012 147.795646 531.0101 147.80397 L 530.5015 351.50493 C 530.4877 357.02776 525.99938 361.49372 520.47655 361.47993 C 520.47598 361.47993 520.4754 361.47993 520.47483 361.47992 L 319.923 360.94463 C 314.40018 360.92988 309.935 356.4408 309.94973 350.91797 C 309.94984 350.8784 309.95018 350.83881 309.95075 350.79924 L 310.80437 292.10691 C 310.88469 286.58465 306.47312 282.04286 300.95085 281.96255 C 300.923 281.96214 300.89516 281.96185 300.86732 281.96168 L 130.49925 280.90729 C 125.04834 280.87355 120.62861 276.48048 120.56189 271.02987 L 119.05473 147.9014 C 118.98713 142.37896 123.40915 137.84735 128.93158 137.77975 C 128.97238 137.77925 129.01318 137.779 129.05398 137.779 Z" fill="#fcffec"/><path d="M 129.05398 137.779 L 521.01013 137.779 C 526.53298 137.779 531.01013 142.25615 531.01013 147.779 C 531.01013 147.78732 531.01012 147.795646 531.0101 147.80397 L 530.5015 351.50493 C 530.4877 357.02776 525.99938 361.49372 520.47655 361.47993 C 520.47598 361.47993 520.4754 361.47993 520.47483 361.47992 L 319.923 360.94463 C 314.40018 360.92988 309.935 356.4408 309.94973 350.91797 C 309.94984 350.8784 309.95018 350.83881 309.95075 350.79924 L 310.80437 292.10691 C 310.88469 286.58465 306.47312 282.04286 300.95085 281.96255 C 300.923 281.96214 300.89516 281.96185 300.86732 281.96168 L 130.49925 280.90729 C 125.04834 280.87355 120.62861 276.48048 120.56189 271.02987 L 119.05473 147.9014 C 118.98713 142.37896 123.40915 137.84735 128.93158 137.77975 C 128.97238 137.77925 129.01318 137.779 129.05398 137.779 Z" stroke="#dabc2a" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="335.637" y="278.727" width="102.191" height="50.5717" fill="#faffc4"/><rect x="335.637" y="278.727" width="102.191" height="50.5717" stroke="#ffc189" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow)"><rect x="335.805" y="149.616" width="102.191" height="119.14" fill="#faffc4"/><rect x="335.805" y="149.616" width="102.191" height="119.14" stroke="#ffc189" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow)"><rect x="131.795" y="149.616" width="97.7571" height="95.5241" fill="#faffc4"/><rect x="131.795" y="149.616" width="97.7571" height="95.5241" stroke="#ffc189" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 119.83489 292.59206 L 291.39527 293.14156 C 296.91809 293.15925 301.38088 297.65072 301.36319 303.17354 C 301.36314 303.18956 301.36305 303.2056 301.36292 303.22162 L 300.91446 359.19774 C 300.87032 364.707 305.2906 369.21415 310.79969 369.2772 L 430.69996 370.6495 C 436.17774 370.7122 440.58542 375.17053 440.58552 380.64867 L 440.58582 397.60582 C 440.58592 403.12867 436.10885 407.6059 430.586 407.606 C 430.58594 407.606 430.58588 407.606 430.58582 407.606 L 120.528715 407.606 C 115.03568 407.606 110.57097 403.17527 110.52901 397.6824 L 109.80315 302.6684 C 109.76096 297.14571 114.20378 292.63449 119.72647 292.5923 C 119.76261 292.59202 119.79875 292.59194 119.83489 292.59206 Z" fill="#e5ffe3"/><path d="M 119.83489 292.59206 L 291.39527 293.14156 C 296.91809 293.15925 301.38088 297.65072 301.36319 303.17354 C 301.36314 303.18956 301.36305 303.2056 301.36292 303.22162 L 300.91446 359.19774 C 300.87032 364.707 305.2906 369.21415 310.79969 369.2772 L 430.69996 370.6495 C 436.17774 370.7122 440.58542 375.17053 440.58552 380.64867 L 440.58582 397.60582 C 440.58592 403.12867 436.10885 407.6059 430.586 407.606 C 430.58594 407.606 430.58588 407.606 430.58582 407.606 L 120.528715 407.606 C 115.03568 407.606 110.57097 403.17527 110.52901 397.6824 L 109.80315 302.6684 C 109.76096 297.14571 114.20378 292.63449 119.72647 292.5923 C 119.76261 292.59202 119.79875 292.59194 119.83489 292.59206 Z" stroke="#0e9319" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="337.865" y="68.4215" width="102.191" height="60" fill="white"/><rect x="337.865" y="68.4215" width="102.191" height="60" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(342.865 68.4215)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="8" textLength="77.808594">dup(), push(), pop(), ..</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="18" textLength="15.984375">------</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="28" textLength="70.253906">getOperandAttr(idx)</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="38" textLength="82.703125">setOperandAttr(idx,obj)</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="48" textLength="58.246094">getLocalAttr(idx)</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="58" textLength="70.695312">setLocalAttr(idx,obj)</tspan></text><rect x="337.865" y="28.4215" width="102.191" height="40" fill="white"/><rect x="337.865" y="28.4215" width="102.191" height="40" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(342.865 28.4215)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="8" textLength="35.570312">int[] locals</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="18" textLength="58.6875">Object[] localAttr</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="28" textLength="48.476562">int[] operands</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="38" textLength="71.59375">Object[] operandAttr</tspan></text><rect x="337.865" y="18.4215" width="102.191" height="10" fill="white"/><rect x="337.865" y="18.4215" width="102.191" height="10" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(342.865 18.4215)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="23.415812" y="8" textLength="45.359375">StackFrame</tspan></text><rect x="133.561" y="59.0283" width="102.191" height="70" fill="white"/><rect x="133.561" y="59.0283" width="102.191" height="70" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(138.561 59.0283)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="8" textLength="25.351562">getIntV</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="24.761719" y="8" textLength="41.792969">alue(idx), ...</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="18" textLength="24.902344">setIntV</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="24.3125" y="18" textLength="50.23828">alue(idx, v), ...</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="28" textLength="15.984375">------</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="38" textLength="56.460938">getFieldAttr(idx)</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="48" textLength="68.910156">setFieldAttr(idx,obj)</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="58" textLength="52.015625">getObjectAttr()</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" fill="blue" x="0" y="68" textLength="62.242188">setObjectAttr(obj)</tspan></text><rect x="133.561" y="29.0283" width="102.191" height="30" fill="white"/><rect x="133.561" y="29.0283" width="102.191" height="30" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(138.561 29.0283)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="8" textLength="38.242188">int[] values</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="18" textLength="33.789062">Object[] fi</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="33.789062" y="18" textLength="27.121094">eldAttrs</tspan><tspan font-family="Helvetica" font-size="8" font-weight="500" x="0" y="28" textLength="59.13672">Object objectAttr</tspan></text><rect x="133.561" y="19.0283" width="102.191" height="10" fill="white"/><rect x="133.561" y="19.0283" width="102.191" height="10" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(138.561 19.0283)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="34.536906" y="8" textLength="23.117188">Fields</tspan></text><rect x="350.285" y="163.457" width="75.3187" height="100" fill="white"/><rect x="350.285" y="163.457" width="75.3187" height="100" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="330.498" y1="193.894" x2="425.604" y2="193.894" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="387.872" y1="153.139" x2="387.872" y2="263.946" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow_2)"><rect x="353.396" y="167.278" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.396" y="167.278" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.198" y="167.048" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.198" y="167.048" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="353.396" y="178.857" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.396" y="178.857" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.198" y="178.627" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.198" y="178.627" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_2)"><rect x="353.396" y="199.952" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.396" y="199.952" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.198" y="199.722" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.198" y="199.722" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="353.396" y="211.531" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.396" y="211.531" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.198" y="211.301" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.198" y="211.301" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="353.396" y="223.11" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.396" y="223.11" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.198" y="222.88" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.198" y="222.88" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(335.377 190.062) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="23.009766">locals</tspan></text><text transform="translate(355.071 149.616)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="26.015625">values</tspan></text><text transform="translate(393.961 149.616)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="37.019531">attributes</tspan></text><text transform="translate(335.474 236.5) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="37.529297">operands</tspan></text><text transform="translate(309.594 187.863)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="18.505371">slots</tspan></text><rect x="144.642" y="183.546" width="75.3187" height="55.3838" fill="white"/><rect x="144.642" y="183.546" width="75.3187" height="55.3838" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="182.229" y1="173.228" x2="182.229" y2="239.159" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow_2)"><rect x="147.753" y="187.367" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="147.753" y="187.367" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="184.555" y="187.137" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="184.555" y="187.137" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="147.753" y="198.946" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="147.753" y="198.946" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="184.555" y="198.716" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="184.555" y="198.716" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_2)"><rect x="147.753" y="211.041" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="147.753" y="211.041" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="184.555" y="210.811" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="184.555" y="210.811" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="147.753" y="222.62" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="147.753" y="222.62" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="184.555" y="222.39" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="184.555" y="222.39" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(134.303 169.705)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">fi</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="4.5" y="9" textLength="41.02295">eld-values</tspan></text><text transform="translate(188.318 169.705)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="37.019531">attributes</tspan></text><line x1="234.498" y1="231.537" x2="322.887" y2="231.537" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="329.357" y1="206.886" x2="240.379" y2="206.886" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(259.457 194.858)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="43.20703">putfield</tspan></text><path d="M 428.964 210.165 L 447.019 210.165 L 447.019 228.737 L 435.664 228.737" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(259.457 221.198)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="43.20703">getfield</tspan></text><text transform="translate(449.973 209.928)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="16.202637">dup</tspan><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="17" textLength="10.801758">..</tspan></text><path d="M 428.169 168.156 L 497.385 168.156 L 497.385 237.983 L 434.869 237.983" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(500.669 202.849)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="27.004395">iload</tspan><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="17" textLength="10.801758">..</tspan></text><path d="M 429.803 200.798 L 456.803 200.798 L 456.803 180.798 L 434.503 180.798" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(458.813 182.982)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="32.405273">istore</tspan><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="17" textLength="10.801758">..</tspan></text><g filter="url(#Shadow_3)"><rect x="353.166" y="236.151" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.166" y="236.151" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="389.968" y="235.921" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="389.968" y="235.921" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><rect x="350.674" y="286.352" width="75.3187" height="34.5028" fill="white"/><rect x="350.674" y="286.352" width="75.3187" height="34.5028" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow_2)"><rect x="353.785" y="290.173" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.785" y="290.173" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.587" y="289.943" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.587" y="289.943" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="353.785" y="301.752" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="353.785" y="301.752" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="390.587" y="301.522" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="390.587" y="301.522" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 428.734 249.545 L 489.176 249.545 L 489.176 306.698 L 434.116 306.698" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow_3)"><rect x="352.936" y="248.531" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="352.936" y="248.531" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_3)"><rect x="389.738" y="248.301" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="389.738" y="248.301" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(454.692 309.299)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="70.211426">invokevirtual</tspan><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="17" textLength="10.801758">..</tspan></text><path d="M 428.603 291.278 L 446.659 291.278 L 446.659 259.189 L 434.503 259.189" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(448.227 265.618)" fill="black"><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="7" textLength="32.405273">return</tspan><tspan font-family="Courier New" font-size="9" font-weight="bold" x="0" y="17" textLength="10.801758">..</tspan></text><g filter="url(#Shadow)"><rect x="214.377" y="301.379" width="54" height="34" fill="white"/><rect x="214.377" y="301.379" width="54" height="34" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(219.377 308.379)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="6" y="8" textLength="32">attribute</tspan><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="10.220703" y="18" textLength="23.558594">object</tspan></text></g><path d="M 199.193 249.815 L 199.193 318.925 L 207.97754 318.8113" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 406.74574 294.90896 L 406.242 315.583 L 272.50995 315.01889" marker-end="url(#FilledArrow_Marker_2)" marker-start="url(#FilledBall_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(118.282 316.108)" fill="blue"><tspan font-family="Courier New" font-size="10" font-weight="bold" fill="blue" x="0" y="8" textLength="72.01172">setAttr(i,o)</tspan></text><text transform="translate(142.52 386.429)" fill="blue"><tspan font-family="Courier New" font-size="10" font-weight="bold" fill="blue" x="0" y="8" textLength="60.009766">getAttr(i)</tspan></text><g filter="url(#Shadow)"><rect x="208.685" y="363.774" width="65" height="34" fill="white"/><rect x="208.685" y="363.774" width="65" height="34" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(213.685 365.774)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="0" y="8" textLength="33.34375">- listener</tspan><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="0" y="18" textLength="46.21875">- Instruction</tspan><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="0" y="28" textLength="47.128906">- native peer</tspan></text></g><path d="M 201.48924 382.13757 L 191.346 382.486 L 191.55037 253.65499" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_3)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 280.88496 380.90578 L 403.869 381.314 L 403.869 332.244" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_3)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="241.23884" y1="363.274" x2="241.30251" y2="342.57897" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><text transform="translate(280.538 386.363)" fill="blue"><tspan font-family="Courier New" font-size="10" font-weight="bold" fill="blue" x="0" y="8" textLength="66.010742">get?Attr(i)</tspan></text><text transform="translate(362.525 383.486)" fill="blue"><tspan font-family="Courier New" font-size="10" font-weight="bold" fill="blue" x="0" y="8" textLength="78.012695">set?Attr(i,o)</tspan></text><text transform="translate(250.716 143.057)" fill="red"><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x=".4946289" y="10" textLength="44.010742">JPF core</tspan></text><text transform="translate(123.34 347.446)" fill="red"><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x="14.221924" y="10" textLength="19.556152">JPF</tspan><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x=".45581055" y="23" textLength="47.08838">extension</tspan></text><text transform="translate(257.781 101.477)" fill="blue"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="blue" x=".25878906" y="10" textLength="38.911133">attribute </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="blue" x="38.623047" y="10" textLength="16.118164">API</tspan></text><g filter="url(#Shadow_3)"><rect x="184.386" y="157.024" width="31.437" height="7.22235" fill="#e6e6e6"/><rect x="184.386" y="157.024" width="31.437" height="7.22235" stroke="#646464" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(140.259 155.826)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="40.016602">object-attr</tspan></text><line x1="342.24" y1="282.597" x2="342.24" y2="275.551" marker-end="url(#FilledArrow_Marker_4)" marker-start="url(#FilledBall_Marker_2)" stroke="#404040" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="363.056" y="337.39" width="77" height="16.4457" fill="white" fill-opacity=".76"/><rect x="363.056" y="337.39" width="77" height="16.4457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(368.056 341.61285)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="12.833984" y="8" textLength="41.332031">ThreadInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="156.551" y="257.852" width="77" height="16.4457" fill="white" fill-opacity=".76"/><rect x="156.551" y="257.852" width="77" height="16.4457" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(161.551 261.07485)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="bold" x="10.609375" y="8" textLength="45.78125">ElementInfo</tspan></text></g></g></g></svg>
diff --git a/doc/graphics/bc-factory.svg b/doc/graphics/bc-factory.svg
new file mode 100644 (file)
index 0000000..f69c951
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="-2 3 732 514" width="61pc" height="514pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-16 18:32:22 +0000</dc:date></metadata><defs><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 0 M 0 -3 L 8 0 L 0 3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier" font-size="11" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Courier" font-size="11" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker_2" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="gray"><g><path d="M 6.3999996 0 L 0 0 M 0 -2.3999999 L 6.3999996 0 L 0 2.3999999" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Times New Roman" font-size="14" panose-1="2 2 5 3 5 4 5 9 3 4" units-per-em="1000" underline-position="-108.88672" underline-thickness="48.828125" slope="-1166.6423" x-height="430.17578" cap-height="662.10938" ascent="891.1133" descent="-216.3086" font-style="italic" font-weight="500"><font-face-src><font-face-name name="TimesNewRomanPS-ItalicMT"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><path d="M 487.518 259.358 L 549.518 259.358 C 558.35456 259.358 565.518 266.52144 565.518 275.358 L 565.518 341.358 C 565.518 350.19456 558.35456 357.358 549.518 357.358 L 487.518 357.358 C 478.68144 357.358 471.518 350.19456 471.518 341.358 L 471.518 275.358 C 471.518 266.52144 478.68144 259.358 487.518 259.358 Z" fill="#e6e6e6"/><path d="M 487.518 259.358 L 549.518 259.358 C 558.35456 259.358 565.518 266.52144 565.518 275.358 L 565.518 341.358 C 565.518 350.19456 558.35456 357.358 549.518 357.358 L 487.518 357.358 C 478.68144 357.358 471.518 350.19456 471.518 341.358 L 471.518 275.358 C 471.518 266.52144 478.68144 259.358 487.518 259.358 Z" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 275.368 259.358 L 337.368 259.358 C 346.20456 259.358 353.368 266.52144 353.368 275.358 L 353.368 356.407 C 353.368 365.24356 346.20456 372.407 337.368 372.407 L 275.368 372.407 C 266.53144 372.407 259.368 365.24356 259.368 356.407 L 259.368 275.358 C 259.368 266.52144 266.53144 259.358 275.368 259.358 Z" fill="#e6e6e6"/><path d="M 275.368 259.358 L 337.368 259.358 C 346.20456 259.358 353.368 266.52144 353.368 275.358 L 353.368 356.407 C 353.368 365.24356 346.20456 372.407 337.368 372.407 L 275.368 372.407 C 266.53144 372.407 259.368 365.24356 259.368 356.407 L 259.368 275.358 C 259.368 266.52144 266.53144 259.358 275.368 259.358 Z" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 26.8252 54.5645 L 26.8252 19.3645 C 26.8252 16.9357 37.5772 14.9645 50.8252 14.9645 C 64.0732 14.9645 74.8252 16.9357 74.8252 19.3645 L 74.8252 54.5645 C 74.8252 56.9933 64.0732 58.9645 50.8252 58.9645 C 37.5772 58.9645 26.8252 56.9933 26.8252 54.5645" fill="white"/><path d="M 26.8252 54.5645 L 26.8252 19.3645 C 26.8252 16.9357 37.5772 14.9645 50.8252 14.9645 C 64.0732 14.9645 74.8252 16.9357 74.8252 19.3645 L 74.8252 54.5645 C 74.8252 56.9933 64.0732 58.9645 50.8252 58.9645 C 37.5772 58.9645 26.8252 56.9933 26.8252 54.5645 M 26.8252 19.3645 C 26.8252 21.7933 37.5772 23.7645 50.8252 23.7645 C 64.0732 23.7645 74.8252 21.7933 74.8252 19.3645" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(31.8252 32.1645)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="1.328125" y="11" textLength="35.34375">*.class</tspan></text><rect x="494.553" y="61.0215" width="121.415" height="28" fill="white"/><rect x="494.553" y="61.0215" width="121.415" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(499.553 61.0215)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="99.375">Instruction ifeq(tgt)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><rect x="494.553" y="35.0215" width="121.415" height="26" fill="white"/><rect x="494.553" y="35.0215" width="121.415" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(499.553 35.0215)" fill="#404040"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="30.96873" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="3.0346484" y="23" textLength="105.3457">InstructionFactory</tspan></text><rect x="639.695" y="200.846" width="61.7822" height="28" fill="white"/><rect x="639.695" y="200.846" width="61.7822" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(644.695 207.846)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="20.890123" y="11" textLength="10.001953">...</tspan></text><line x1="555.26" y1="152.712" x2="555.26028" y2="102.5215" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 304.67782 189.418 L 304.67782 182.418 L 304.67782 151.623 L 308.217 151.623 L 666.902 151.623 L 669.7412 151.623 L 669.7412 193.34685 L 669.7412 200.34685" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="515.096" y1="189.418" x2="515.93555" y2="151.623" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="352.606" y="107.24" width="121.415" height="14" fill="white"/><rect x="352.606" y="107.24" width="121.415" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(357.606 107.24)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="108.714844">Instruction execute()</tspan></text><rect x="352.606" y="93.2401" width="121.415" height="14" fill="white"/><rect x="352.606" y="93.2401" width="121.415" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(357.606 93.2401)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="24.708477" y="11" textLength="61.998047">Instruction</tspan></text><g filter="url(#Shadow)"><rect x="275.683" y="314.395" width="61.7822" height="28" fill="white"/><rect x="275.683" y="314.395" width="61.7822" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(280.683 321.395)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="11.8901234" y="11" textLength="28.001953">IFEQ</tspan></text></g><g filter="url(#Shadow)"><rect x="485.885" y="314.395" width="61.7822" height="28" fill="white"/><rect x="485.885" y="314.395" width="61.7822" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(490.885 321.395)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="11.8901234" y="11" textLength="28.001953">IFEQ</tspan></text></g><g filter="url(#Shadow)"><rect x="275.427" y="270.568" width="61.7822" height="28" fill="white"/><rect x="275.427" y="270.568" width="61.7822" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(280.427 277.568)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="20.890123" y="11" textLength="10.001953">...</tspan></text></g><g filter="url(#Shadow)"><rect x="485.885" y="270.857" width="61.7822" height="28" fill="white"/><rect x="485.885" y="270.857" width="61.7822" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(490.885 277.857)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="20.890123" y="11" textLength="10.001953">...</tspan></text></g><path d="M 246.11 247.41 L 245.412 327.791 L 265.2835 327.98724" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><line x1="245.776" y1="285.8723" x2="265.02941" y2="285.4575" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><path d="M 578.975 247.41 L 578.337 328.532 L 558.06717 328.4869" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><line x1="578.68897" y1="283.77946" x2="558.06563" y2="284.1384" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="4,4"/><path d="M 414.287 327.791 L 413.53368 157.12295 C 419.03363 157.09867 418.98507 146.09878 413.48513 146.12305 L 413.43488 134.73987" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="337.9652" y1="328.21897" x2="414.287" y2="327.791" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="485.385" y1="328.21" x2="414.287" y2="327.791" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(149.162 384.451)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="79.21289">Instruction </tspan><tspan font-family="Courier" font-size="11" font-weight="bold" fill="blue" x="79.21289" y="10" textLength="46.20752">execute</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="125.42041" y="10" textLength="39.606445"> (){..</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="23" textLength="151.82471">  cond = popCondition()</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="36" textLength="72.611816">  if (cond)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="49" textLength="138.62256">    return jumpTarget</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="62" textLength="39.606445">  else</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="75" textLength="158.42578">    return getNextInsn()</tspan></text><path d="M 329.824 76.4422 L 412.748 76.4422 L 412.87469 83.34177" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="216.085" y="82.7095" width="113.739" height="14" fill="white"/><rect x="216.085" y="82.7095" width="113.739" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(221.085 82.7095)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="78.708984">setCode(code)</tspan></text><rect x="216.085" y="54.7095" width="113.739" height="28" fill="white"/><rect x="216.085" y="54.7095" width="113.739" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(221.085 54.7095)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="36.011719">factory</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="91.38281">Instruction[] code</tspan></text><rect x="216.085" y="40.7095" width="113.739" height="14" fill="white"/><rect x="216.085" y="40.7095" width="113.739" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(221.085 40.7095)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="19.546258" y="11" textLength="64.646484">MethodInfo</tspan></text><line x1="329.824" y1="62.127" x2="484.653" y2="62.054543" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="74.8252" y1="36.9645" x2="93.35158" y2="36.74587" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="337.7092" y1="284.6111" x2="485.385" y2="284.8139" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="50.7243" y1="176.837" x2="717.844" y2="176.837" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="16,9,1,9"/><text transform="translate(53.112 183.541)" fill="red"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x=".23876953" y="12" textLength="31.875977">concr</tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="31.595215" y="12" textLength="132.166016">ete execution semantics</tspan></text><text transform="translate(53.612 153.212)" fill="red"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x=".130859375" y="12" textLength="161.73828">abstract execution semantics</tspan></text><text transform="translate(153.46 299.483)" fill="red"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x=".25439453" y="12" textLength="31.875977">concr</tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="31.61084" y="12" textLength="50.134766">ete value</tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="14.182617" y="28" textLength="53.634766">execution</tspan></text><text transform="translate(592.455 317.551)" fill="red"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x=".20849609" y="12" textLength="83.58301">symbolic value</tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="15.182617" y="28" textLength="53.634766">execution</tspan></text><text transform="translate(266.575 349.18)" fill="black"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" x=".13964844" y="12" textLength="79.720703">instruction set</tspan></text><text transform="translate(414.822 364.88)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="79.21289">Instruction </tspan><tspan font-family="Courier" font-size="11" font-weight="bold" fill="blue" x="79.21289" y="10" textLength="46.20752">execute</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="125.42041" y="10" textLength="33.005371">(){..</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="23" textLength="158.42578">  if (!firstStepInsn()){</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="36" textLength="250.84082">    setNextCG(new PCChoiceGenerator())</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="49" textLength="99.016113">    return this</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="62" textLength="19.803223">  }</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="75" textLength="224.43652">  popCondition() // not interested</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="88" textLength="211.23438">  cond = getCG().getNextChoice()</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="101" textLength="99.016113">  if (cond){...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="114" textLength="138.62256">    return jumpTarget</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="127" textLength="85.813965">  } else {...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="140" textLength="158.42578">    return getNextInsn()</tspan></text><text transform="translate(617.76 80.3873)" fill="red"><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="7.2963867" y="12" textLength="22.16211">per </tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="29.458496" y="11.753906" textLength="3.5"> </tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x="32.958496" y="12" textLength="49.745117">bytecode</tspan><tspan font-family="Times New Roman" font-size="14" font-style="italic" font-weight="500" fill="red" x=".091308594" y="28" textLength="89.817383">factory methods</tspan></text><rect x="232.381" y="215.418" width="139.537" height="28" fill="white"/><rect x="232.381" y="215.418" width="139.537" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(237.381 215.418)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="99.375">Instruction ifeq(tgt)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><rect x="232.381" y="189.418" width="139.537" height="26" fill="white"/><rect x="232.381" y="189.418" width="139.537" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(237.381 189.418)" fill="#404040"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="7.3270938" y="10" textLength="16.123047">gov</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="22.712836" y="10" textLength="99.49707">.nasa.jpf.jvm.bytecode</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="12.0956484" y="23" textLength="105.3457">InstructionFactory</tspan></text><rect x="429.84" y="215.418" width="174" height="28" fill="white"/><rect x="429.84" y="215.418" width="174" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(434.84 215.418)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="99.375">Instruction ifeq(tgt)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><rect x="429.84" y="189.418" width="174" height="26" fill="white"/><rect x="429.84" y="189.418" width="174" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(434.84 189.418)" fill="#404040"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="39.5708" y="10" textLength="16.123047">gov</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="54.956543" y="10" textLength="69.472656">.nasa.jpf.symbc</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="2.6523438" y="23" textLength="158.69531">SymbolicInstructionFactory</tspan></text><text transform="translate(12.40741 223.195)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="79.21289">Instruction </tspan><tspan font-family="Courier" font-size="11" font-weight="bold" fill="blue" x="79.21289" y="10" textLength="26.404297">ifeq</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="105.61719" y="10" textLength="112.21826">(int jumpTarget){</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="23" textLength="191.43115">  return new IFEQ(jumpTarget)</tspan></text><rect x="101.651" y="55.837" width="84.557" height="14" fill="white"/><rect x="101.651" y="55.837" width="84.557" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(106.651 55.837)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="10.2726406" y="11" textLength="54.01172">ClassInfo</tspan></text><rect x="101.651" y="29.149" width="84.557" height="14" fill="white"/><rect x="101.651" y="29.149" width="84.557" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(106.651 29.149)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="10.93182" y="11" textLength="52.69336">ClassFile</tspan></text><rect x="101.651" y="83.6054" width="84.557" height="14" fill="white"/><rect x="101.651" y="83.6054" width="84.557" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(106.651 83.6054)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="1.6095547" y="11" textLength="71.33789">CodeBuilder</tspan></text><line x1="215.602" y1="62.1806" x2="194.508" y2="62.1806" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="186.208" y1="90.6054" x2="207.785" y2="90.6054" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.01023" y1="43.149" x2="144.06628" y2="48.008652" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="143.92952" y1="69.837" x2="143.92953" y2="75.3054" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="345.178" y="20.3772" width="84.557" height="14" fill="white"/><rect x="345.178" y="20.3772" width="84.557" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(350.178 20.3772)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="18.285336" y="11" textLength="30.65625">Confi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="48.941586" y="11" textLength="7.330078">g</tspan></text><path d="M 387.45665 34.3772 L 387.457 51.2746 L 338.124 51.2746" marker-end="url(#StickArrow_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(435.518 12.0618)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="165.02686">vm.insn_factory.class=...</tspan></text></g></g></svg>
diff --git a/doc/graphics/cg-impl.svg b/doc/graphics/cg-impl.svg
new file mode 100644 (file)
index 0000000..07a14cb
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="5 12 774 590" width="774pt" height="590pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-02 07:14:49 +0000</dc:date></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="539.55078" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="bold"><font-face-src><font-face-name name="Helvetica-BoldOblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="1176.0376"/><g><title>Layer 1</title><path d="M 624 341 L 640 341 L 640 441.1" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(42.3333 21.6667)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="129.62109">initNextTransition</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="129.62109" y="11" textLength="43.20703">(){...</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="115.21875">  curCg = nextCg</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="108.01758">  nextCg = null</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="53" textLength="122.41992">  curCg.advance()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="67" textLength="144.02344">   ..setExecThread()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="81" textLength="36.00586">  ...</tspan></text><text transform="translate(19.7895 260)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="122.41992">executeTransition</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="122.41992" y="11" textLength="36.00586">(){..</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="172.82812">  isFirstStepInsn = true</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="158.42578">  while (pc != null) {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="53" textLength="237.63867">    nextPc = executeInstruction()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="67" textLength="208.83398">    if (ss.breakTransition())</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="81" textLength="79.21289">      break</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="95" textLength="57.609375">    else</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="109" textLength="122.41992">      pc = nextPc</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="123" textLength="194.43164">    isFirstStepInsn = false</tspan></text><text transform="translate(252.667 349.409)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="64.810547">execute()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="64.810547" y="11" textLength="21.603516">{..</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="180.0293">  if (!ti.isFirstStepInsn</tspan><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="180.0293" y="25" textLength="14.402344">()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="194.43164" y="25" textLength="21.603516">) {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="180.0293">    ChoiceGenerator cg = </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="53" textLength="259.24219">          ..createMonitorEnterCG(..)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="67" textLength="144.02344">    if (cg != null){</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="81" textLength="252.04102">      ss.setNextChoiceGenerator(cg)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="95" textLength="194.43164">      return this // repeat</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="109" textLength="21.603516">  }</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="137" textLength="93.615234">  ei.lock(ti)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="151" textLength="165.62695">  return getNextPc(ti);</tspan></text><rect x="458.041" y="280.5" width="90" height="14" fill="white"/><rect x="458.041" y="280.5" width="90" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(463.041 280.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="50.021484">execute()</tspan></text><rect x="458.041" y="266.5" width="90" height="14" fill="white"/><rect x="458.041" y="266.5" width="90" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(463.041 266.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="9.0009766" y="11" textLength="61.998047">Instruction</tspan></text><rect x="254" y="65" width="152" height="42" fill="white"/><rect x="254" y="65" width="152" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(259 65)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="47.34375">initNextT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="46.904297" y="11" textLength="53.34961">ransition()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="37.347656">breakT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="36.908203" y="25" textLength="53.34961">ransition()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="140.05664">setNextChoiceGenerator()</tspan></text><rect x="254" y="37" width="152" height="28" fill="white"/><rect x="254" y="37" width="152" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(259 37)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="38.021484">nextCg</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="32.009766">curCg</tspan></text><rect x="254" y="23" width="152" height="14" fill="white"/><rect x="254" y="23" width="152" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(259 23)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="34.982422" y="11" textLength="72.035156">SystemState</tspan></text><rect x="547.541" y="60" width="113.459" height="28" fill="white"/><rect x="547.541" y="60" width="113.459" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(552.541 60)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="98.033203">hasMoreChoices()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="53.361328">advance()</tspan></text><rect x="547.541" y="46" width="113.459" height="14" fill="white"/><rect x="547.541" y="46" width="113.459" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(552.541 46)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="3.0498125" y="11" textLength="97.359375">ChoiceGenerator</tspan></text><rect x="472" y="153" width="101" height="14" fill="white"/><rect x="472" y="153" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(477 153)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="86.70117">getNextChoice()</tspan></text><rect x="472" y="139" width="101" height="14" fill="white"/><rect x="472" y="139" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(477 139)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="52.03711">threadSet</tspan></text><rect x="472" y="125" width="101" height="14" fill="white"/><rect x="472" y="125" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(477 125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="16.496094" y="11" textLength="58.007812">ThreadCG</tspan></text><rect x="596" y="153" width="101" height="14" fill="white"/><rect x="596" y="153" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(601 153)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="86.70117">getNextChoice()</tspan></text><rect x="596" y="139" width="101" height="14" fill="white"/><rect x="596" y="139" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(601 139)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="54.046875">doubleSet</tspan></text><rect x="596" y="125" width="101" height="14" fill="white"/><rect x="596" y="125" width="101" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(601 125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="16.167969" y="11" textLength="58.664062">DoubleCG</tspan></text><rect x="713.438" y="125" width="54" height="18" fill="white"/><rect x="713.438" y="125" width="54" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(718.438 127)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="16.999023" y="11" textLength="10.001953">...</tspan></text><rect x="495" y="190" width="54" height="18" fill="white"/><rect x="495" y="190" width="54" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(500 192)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="16.999023" y="11" textLength="10.001953">...</tspan></text><rect x="85.4035" y="207" width="118" height="42" fill="white"/><rect x="85.4035" y="207" width="118" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(90.4035 207)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="49.359375">executeT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="48.919922" y="11" textLength="53.34961">ransition()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="105.38086">executeInstruction()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="87.35156">isFirstStepInsn()</tspan></text><rect x="85.4035" y="193" width="118" height="14" fill="white"/><rect x="85.4035" y="193" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(90.4035 193)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="64.04297">{pc, nextPc}</tspan></text><rect x="85.4035" y="179" width="118" height="14" fill="white"/><rect x="85.4035" y="179" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(90.4035 179)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="23.000977" y="11" textLength="61.998047">ThreadInfo</tspan></text><line x1="609.041" y1="112" x2="609.041" y2="101.5" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 521.57353 125 L 521 112 L 740.403 112 L 740.438 125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="646.4202" y1="125" x2="646.3708" y2="112" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="522" y1="190.257" x2="522" y2="179.757" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="366.5" y="327.5" width="90" height="18" fill="white"/><rect x="366.5" y="327.5" width="90" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(371.5 329.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="2.6699219" y="11" textLength="74.660156">MonitorEnter</tspan></text><rect x="553.5" y="327.5" width="68" height="18" fill="white"/><rect x="553.5" y="327.5" width="68" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(558.5 329.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="6.658203" y="11" textLength="44.683594">Invoke..</tspan></text><rect x="476.703" y="327.5" width="54" height="18" fill="white"/><rect x="476.703" y="327.5" width="54" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(481.703 329.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="16.999023" y="11" textLength="10.001953">...</tspan></text><line x1="503.5" y1="318.5" x2="503.5" y2="308" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 411.5 327.5 L 411.5 318.5 L 587.458 318.5 L 587.5 327.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="503.703" y1="327.5" x2="503.87076" y2="318.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="573.5" y="363.833" width="83" height="18" fill="white"/><rect x="573.5" y="363.833" width="83" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(578.5 365.833)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="4.1767578" y="11" textLength="64.646484">MethodInfo</tspan></text><rect x="591.5" y="399.833" width="83" height="18" fill="white"/><rect x="591.5" y="399.833" width="83" height="18" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(596.5 401.833)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="5.4804688" y="11" textLength="62.039062">NativePeer</tspan></text><text transform="translate(463.041 446)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="160.0957">e.g. JPF_gov_nasa_jpf_vm_V</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="159.43945" y="11" textLength="22.669922">erify</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="181.22461" y="11" textLength="91.39453">.getBoolean(env)</tspan></text><line x1="210" y1="469.665" x2="362" y2="469.665" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="8,4,1,4"/><text transform="translate(35 434.665)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="bold" fill="red" x="0" y="11" textLength="43.32422">top half</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="43.32422" y="11" textLength="81.38672">: executed on fi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="124.71094" y="11" textLength="70.69922">rst invocation</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="208.78125">optionally sets next CG and reexecutes</tspan></text><text transform="translate(35 475.665)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="bold" fill="red" x="0" y="11" textLength="65.320312">bottom half</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="65.320312" y="11" textLength="124.722656">: executed on revisit (or</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="187.42969">if no CG created because of policy)</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="39" textLength="165.43945">does semantic action based on</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="53" textLength="102.703125">current CGs choice</tspan></text><line x1="585" y1="131" x2="586" y2="246" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="8,4,1,4"/><text transform="translate(468 224)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="101.396484">scheduling choices</tspan></text><text transform="translate(601.5 224)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="127.41211">data acquisition choices</tspan></text><path d="M 700 163 L 747 163 L 747 546.1" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><line x1="406" y1="71" x2="537.641" y2="71" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 194 249 C 222 278.6667 258.6663 285.99975 307.333 289.333 C 352.12157 292.40069 408.50888 290.38684 448.14126 290.04641" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><circle cx="263.333" cy="171" r="8.000013" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(261.933 164)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="6.673828">1</tspan></text><circle cx="314.667" cy="277.333" r="8.000013" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(313.267 270.333)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="6.673828">2</tspan></text><circle cx="374.5" cy="207.333" r="8.000013" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(373.1 200.333)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="6.673828">3</tspan></text><line x1="426.041" y1="531.45" x2="585" y2="531.45" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="8,4,1,4"/><text transform="translate(424.333 511.333)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="39.363281">top half</tspan></text><text transform="translate(423.667 536.333)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="36.685547">bottom</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="19.347656">half</tspan></text><text transform="translate(465 466.95)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="194.43164">if (!ti.isFirstStepInsn()){</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="158.42578">  cg = new BooleanCG()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="223.23633">  ss.setNextChoiceGenerator(cg)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="53" textLength="194.43164">  env.repeatInvocation() ..</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="81" textLength="57.609375">} else {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="95" textLength="216.03516">  cg = ss.getChoiceGenerator()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="109" textLength="288.04688">  return ((BooleanCG)cg).getNextChoice()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="123" textLength="7.201172">}</tspan></text><text transform="translate(56 126.333)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="108.01758">breakTransition</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="108.01758" y="11" textLength="43.20703">(){...</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="165.62695">  return nextCg != null</tspan></text><path d="M 301.333 106 C 299.99966 155.3333 295.3337 164.6667 280.667 179 C 267.09314 192.2653 251.8064 204.10311 213.1948 210.54982" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 458.041 277.333 C 382.6786 281.99966 361.33353 214.0001 353 187.667 C 345.40257 163.6599 344.73164 148.240585 344.60825 115.9" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/></g></g></svg>
diff --git a/doc/graphics/cg-motivation.svg b/doc/graphics/cg-motivation.svg
new file mode 100644 (file)
index 0000000..e8c750c
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="36 72 668 428" width="668pt" height="428pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2010-02-22 05:27:10 +0000</dc:date></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 0 M 0 -3 L 8 0 L 0 3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker_2" viewBox="-9 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M -8 0 L 0 0 M 0 3 L -8 0 L 0 -3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Lucida Grande" font-size="12" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Lucida Grande" font-size="14" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><line x1="401.4" y1="354" x2="449.6" y2="354" marker-end="url(#StickArrow_Marker)" marker-start="url(#StickArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="425.5" y1="331" x2="425.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="440.5" y1="338" x2="440.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="410.5" y1="338" x2="410.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(412 316)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".38085938" y="12" textLength="7.330078">T</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.5" y="12" textLength="9.919922"> ±</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="17.419922" y="12" textLength="12.199219"> Δ</tspan></text><circle cx="425.5" cy="354" r="3.0000048" fill="white"/><circle cx="425.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="440.5" cy="354" r="3.0000048" fill="white"/><circle cx="440.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="410.5" cy="354" r="3.0000048" fill="white"/><circle cx="410.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(250.5215 365.55046)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="21.603516">.. </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="295.24805">double v = Verify.getDouble(&quot;velocity&quot;); </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="14.402344">..</tspan></text><text transform="translate(220.02 434.55)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="468.07617">velocity.class = gov.nasa.jpf.jvm.choice.DoubleThresholdGenerator</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="187.23047">velocity.threshold = 13250</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="144.02344">velocity.delta = 500</tspan></text><text transform="translate(132.175 372.129)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".020996094" y="13" textLength="93.95801">application code</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="14.13501" y="29" textLength="65.72998">(test driver)</tspan></text><text transform="translate(50 436)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="32.280518" y="13" textLength="27.459961">confi</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="59.74048" y="13" textLength="50.59082">guration </tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".49780273" y="29" textLength="123.55713">(e.g. mode property fi</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="124.05493" y="29" textLength="14.447266">le)</tspan></text><path d="M 474.5 396 C 475.1666 401.49945 481.8328 409.25033 476.5 412.5 C 471.1672 415.74967 475.91332 414.91673 442.5 415.5 C 409.08668 416.08327 308.41342 414.83345 276 416 C 243.58658 417.16655 250.58307 418.91703 248 422.5 C 246.49657 424.5854 249.56637 427.63104 253.21663 430.79843" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(476.5 345.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".32714844" y="12" textLength="29.683594">C = { </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="29.799805" y="12" textLength="7.330078">T</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="36.473633" y="12" textLength="3.9960938">-</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="40.469727" y="12" textLength="68.203125">Δ, T, T+ Δ }</tspan></text><text transform="translate(77.73514 82.637407)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="136.822266">Verify.getBoolean()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="129.62109">Verify.getInt(0,4)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="67" textLength="180.0293">Verify.getDouble(1.0,1.5)</tspan></text><text transform="translate(267.733 84)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="12" textLength="82.376953">C = { true, false</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="82.376953" y="12" textLength="7.6992188"> }</tspan></text><text transform="translate(267.233 111)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="12" textLength="89.72461">C = { 0, 1, 2, 3, 4</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="89.72461" y="12" textLength="7.6992188"> }</tspan></text><text transform="translate(373.733 83.6349)" fill="red"><tspan font-family="Lucida Grande" font-size="14" font-weight="500" fill="red" x="0" y="14" textLength="10.698242">✓</tspan></text><text transform="translate(374.733 108.635)" fill="red"><tspan font-family="Helvetica" font-size="14" font-weight="bold" fill="red" x="0" y="14" textLength="8.551758">?</tspan></text><text transform="translate(404.733 111)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="279.48633">potentially large sets with lots of uninteresting values</tspan></text><text transform="translate(374.733 135.635)" fill="red"><tspan font-family="Helvetica" font-size="14" font-weight="bold" fill="red" x="0" y="14" textLength="17.103516">??</tspan></text><text transform="translate(404.733 138)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="22.68164">no fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="22.68164" y="11" textLength="166.08398">nite value set without heuristics</tspan></text><text transform="translate(267.233 138)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="13" textLength="29.683594">C = { </tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="29.683594" y="13" textLength="9.267578">∞</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="38.951172" y="13" textLength="7.6992188"> }</tspan></text><text transform="translate(214.675 327.129)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".3239746" y="13" textLength="144.35205">e.g. &quot;Threshold&quot; heuristic</tspan></text><path d="M 327 180.284 L 327 211.409 L 320 211.409 L 334 221.784 L 348 211.409 L 341 211.409 L 341 180.284 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(202 247.12)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="63.86914" y="14" textLength="125.26172">Choice Generators</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="122.41211" y="31" textLength="8.1757812">+</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x=".09326172" y="48" textLength="35.765625">Confi</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="35.858887" y="48" textLength="217.04785">gurable Heuristic Choice Models</tspan></text><text transform="translate(411 238)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="169.42383">JPF internal object to store and </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="144.08203">enumerate a set of choices</tspan></text><text transform="translate(468 283)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="25.347656">confi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="25.347656" y="11" textLength="96.720703">gurable classes to</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="182.09766">create ChoiceGenerator instances</tspan></text><rect x="107" y="225.12" width="140" height="43" fill="white"/><rect x="107" y="225.12" width="140" height="43" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 225.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="98.033203">hasMoreChoices()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="53.361328">advance()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="40" textLength="90.035156">getNextChoice() </tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="90.035156" y="40" textLength="12">→</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="102.035156" y="40" textLength="9.3339844"> x</tspan></text><rect x="107" y="211.12" width="140" height="14" fill="white"/><rect x="107" y="211.12" width="140" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 211.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="73.382812">choiceSet: {x}</tspan></text><rect x="107" y="197.12" width="140" height="14" fill="white"/><rect x="107" y="197.12" width="140" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 197.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="12.9833984" y="11" textLength="104.033203">xChoiceGenerator</tspan></text></g></g></svg>
diff --git a/doc/graphics/cg-ontology.svg b/doc/graphics/cg-ontology.svg
new file mode 100644 (file)
index 0000000..fab55e3
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="131 133 625 388" width="625pt" height="388pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2010-02-22 02:58:46 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation=".48301078"/><feOffset in="blur" result="offset" dx="1" dy="1"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Courier" font-size="13" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id139_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id140_Graphic" filter="url(#Shadow)"/></g><path d="M 284.70481 177.8364 L 284.70481 177.8364 C 294.09105 185.10919 295.80434 198.61399 288.53156 208.00022 C 288.53156 208.00022 288.53156 208.00022 288.53156 208.00022 L 231.091 282.13285 C 223.81822 291.51909 210.31342 293.23238 200.92719 285.9596 L 200.92719 285.9596 C 191.54095 278.68681 189.82766 265.18201 197.10044 255.79578 L 254.541 181.66315 C 261.81378 172.27691 275.31858 170.56362 284.70481 177.8364 C 284.70481 177.8364 284.70481 177.8364 284.70481 177.8364 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 284.70481 177.8364 L 284.70481 177.8364 C 294.09105 185.10919 295.80434 198.61399 288.53156 208.00022 C 288.53156 208.00022 288.53156 208.00022 288.53156 208.00022 L 231.091 282.13285 C 223.81822 291.51909 210.31342 293.23238 200.92719 285.9596 L 200.92719 285.9596 C 191.54095 278.68681 189.82766 265.18201 197.10044 255.79578 L 254.541 181.66315 C 261.81378 172.27691 275.31858 170.56362 284.70481 177.8364 C 284.70481 177.8364 284.70481 177.8364 284.70481 177.8364 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id139_Graphic"><circle cx="269.28125" cy="195.78125" r="18.281279" fill="white"/><circle cx="269.28125" cy="195.78125" r="18.281279" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id140_Graphic"><circle cx="215.5" cy="267.5" r="15.500025" fill="white"/><circle cx="215.5" cy="267.5" r="15.500025" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><line x1="258.01217" y1="210.80888" x2="225.10027" y2="254.69777" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(145.432 221)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".19897461" y="13" textLength="7.940918">T</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="7.6638184" y="13" textLength="49.137207">ransition</tspan></text><rect x="231" y="217" width="75" height="11" fill="blue" fill-opacity=".14"/><rect x="231" y="217" width="75" height="11" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(253.562 187)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".3227539" y="13" textLength="30.354492">State</tspan></text><text transform="translate(312.5 213)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".21044922" y="14" textLength="43.579102">Choice</tspan></text><line x1="267" y1="144" x2="268.45463" y2="177.01808" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 205.51021 279.9999 L 183.932 307 L 183.932 307 L 183.932 307" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="224.25289" y1="280.89727" x2="240" y2="305" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="276.61163" y1="213.07815" x2="294.378" y2="255" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(360.562 248)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="116.74414">Scheduling Choice</tspan></text><text transform="translate(360.562 343)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="77.041016">Data Choice</tspan></text><text transform="translate(405.562 361)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="13" textLength="249.64062">boolean b = Verify.getBoolean();</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="29" textLength="335.45459">double d = Verify.getDouble(&quot;MyHeuristic&quot;);</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="45" textLength="15.602539">..</tspan></text><text transform="translate(405.562 268)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="13" textLength="171.62793">synchronized (..) {..}</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="29" textLength="70.211426">wait (..)</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="45" textLength="140.42285">x = mySharedObject</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="61" textLength="15.602539">..</tspan></text><text transform="translate(360.562 424)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="92.59961">Control Choice</tspan></text><text transform="translate(405.562 448)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="13" textLength="109.217773">if (&lt;cond&gt;) ..</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="29" textLength="210.63428">INVOKECG.setInvocations(..)</tspan><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="0" y="45" textLength="15.602539">..</tspan></text><path d="M 332.571 233.32 L 332.571 433.328 L 355.562 433.328" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="333.347" y1="256.576" x2="355.562" y2="256.576" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="332.571" y1="351.929" x2="354.787" y2="351.929" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/cg-sequence.svg b/doc/graphics/cg-sequence.svg
new file mode 100644 (file)
index 0000000..5e0ff92
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="133 132 503 243" width="503pt" height="243pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2006-05-05 17:02:45 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation=".48301078"/><feOffset in="blur" result="offset" dx="1" dy="1"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="black"><g><path d="M 5.6000004 0 L 0 -2.1000001 L 0 2.1000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="black"><g><path d="M 4.7999992 0 L 0 -1.7999997 L 0 1.7999997 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id4_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id5_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id22_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id7_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id17_Graphic" filter="url(#Shadow)"/></g><path d="M 237.89106 230.97335 L 237.89106 230.97335 C 243.97415 235.39297 245.32265 243.9071 240.90303 249.99018 C 240.90303 249.99018 240.90303 249.99018 240.90303 249.99018 L 213.52887 287.66748 C 209.10925 293.75057 200.59513 295.09907 194.51204 290.67945 L 194.51204 290.67945 C 188.42895 286.25983 187.08045 277.7457 191.50007 271.66262 L 218.87423 233.98532 C 223.29385 227.90223 231.80797 226.55373 237.89106 230.97335 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 237.89106 230.97335 L 237.89106 230.97335 C 243.97415 235.39297 245.32265 243.9071 240.90303 249.99018 C 240.90303 249.99018 240.90303 249.99018 240.90303 249.99018 L 213.52887 287.66748 C 209.10925 293.75057 200.59513 295.09907 194.51204 290.67945 L 194.51204 290.67945 C 188.42895 286.25983 187.08045 277.7457 191.50007 271.66262 L 218.87423 233.98532 C 223.29385 227.90223 231.80797 226.55373 237.89106 230.97335 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 197.99853 196.87463 L 197.99853 196.87463 C 203.38779 192.9742 210.91857 194.18114 214.819 199.5704 C 214.819 199.5704 214.819 199.5704 214.819 199.5704 L 240.80254 235.4721 C 244.70297 240.86136 243.49603 248.39214 238.10677 252.29257 L 238.10677 252.29257 C 232.71751 256.193 225.18673 254.98606 221.2863 249.5968 C 221.2863 249.5968 221.2863 249.5968 221.2863 249.5968 L 195.30276 213.6951 C 191.40233 208.30584 192.60927 200.77506 197.99853 196.87463 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 197.99853 196.87463 L 197.99853 196.87463 C 203.38779 192.9742 210.91857 194.18114 214.819 199.5704 C 214.819 199.5704 214.819 199.5704 214.819 199.5704 L 240.80254 235.4721 C 244.70297 240.86136 243.49603 248.39214 238.10677 252.29257 L 238.10677 252.29257 C 232.71751 256.193 225.18673 254.98606 221.2863 249.5968 C 221.2863 249.5968 221.2863 249.5968 221.2863 249.5968 L 195.30276 213.6951 C 191.40233 208.30584 192.60927 200.77506 197.99853 196.87463 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id4_Graphic"><circle cx="205.5" cy="207.5" r="10.500017" fill="white"/><circle cx="205.5" cy="207.5" r="10.500017" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id5_Graphic"><circle cx="176.5" cy="241.5" r="10.500017" fill="white"/><circle cx="176.5" cy="241.5" r="10.500017" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id22_Graphic"><circle cx="156.5" cy="280.5" r="10.500017" fill="white"/><circle cx="156.5" cy="280.5" r="10.500017" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id7_Graphic"><circle cx="229.5" cy="240.5" r="10.500017" fill="white"/><circle cx="229.5" cy="240.5" r="10.500017" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><line x1="198.36125" y1="215.86957" x2="183.63874" y2="233.13042" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="211.97076" y1="216.39733" x2="223.02918" y2="231.60271" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="171.47923" y1="251.2905" x2="161.52077" y2="270.7095" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id17_Graphic"><circle cx="201.5" cy="280.5" r="10.500017" fill="white"/><circle cx="201.5" cy="280.5" r="10.500017" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><line x1="182.43751" y1="250.76253" x2="195.56247" y2="271.23748" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="223.19091" y1="249.51298" x2="207.80908" y2="271.48702" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="233.83421" y1="250.61316" x2="246" y2="279" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(255 275)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><line x1="197.32751" y1="290.68088" x2="189" y2="311" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="237.95824" y1="247.5337" x2="277" y2="280" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="205.49625" y1="290.75125" x2="213" y2="310" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(195 307)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(330 250)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="29.355469">get_fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="29.355469" y="11" textLength="16.013672">eld</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="74.039062">monitor_enter</tspan></text><text transform="translate(330 197)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="10.001953">...</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="29.355469">get_fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="29.355469" y="25" textLength="16.013672">eld</tspan></text><text transform="translate(330 316)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="74.039062">monitor_enter</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><path d="M 312 246 L 397 246 C 404.73199 246 411 252.26801 411 260 L 411 282 C 411 289.73199 404.73199 296 397 296 L 312 296 C 304.26801 296 298 289.73199 298 282 L 298 260 C 298 252.26801 304.26801 246 312 246 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 312 246 L 397 246 C 404.73199 246 411 252.26801 411 260 L 411 282 C 411 289.73199 404.73199 296 397 296 L 312 296 C 304.26801 296 298 289.73199 298 282 L 298 260 C 298 252.26801 304.26801 246 312 246 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 312 179 L 397 179 C 404.73199 179 411 185.26801 411 193 L 411 215 C 411 222.73199 404.73199 229 397 229 L 312 229 C 304.26801 229 298 222.73199 298 215 L 298 193 C 298 185.26801 304.26801 179 312 179 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 312 179 L 397 179 C 404.73199 179 411 185.26801 411 193 L 411 215 C 411 222.73199 404.73199 229 397 229 L 312 229 C 304.26801 229 298 222.73199 298 215 L 298 193 C 298 185.26801 304.26801 179 312 179 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 312 314 L 397 314 C 404.73199 314 411 320.26801 411 328 L 411 350 C 411 357.73199 404.73199 364 397 364 L 312 364 C 304.26801 364 298 357.73199 298 350 L 298 328 C 298 320.26801 304.26801 314 312 314 Z" fill="#ffccc3" fill-opacity=".25"/><path d="M 312 314 L 397 314 C 404.73199 314 411 320.26801 411 328 L 411 350 C 411 357.73199 404.73199 364 397 364 L 312 364 C 304.26801 364 298 357.73199 298 350 L 298 328 C 298 320.26801 304.26801 314 312 314 Z" stroke="#ff97a0" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(460.266 141)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="106.61328">ChoiceGenerator</tspan></text><path d="M 386 218 L 456 218 L 456 224.5" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 456 246 L 456 255 L 387.7 255" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(301 193.5)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".2241211" y="14" textLength="8.551758">T</tspan></text><text transform="translate(310 201.5)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".27807617" y="10" textLength="2.4438477">j</tspan></text><text transform="translate(301 259.178)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".2241211" y="14" textLength="8.551758">T</tspan></text><text transform="translate(309 267.178)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="14.985352">j+1</tspan></text><text transform="translate(300 327.178)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".2241211" y="14" textLength="8.551758">T</tspan></text><text transform="translate(308 335.178)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="14.985352">j+2</tspan></text><path d="M 406 287 L 455 287 L 455 293.5" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 455 315 L 455 324 L 412.7 324" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(452 228)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="21">CG</tspan></text><text transform="translate(475.15 236)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="5.5">k</tspan></text><text transform="translate(452 297)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="21">CG</tspan></text><text transform="translate(475.15 305)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="2.4438477">l</tspan></text><text transform="translate(431 200)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="58.68164">setNextCG</tspan></text><text transform="translate(431 256)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="78.708984">getNextChoice</tspan></text><text transform="translate(323.5 141)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="8.551758">T</tspan><tspan font-family="Helvetica" font-size="14" font-weight="500" x="8.0390625" y="14" textLength="52.916992">ransition</tspan></text><text transform="translate(198.707 241.273)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(203.818 238.508)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">1</tspan></text><text transform="translate(205.471 248.397)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(225.2 255.48)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(231.407 251.619)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">2</tspan></text><text transform="translate(231.69 262.33)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(248.814 242.537)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(255.295 238.95)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">3</tspan></text><text transform="translate(255.578 249.661)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(223.631 231.98)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49804688" y="11" textLength="8.0039062">S</tspan></text><text transform="translate(231.712 236.938)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(513.826 231.676)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(518.798 228.5)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">1</tspan></text><text transform="translate(520.589 238.389)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(529.688 231.676)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(535.248 228.5)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">2</tspan></text><text transform="translate(536.451 238.389)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(506.378 228.326)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".32910156" y="13" textLength="4.341797">{</tspan></text><text transform="translate(506.378 296.609)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".32910156" y="13" textLength="4.341797">{</tspan></text><text transform="translate(559.536 228.326)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".32910156" y="13" textLength="4.341797">}</tspan></text><text transform="translate(544.246 296.609)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".32910156" y="13" textLength="4.341797">}</tspan></text><text transform="translate(526.246 232.672)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".33300781" y="11" textLength="3.3339844">,</tspan></text><text transform="translate(495.88 228.328)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".20410156" y="13" textLength="7.591797">=</tspan></text><text transform="translate(495.882 296.807)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".20410156" y="13" textLength="7.591797">=</tspan></text><text transform="translate(181.122 141)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="0" y="14" textLength="32.689453">State</tspan></text><path d="M 595.073 343 L 595.073 238.537 L 583.773 238.537" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(569.537 344.5)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="51.351562">backtrack</tspan></text><text transform="translate(570.537 218.5)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="45.36914">advance</tspan></text><text transform="translate(546.227 231.545)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(551.787 228.369)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">2</tspan></text><text transform="translate(552.99 238.258)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="4.5">k</tspan></text><text transform="translate(542.785 232.541)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".33300781" y="11" textLength="3.3339844">,</tspan></text><text transform="translate(513.076 299.926)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="6">c</tspan></text><text transform="translate(518.048 296.75)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="5.005371">1</tspan></text><text transform="translate(519.839 306.639)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="1.9995117">l</tspan></text><text transform="translate(527.938 299.926)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(525.496 300.922)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".33300781" y="11" textLength="3.3339844">,</tspan></text><text transform="translate(417.5 185.5)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="42.029297">execute</tspan></text><text transform="translate(340.5 159)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="63.375">{Instruction}</tspan></text><text transform="translate(195.631 272.98)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49804688" y="11" textLength="8.0039062">S</tspan></text><text transform="translate(204.712 277.938)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="1.9995117">l</tspan></text></g></g></svg>
diff --git a/doc/graphics/choicegen-example.svg b/doc/graphics/choicegen-example.svg
new file mode 100644 (file)
index 0000000..675ec8d
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="36 72 663 436" width="663pt" height="436pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2006-05-03 17:51:17 +0000</dc:date></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 0 M 0 -3 L 8 0 L 0 3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker_2" viewBox="-9 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M -8 0 L 0 0 M 0 3 L -8 0 L 0 -3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Lucida Grande" font-size="12" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Lucida Grande" font-size="14" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><line x1="401.4" y1="354" x2="449.6" y2="354" marker-end="url(#StickArrow_Marker)" marker-start="url(#StickArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="425.5" y1="331" x2="425.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="440.5" y1="338" x2="440.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="410.5" y1="338" x2="410.5" y2="364" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(412 316)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".38085938" y="12" textLength="7.330078">T</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.5" y="12" textLength="9.919922"> ±</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="17.419922" y="12" textLength="12.199219"> Δ</tspan></text><circle cx="425.5" cy="354" r="3.0000048" fill="white"/><circle cx="425.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="440.5" cy="354" r="3.0000048" fill="white"/><circle cx="440.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="410.5" cy="354" r="3.0000048" fill="white"/><circle cx="410.5" cy="354" r="3.0000048" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(250.5215 365.55046)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="21.603516">.. </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="295.24805">double v = Verify.getDouble(&quot;velocity&quot;); </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="14.402344">..</tspan></text><text transform="translate(220.01942 434.54995)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="230.4375">velocity.class = DoubleThreshold</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="187.23047">velocity.threshold = 13250</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="144.02344">velocity.delta = 500</tspan></text><text transform="translate(132.175 372.129)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".020996094" y="13" textLength="93.95801">application code</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="14.13501" y="29" textLength="65.72998">(test driver)</tspan></text><text transform="translate(50 436)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="32.280518" y="13" textLength="27.459961">confi</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="59.74048" y="13" textLength="50.59082">guration </tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".49780273" y="29" textLength="123.55713">(e.g. mode property fi</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="124.05493" y="29" textLength="14.447266">le)</tspan></text><path d="M 474.5 396 C 475.1666 401.49945 481.8328 409.25033 476.5 412.5 C 471.1672 415.74967 475.91332 414.91673 442.5 415.5 C 409.08668 416.08327 308.41342 414.83345 276 416 C 243.58658 417.16655 250.58307 418.91703 248 422.5 C 246.49657 424.5854 249.56637 427.63104 253.21663 430.79843" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(476.5 345.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".32714844" y="12" textLength="29.683594">C = { </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="29.799805" y="12" textLength="7.330078">T</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="36.473633" y="12" textLength="3.9960938">-</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="40.469727" y="12" textLength="68.203125">Δ, T, T+ Δ }</tspan></text><text transform="translate(77.73514 82.637407)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="136.822266">Verify.getBoolean()</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="129.62109">Verify.getInt(0,4)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="67" textLength="180.0293">Verify.getDouble(1.0,1.5)</tspan></text><text transform="translate(267.733 84)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="12" textLength="82.376953">C = { true, false</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="82.376953" y="12" textLength="7.6992188"> }</tspan></text><text transform="translate(267.233 111)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="12" textLength="89.72461">C = { 0, 1, 2, 3, 4</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="89.72461" y="12" textLength="7.6992188"> }</tspan></text><text transform="translate(373.733 83.6349)" fill="black"><tspan font-family="Lucida Grande" font-size="14" font-weight="500" x="0" y="14" textLength="10.698242">✓</tspan></text><text transform="translate(374.733 108.635)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="0" y="14" textLength="8.551758">?</tspan></text><text transform="translate(404.733 111)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="279.48633">potentially large sets with lots of uninteresting values</tspan></text><text transform="translate(374.733 135.635)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="0" y="14" textLength="17.103516">??</tspan></text><text transform="translate(404.733 138)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="22.68164">no fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="22.68164" y="11" textLength="166.08398">nite value set without heuristics</tspan></text><text transform="translate(267.233 138)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="13" textLength="29.683594">C = { </tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="29.683594" y="13" textLength="9.267578">∞</tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="38.951172" y="13" textLength="7.6992188"> }</tspan></text><text transform="translate(200.175 327.129)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".0021972656" y="13" textLength="173.99561">e.g. &quot;DoubleThresholdChoice&quot;</tspan></text><path d="M 327 180.284 L 327 211.409 L 320 211.409 L 334 221.784 L 348 211.409 L 341 211.409 L 341 180.284 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(202 247.12)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="63.86914" y="14" textLength="125.26172">Choice Generators</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="122.41211" y="31" textLength="8.1757812">+</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x=".09326172" y="48" textLength="35.765625">Confi</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="35.858887" y="48" textLength="217.04785">gurable Heuristic Choice Models</tspan></text><text transform="translate(411 238)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="169.42383">JPF internal object to store and </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="144.08203">enumerate a set of choices</tspan></text><text transform="translate(468 283)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="25.347656">confi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="25.347656" y="11" textLength="96.720703">gurable classes to</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="182.09766">create ChoiceGenerator instances</tspan></text><rect x="107" y="225.12" width="140" height="43" fill="white"/><rect x="107" y="225.12" width="140" height="43" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 225.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="98.033203">hasMoreChoices()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="53.361328">advance()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="40" textLength="90.035156">getNextChoice() </tspan><tspan font-family="Lucida Grande" font-size="12" font-weight="500" x="90.035156" y="40" textLength="12">→</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="102.035156" y="40" textLength="9.3339844"> x</tspan></text><rect x="107" y="211.12" width="140" height="14" fill="white"/><rect x="107" y="211.12" width="140" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 211.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="73.382812">choiceSet: {x}</tspan></text><rect x="107" y="197.12" width="140" height="14" fill="white"/><rect x="107" y="197.12" width="140" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(112 197.12)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="12.9833984" y="11" textLength="104.033203">xChoiceGenerator</tspan></text></g></g></svg>
diff --git a/doc/graphics/genpeer.svg b/doc/graphics/genpeer.svg
new file mode 100644 (file)
index 0000000..6887428
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="65 35 468 408" width="39pc" height="34pc" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-04 01:29:55 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Monaco" font-size="12" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="576" height="734"/><g><title>Layer 1</title><g><xl:use xl:href="#id1_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id6_Graphic" filter="url(#Shadow)"/></g><g id="id1_Graphic"><path d="M 266.75 164 L 266.75 231.5 L 262 231.5 L 271.5 254 L 281 231.5 L 276.25 231.5 L 276.25 164 Z" fill="white"/><path d="M 266.75 164 L 266.75 231.5 L 262 231.5 L 271.5 254 L 281 231.5 L 276.25 231.5 L 276.25 164 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id6_Graphic"><ellipse cx="271.5" cy="202.5" rx="46.500074" ry="19.500031" fill="white"/><ellipse cx="271.5" cy="202.5" rx="46.500074" ry="19.500031" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(239.3 194)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="4.1829102" y="14" textLength="56.03418">GenPeer</tspan></text></g><text transform="translate(133 44.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="100.816406">package x.y.z;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="108.01758">class MyClass {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="36.00586">  ...</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="273.64453">  native String foo (int i, String s);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="7.201172">}</tspan></text><text transform="translate(79 257.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="180.0293">class JPF_x_y_z_MyClass {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="36.00586">  ...</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="43.20703">  @MJI</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="108.01758">  public static</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="439.27148">      int foo__ILjava_lang_String__2 (MJIEnv env, int objRef,</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="92" textLength="403.26562">                                      int i, int sRef) {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="108" textLength="187.23047">    int ref = MJIEnv.NULL;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="124" textLength="180.0293">    // &lt;2do&gt; fill in body</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="140" textLength="108.01758">    return ref;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="156" textLength="21.603516">  }</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="172" textLength="7.201172">}</tspan></text><text transform="translate(93.5 147.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x=".21777344" y="11" textLength="48.955078">&quot;java gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="48.288086" y="11" textLength="115.39453">.nasa.jpf.GenPeer x.y</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="162.79785" y="11" textLength="213.98438">.z.MyClass &gt; JPF_x_y_z_MyClass.java&quot;</tspan></text></g></g></svg>
diff --git a/doc/graphics/interleavings.svg b/doc/graphics/interleavings.svg
new file mode 100644 (file)
index 0000000..4dfa19e
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="7 283 477 166" width="477pt" height="166pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-11-19 20:18:08 +0000</dc:date></metadata><defs><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Hoefler Text" font-size="14" panose-1="2 3 6 2 5 5 6 2 2 3" units-per-em="1000" underline-position="0" underline-thickness="67.471858" slope="0" x-height="425" cap-height="689.5" ascent="720.99304" descent="-279.00696" font-weight="500"><font-face-src><font-face-name name="HoeflerText-Regular"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="8" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><rect x="116" y="335.875" width="9" height="17" fill="#ceccff"/><rect x="116" y="335.875" width="9" height="17" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="116" y="355.875" width="9" height="21.5" fill="#ceccff"/><rect x="116" y="355.875" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="116" y="388.375" width="9" height="33" fill="#ceccff"/><rect x="116" y="388.375" width="9" height="33" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="148.5" y="335.875" width="9" height="21.5" fill="#ffd1d7"/><rect x="148.5" y="335.875" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="148.5" y="360.375" width="9" height="21.5" fill="#ffd1d7"/><rect x="148.5" y="360.375" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="148.5" y="393.875" width="9" height="21.5" fill="#ffd1d7"/><rect x="148.5" y="393.875" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="184" y="336.375" width="9" height="26.5" fill="#d6ffc8"/><rect x="184" y="336.375" width="9" height="26.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="184" y="365.875" width="9" height="14" fill="#d6ffc8"/><rect x="184" y="365.875" width="9" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="184" y="394.375" width="9" height="21.5" fill="#d6ffc8"/><rect x="184" y="394.375" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(183.5 376)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(148 376.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(115.5 372.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(101 337)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".9411621" y="10" textLength="6.1176758">1</tspan></text><text transform="translate(100.5 361)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".9411621" y="10" textLength="6.1176758">2</tspan></text><text transform="translate(115.5 309.5)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".33105469" y="14" textLength="9.3378906">P</tspan></text><text transform="translate(119.5 318)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="1.49731445" y="9" textLength="5.005371">1</tspan></text><text transform="translate(148.5 309.875)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".33105469" y="14" textLength="9.3378906">P</tspan></text><text transform="translate(152.5 318.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="1.49731445" y="9" textLength="5.005371">2</tspan></text><text transform="translate(183.5 309.875)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".33105469" y="14" textLength="9.3378906">P</tspan></text><text transform="translate(187.5 318.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x=".75024414" y="9" textLength="6.4995117">N</tspan></text><text transform="translate(101.5 394.375)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".4411621" y="10" textLength="6.1176758">n</tspan></text><text transform="translate(106.5 400.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="1.49731445" y="9" textLength="5.005371">1</tspan></text><text transform="translate(135.5 397.375)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".4411621" y="10" textLength="6.1176758">n</tspan></text><text transform="translate(140.5 403.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="1.49731445" y="9" textLength="5.005371">2</tspan></text><rect x="254" y="355.875" width="9" height="21.5" fill="#ffd1d7"/><rect x="254" y="355.875" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="254" y="380.375" width="9" height="26.5" fill="#d6ffc8"/><rect x="254" y="380.375" width="9" height="26.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(253.5 402)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(21.5 367.375)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="14.499" y="13" textLength="10.276">A</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="24.075" y="13" textLength="5.166">t</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="29.031" y="13" textLength="29.47">omic</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x=".121" y="30" textLength="6.188">I</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="5.735" y="30" textLength="23.394">nstr</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="29.479" y="30" textLength="43.4">uctions</tspan></text><text transform="translate(128 292.5)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x=".175" y="13" textLength="48.65">Threads</tspan></text><rect x="254.5" y="416.375" width="9" height="21.5" fill="#d6ffc8"/><rect x="254.5" y="416.375" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(240 339.5)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".9411621" y="10" textLength="6.1176758">1</tspan></text><text transform="translate(239.5 359)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".9411621" y="10" textLength="6.1176758">2</tspan></text><text transform="translate(257 309.875)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".05517578" y="14" textLength="3.8896484">I</tspan></text><text transform="translate(260 317.5)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="1.49731445" y="9" textLength="5.005371">1</tspan></text><text transform="translate(230.5 418.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".38574219" y="11" textLength="15.228516">∑n</tspan></text><text transform="translate(244.5 426.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="3.0002441" y="9" textLength="1.9995117">i</tspan></text><text transform="translate(296.5 309.875)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x=".05517578" y="14" textLength="3.8896484">I</tspan></text><text transform="translate(299.5 317.5)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x=".25146484" y="9" textLength="7.4970703">M</tspan></text><text transform="translate(276.5 310.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(166.5 310.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(170.5 397.375)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".4411621" y="10" textLength="6.1176758">n</tspan></text><text transform="translate(175.5 403.375)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x=".75024414" y="9" textLength="6.4995117">N</tspan></text><rect x="297.5" y="336.375" width="9" height="26.5" fill="#d6ffc8"/><rect x="297.5" y="336.375" width="9" height="26.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="297.5" y="365.375" width="9" height="17" fill="#ceccff"/><rect x="297.5" y="365.375" width="9" height="17" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="254" y="335.875" width="9" height="17" fill="#ceccff"/><rect x="254" y="335.875" width="9" height="17" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="297.5" y="384.875" width="9" height="21.5" fill="#ffd1d7"/><rect x="297.5" y="384.875" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(297 402)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><rect x="298" y="416.375" width="9" height="21.5" fill="#ffd1d7"/><rect x="298" y="416.375" width="9" height="21.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(276.5 364.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(166.5 355.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".49902344" y="11" textLength="10.001953">...</tspan></text><text transform="translate(239.5 292.5)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x=".224" y="13" textLength="6.188">I</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="5.838" y="13" textLength="12.838">nt</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="18.466" y="13" textLength="28.182">erlea</tspan><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="46.858" y="13" textLength="29.918">vings</tspan></text><path d="M 210.5 369.125 L 224 369.125 L 224 366.375 L 228.5 371.875 L 224 377.375 L 224 374.625 L 210.5 374.625 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(394 355.375)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="0" y="13" textLength="46.102">(∑    n )!</tspan></text><text transform="translate(397 382.375)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="0" y="13" textLength="44.674">∏   (n !)</tspan></text><text transform="translate(408 351.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="1.1113281" y="8" textLength="5.7773438">N</tspan></text><text transform="translate(406 365.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="3.0507812" y="8" textLength="10.8984375">i=1</tspan></text><text transform="translate(426 365.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="3.1113281" y="8" textLength="1.7773438">i</tspan></text><text transform="translate(408 379.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="1.1113281" y="8" textLength="5.7773438">N</tspan></text><text transform="translate(406 392.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="3.0507812" y="8" textLength="10.8984375">i=1</tspan></text><text transform="translate(428 392.375)" fill="black"><tspan font-family="Helvetica" font-size="8" font-weight="500" x="3.1113281" y="8" textLength="1.7773438">i</tspan></text><line x1="380.5" y1="378.375" x2="457" y2="378.375" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(345 368.5)" fill="black"><tspan font-family="Hoefler Text" font-size="14" font-weight="500" x="8.394" y="13" textLength="26.712">M = </tspan></text><rect x="342.5" y="346.375" width="130" height="61.5" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/jpf-abstractions.svg b/doc/graphics/jpf-abstractions.svg
new file mode 100644 (file)
index 0000000..b636c6f
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="5 32 709 537" width="709pt" height="537pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-11-26 21:24:31 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker_2" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="gray"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Monaco" font-size="10" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="blue"><g><path d="M 6.3999996 0 L 0 0 M 0 -2.3999999 L 6.3999996 0 L 0 2.3999999" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Ball_Marker" viewBox="-6 -4 7 8" markerWidth="7" markerHeight="8" color="blue"><g><circle cx="-2.3999988" cy="0" r="2.3999977" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><rect x="67.3713" y="354" width="232.664" height="103.055" fill="#dee4ff"/><rect x="67.3713" y="354" width="232.664" height="103.055" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="251.917" y="60.5674" width="137.5" height="101.779" fill="#fff9ab"/><rect x="251.917" y="60.5674" width="137.5" height="101.779" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="45" y="191.359" width="261" height="127.641" fill="#aebcff"/><rect x="45" y="191.359" width="261" height="127.641" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="336" y="193" width="367" height="236.266" fill="#a7ffb4"/><rect x="336" y="193" width="367" height="236.266" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="126.92008" y="233.5" width="105.75" height="14" fill="white"/><rect x="126.92008" y="233.5" width="105.75" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(131.92008 233.5)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="47.34375">search ()</tspan></text></g><g filter="url(#Shadow)"><rect x="126.92008" y="219.5" width="105.75" height="14" fill="white"/><rect x="126.92008" y="219.5" width="105.75" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(131.92008 219.5)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="37.330078">VM vm</tspan></text></g><g filter="url(#Shadow)"><rect x="126.92008" y="205.5" width="105.75" height="14" fill="white"/><rect x="126.92008" y="205.5" width="105.75" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(131.92008 205.5)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="red" x="27.862305" y="11" textLength="40.02539">Search</tspan></text></g><line x1="179.49" y1="268.089" x2="179.59506" y2="260.99852" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="353.25" y="219.5" width="105.75" height="42" fill="white"/><rect x="353.25" y="219.5" width="105.75" height="42" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(358.25 219.5)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="51.339844">forward ()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="25" textLength="62.677734">backtrack ()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="39" textLength="76.69336">restoreState ()</tspan></text></g><g filter="url(#Shadow)"><rect x="353.25" y="205.5" width="105.75" height="14" fill="white"/><rect x="353.25" y="205.5" width="105.75" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(358.25 205.5)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="red" x="38.875" y="11" textLength="18">VM</tspan></text></g><g filter="url(#Shadow)"><rect x="60.625" y="289" width="105.75" height="14" fill="white"/><rect x="60.625" y="289" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(65.625 289)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="47.34375">search ()</tspan></text></g><g filter="url(#Shadow)"><rect x="60.625" y="275" width="105.75" height="14" fill="white"/><rect x="60.625" y="275" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(65.625 275)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="19.864258" y="11" textLength="56.021484">DFSearch</tspan></text></g><g filter="url(#Shadow)"><rect x="263.917" y="105.328" width="114" height="14" fill="white"/><rect x="263.917" y="105.328" width="114" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(268.917 105.328)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="28.669922">run ()</tspan></text></g><g filter="url(#Shadow)"><rect x="263.917" y="91.3279" width="114" height="14" fill="white"/><rect x="263.917" y="91.3279" width="114" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(268.917 91.3279)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="90.697266">search, vm, confi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="90.697266" y="11" textLength="6.673828">g</tspan></text></g><g filter="url(#Shadow)"><rect x="263.917" y="77.3279" width="114" height="14" fill="white"/><rect x="263.917" y="77.3279" width="114" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(268.917 77.3279)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="red" x="40.996094" y="11" textLength="22.007812">JPF</tspan></text></g><path d="M 263.917 113.349 L 31.6528 111.887 L 31 284 L 43.1 284" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 377.917 111.887 L 483.589 111.925 L 483.312 205.5 L 468.9 205.5" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><line x1="232.67008" y1="226.5" x2="343.35009" y2="226.04105" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="371.194" y="340.3" width="122" height="28" fill="white"/><rect x="371.194" y="340.3" width="122" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(376.194 340.3)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="49.359375">executeT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="48.919922" y="11" textLength="56.683594">ransition ()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="25" textLength="108.714844">executeInstruction ()</tspan></text></g><g filter="url(#Shadow)"><rect x="371.194" y="326.3" width="122" height="14" fill="white"/><rect x="371.194" y="326.3" width="122" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(376.194 326.3)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="25.000977" y="11" textLength="61.998047">ThreadInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="341.325" y="292.463" width="145" height="28" fill="white"/><rect x="341.325" y="292.463" width="145" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(346.325 292.463)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="74.689453">initializeNextT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="74.25" y="11" textLength="53.34961">ransition()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="25" textLength="74.033203">executeNextT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="73.59375" y="25" textLength="53.34961">ransition()</tspan></text></g><g filter="url(#Shadow)"><rect x="341.325" y="278.463" width="145" height="14" fill="white"/><rect x="341.325" y="278.463" width="145" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(346.325 278.463)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="31.482422" y="11" textLength="72.035156">SystemState</tspan></text></g><g filter="url(#Shadow)"><rect x="591.25" y="232.411" width="82.375" height="20.589" fill="white"/><rect x="591.25" y="232.411" width="82.375" height="20.589" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(596.25 235.7055)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="9.1816406" y="11" textLength="54.01172">ClassInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="605.438" y="258.411" width="82.375" height="20.589" fill="white"/><rect x="605.438" y="258.411" width="82.375" height="20.589" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(610.438 261.7055)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="3.8642578" y="11" textLength="64.646484">MethodInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="606.25" y="284.411" width="82.375" height="20.589" fill="white"/><rect x="606.25" y="284.411" width="82.375" height="20.589" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(611.25 287.7055)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="11.191406" y="11" textLength="49.992188">FieldInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="500.625" y="279.161" width="82.375" height="20.589" fill="white"/><rect x="500.625" y="279.161" width="82.375" height="20.589" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(505.625 282.4555)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="1.8515625" y="11" textLength="68.671875">ElementInfo</tspan></text></g><g filter="url(#Shadow)"><rect x="515.594" y="305.161" width="67.0625" height="20.589" fill="white"/><rect x="515.594" y="305.161" width="67.0625" height="20.589" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(520.594 308.4555)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="11.1933594" y="11" textLength="34.675781">Fields</tspan></text></g><g filter="url(#Shadow)"><rect x="515.938" y="330.161" width="67.0625" height="20.589" fill="white"/><rect x="515.938" y="330.161" width="67.0625" height="20.589" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(520.938 333.4555)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="6.538086" y="11" textLength="43.986328">Monitor</tspan></text></g><g filter="url(#Shadow)"><rect x="485.938" y="251.456" width="82.375" height="20.589" fill="white"/><rect x="485.938" y="251.456" width="82.375" height="20.589" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(490.938 254.7505)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="16.511719" y="11" textLength="39.351562">Statics</tspan></text></g><text transform="translate(374.731 408.79)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".12988281" y="11" textLength="106.740234">bytecode  execution</tspan></text><text transform="translate(611.5 312.411)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".31054688" y="11" textLength="62.378906">type + code</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="18.161133" y="25" textLength="26.677734">mgnt</tspan></text><text transform="translate(536.5 357.572)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".4892578" y="11" textLength="32.021484">object</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".158203125" y="25" textLength="32.683594">model</tspan></text><rect x="251.917" y="43.7905" width="78" height="17.1216" fill="#fff9ab"/><rect x="251.917" y="43.7905" width="78" height="17.1216" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(256.917 45.3513)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.0869141" y="11" textLength="19.347656">gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="20.549805" y="11" textLength="45.36328">.nasa.jpf</tspan></text><rect x="45" y="173" width="114" height="18" fill="#aebcff"/><rect x="45" y="173" width="114" height="18" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(50 175)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".41113281" y="11" textLength="19.347656">gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="18.874023" y="11" textLength="84.714844">.nasa.jpf.search</tspan></text><g filter="url(#Shadow)"><rect x="183.625" y="377.236" width="105.75" height="14" fill="white"/><rect x="183.625" y="377.236" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(188.625 377.236)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="47.34375">search ()</tspan></text></g><g filter="url(#Shadow)"><rect x="183.625" y="363.236" width="105.75" height="14" fill="white"/><rect x="183.625" y="363.236" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(188.625 363.236)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="2.1865234" y="11" textLength="91.376953">HeuristicSearch</tspan></text></g><path d="M 113.5 275 L 113.5 268 L 236.5 268 L 236.5 356.236 L 236.5 363.236" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="336" y="175" width="97.375" height="18" fill="#a7ffb4"/><rect x="336" y="175" width="97.375" height="18" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(341 177)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.109375" y="11" textLength="19.347656">gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="20.572266" y="11" textLength="64.69336">.nasa.jpf.vm</tspan></text><rect x="67.3713" y="336" width="162" height="18" fill="#dee4ff"/><rect x="67.3713" y="336" width="162" height="18" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(72.3713 338)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".40234375" y="11" textLength="19.347656">gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="18.865234" y="11" textLength="132.73242">.nasa.jpf.search.heuristic</tspan></text><line x1="236.275" y1="419.43" x2="236.39227" y2="404.73557" marker-end="url(#UMLInheritance_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="396" y="467.057" width="166.625" height="90" fill="#deffe8"/><rect x="396" y="467.057" width="166.625" height="90" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="396" y="449.057" width="162" height="18" fill="#deffe8"/><rect x="396" y="449.057" width="162" height="18" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(401 451.057)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="8.4033203" y="11" textLength="19.347656">gov</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="26.866211" y="11" textLength="116.73047">.nasa.jpf.vm.bytecode</tspan></text><g filter="url(#Shadow)"><rect x="416.125" y="485.057" width="122" height="14" fill="white"/><rect x="416.125" y="485.057" width="122" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(421.125 485.057)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="53.35547">execute ()</tspan></text></g><g filter="url(#Shadow)"><rect x="416.125" y="471.057" width="122" height="14" fill="white"/><rect x="416.125" y="471.057" width="122" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(421.125 471.057)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="25.000977" y="11" textLength="61.998047">Instruction</tspan></text></g><line x1="476.365" y1="520.646" x2="476.3219" y2="512.5568" marker-end="url(#UMLInheritance_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="459.25" y="525.351" width="43" height="18" fill="white"/><rect x="459.25" y="525.351" width="43" height="18" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(464.25 527.351)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="11.4990234" y="11" textLength="10.001953">...</tspan></text></g><rect x="455.25" y="521.351" width="43" height="18" fill="white"/><rect x="455.25" y="521.351" width="43" height="18" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(460.25 523.351)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="11.4990234" y="11" textLength="10.001953">...</tspan></text><path d="M 371.194 347.3 L 353.25 347.55 L 353.25 485.057 L 406.225 485.057" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="251.917" y="274.824" width="43" height="18" fill="white"/><rect x="251.917" y="274.824" width="43" height="18" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(256.917 276.824)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="11.4990234" y="11" textLength="10.001953">...</tspan></text></g><path d="M 235.68224 268 L 272.978 268 L 273.417 274.824" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow)"><rect x="183.4" y="433.43" width="105.75" height="14" fill="white"/><rect x="183.4" y="433.43" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(188.4 433.43)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="47.34375">search ()</tspan></text></g><g filter="url(#Shadow)"><rect x="183.4" y="419.43" width="105.75" height="14" fill="white"/><rect x="183.4" y="419.43" width="105.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(188.4 419.43)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="10.199219" y="11" textLength="75.351562">BFSHeuristic</tspan></text></g><g filter="url(#Shadow)"><rect x="108" y="419.253" width="43" height="18" fill="white"/><rect x="108" y="419.253" width="43" height="18" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(113 421.253)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="11.4990234" y="11" textLength="10.001953">...</tspan></text></g><path d="M 235.682 410.984 L 129.425 410.984 L 129.5 419.253" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(31 471.057)" fill="blue"><tspan font-family="Monaco" font-size="10" font-weight="500" fill="blue" x="0" y="10" textLength="102.0166">while (notDone) {</tspan><tspan font-family="Monaco" font-size="10" font-weight="500" fill="blue" x="0" y="24" textLength="96.015625"> ..vm.forward();</tspan><tspan font-family="Monaco" font-size="10" font-weight="500" fill="blue" x="0" y="38" textLength="108.01758"> ..vm.backtrack();</tspan><tspan font-family="Monaco" font-size="10" font-weight="500" fill="blue" x="0" y="52" textLength="156.02539"> if (!properties.check()){</tspan><tspan font-family="Monaco" font-size="10" font-weight="500" fill="blue" x="0" y="66" textLength="168.02734">   reportError(); break; ...</tspan></text><path d="M 48.938554 304.05756 C 43.085163 312.15881 30.529805 331.16365 22.8773 354.647 C 12.6393 386.0645 17.02699 410.2525 22.8773 427.469 C 26.786267 438.97245 34.286499 452.55632 39.54308 460.30485" marker-end="url(#StickArrow_Marker)" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(142.526 127.115)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="0" y="13" textLength="90.67627">search.class=...</tspan></text><text transform="translate(405.865 127)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="0" y="13" textLength="68.98633">vm.class=...</tspan></text><g filter="url(#Shadow)"><rect x="396.381" y="390.67" width="105.75" height="14" fill="white"/><rect x="396.381" y="390.67" width="105.75" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(401.381 390.67)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="gray" x="0" y="11" textLength="85.365234">push (), pop() ...</tspan></text></g><g filter="url(#Shadow)"><rect x="396.381" y="376.67" width="105.75" height="14" fill="white"/><rect x="396.381" y="376.67" width="105.75" height="14" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(401.381 377.67)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="13.855469" y="11" textLength="68.039062">StackFrame</tspan></text></g><g filter="url(#Shadow)"><rect x="292.164" y="131.276" width="82.375" height="20.589" fill="white"/><rect x="292.164" y="131.276" width="82.375" height="20.589" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(297.164 134.5705)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="17.194336" y="11" textLength="30.65625">Confi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="47.850586" y="11" textLength="7.330078">g</tspan></text></g><g filter="url(#Shadow)"><rect x="488.713" y="222.411" width="67.0625" height="20.589" fill="white"/><rect x="488.713" y="222.411" width="67.0625" height="20.589" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(493.713 225.7055)" fill="gray"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="gray" x="13.859375" y="11" textLength="29.34375">Heap</tspan></text></g></g></g></svg>
diff --git a/doc/graphics/jpf-basic.svg b/doc/graphics/jpf-basic.svg
new file mode 100644 (file)
index 0000000..fdfae11
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="162 106 384 214" width="32pc" height="214pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-11-19 18:37:23 +0000</dc:date></metadata><defs><font-face font-family="Helvetica" font-size="18" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-857.14286" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><rect x="299.965" y="117" width="99.4872" height="56.0076" fill="#fffab4"/><rect x="299.965" y="117" width="99.4872" height="56.0076" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(304.965 134.0038)" fill="black"><tspan font-family="Helvetica" font-size="18" font-weight="bold" x="28.23774" y="18" textLength="33.011719">JPF</tspan></text><path d="M 206.06 166.232 L 206.06 123.032 C 206.06 120.0512 218.156 117.632 233.06 117.632 C 247.964 117.632 260.06 120.0512 260.06 123.032 L 260.06 166.232 C 260.06 169.2128 247.964 171.632 233.06 171.632 C 218.156 171.632 206.06 169.2128 206.06 166.232" fill="#dfdbff"/><path d="M 206.06 166.232 L 206.06 123.032 C 206.06 120.0512 218.156 117.632 233.06 117.632 C 247.964 117.632 260.06 120.0512 260.06 123.032 L 260.06 166.232 C 260.06 169.2128 247.964 171.632 233.06 171.632 C 218.156 171.632 206.06 169.2128 206.06 166.232 M 206.06 123.032 C 206.06 126.0128 218.156 128.432 233.06 128.432 C 247.964 128.432 260.06 126.0128 260.06 123.032" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(211.06 139.332)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="2.8554688" y="13" textLength="38.289062">*.class</tspan></text><path d="M 438.51 119.263 L 510.51 119.263 L 510.51 162.463 C 488.91 157.063 460.11 178.663 438.51 167.863 Z" fill="#ffcac7"/><path d="M 438.51 119.263 L 510.51 119.263 L 510.51 162.463 C 488.91 157.063 460.11 178.663 438.51 167.863 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(443.51 135.563)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="14.0200195" y="13" textLength="33.95996">report</tspan></text><path d="M 322.497 264.402 L 322.497 221.202 C 322.497 218.2212 334.593 215.802 349.497 215.802 C 364.401 215.802 376.497 218.2212 376.497 221.202 L 376.497 264.402 C 376.497 267.3828 364.401 269.802 349.497 269.802 C 334.593 269.802 322.497 267.3828 322.497 264.402" fill="#dfdbff"/><path d="M 322.497 264.402 L 322.497 221.202 C 322.497 218.2212 334.593 215.802 349.497 215.802 C 364.401 215.802 376.497 218.2212 376.497 221.202 L 376.497 264.402 C 376.497 267.3828 364.401 269.802 349.497 269.802 C 334.593 269.802 322.497 267.3828 322.497 264.402 M 322.497 221.202 C 322.497 224.1828 334.593 226.602 349.497 226.602 C 364.401 226.602 376.497 224.1828 376.497 221.202" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(327.497 237.502)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x="10.7995605" y="13" textLength="22.400879">*.jpf</tspan></text><text transform="translate(176 185.642)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".154296875" y="14" textLength="90.26172">System under </tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="90.16992" y="14" textLength="8.551758">T</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="97.16992" y="14" textLength="18.675781">est</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="8.1967773" y="31" textLength="99.606445">(Java bytecode)</tspan></text><text transform="translate(418.768 185.485)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".46923828" y="14" textLength="9.3378906">V</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="9.041504" y="14" textLength="22.558594">erifi</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="31.600098" y="14" textLength="80.930664">cation Result</tspan></text><text transform="translate(290.911 276.286)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="3.4726562" y="14" textLength="61.461914">JPF Confi</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="64.93457" y="14" textLength="50.592773">guration</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".35888672" y="31" textLength="92.59961">Properties to V</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="92.19287" y="31" textLength="26.448242">erify</tspan></text><path d="M 266.46 140.155075 L 288.70305 140.155075 L 288.70305 134.628 L 296.1174 145.68215 L 288.70305 156.7363 L 288.70305 151.209225 L 266.46 151.209225 Z" fill="white"/><path d="M 266.46 140.155075 L 288.70305 140.155075 L 288.70305 134.628 L 296.1174 145.68215 L 288.70305 156.7363 L 288.70305 151.209225 L 266.46 151.209225 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 404.611 140.078075 L 426.85405 140.078075 L 426.85405 134.551 L 434.2684 145.60515 L 426.85405 156.6593 L 426.85405 151.132225 L 404.611 151.132225 Z" fill="white"/><path d="M 404.611 140.078075 L 426.85405 140.078075 L 426.85405 134.551 L 434.2684 145.60515 L 426.85405 156.6593 L 426.85405 151.132225 L 404.611 151.132225 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 344.23062 209.36985 L 344.23062 187.1268 L 338.70355 187.1268 L 349.7577 179.71245 L 360.81185 187.1268 L 355.28478 187.1268 L 355.28478 209.36985 Z" fill="white"/><path d="M 344.23062 209.36985 L 344.23062 187.1268 L 338.70355 187.1268 L 349.7577 179.71245 L 360.81185 187.1268 L 355.28478 187.1268 L 355.28478 209.36985 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/jpf-intro-new.svg b/doc/graphics/jpf-intro-new.svg
new file mode 100644 (file)
index 0000000..56fc211
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="3 18 771 561" width="771pt" height="561pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2009-04-10 01:07:23 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="blue"><g><path d="M 5.6000004 0 L 0 -2.1000001 L 0 2.1000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-7 -4 8 8" markerWidth="8" markerHeight="8" color="blue"><g><path d="M -5.6000004 0 L 0 2.1000001 L 0 -2.1000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><filter id="Shadow_2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-8 -4 9 8" markerWidth="9" markerHeight="8" color="black"><g><path d="M -6.3999996 0 L 0 2.3999999 L 0 -2.3999999 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-923.0769" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="15" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_4" viewBox="-1 -3 6 6" markerWidth="6" markerHeight="6" color="blue"><g><path d="M 4 0 L 0 -1.5 L 0 1.5 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_5" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff8987"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_6" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id267_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id10_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id312_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id320_Graphic" filter="url(#Shadow)"/></g><line x1="437.4379" y1="41.027123" x2="519.2401" y2="40.616177" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="145.336" y1="40.4099" x2="424.873" y2="40.4099" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 135.678 89.1207 L 493.624 89.1207 C 500.25142 89.1207 505.624 94.493283 505.624 101.1207 L 505.624 477.0257 C 505.624 483.65312 500.25142 489.0257 493.624 489.0257 L 135.678 489.0257 C 129.05058 489.0257 123.678 483.65312 123.678 477.0257 L 123.678 101.1207 C 123.678 94.493283 129.05058 89.1207 135.678 89.1207 Z" fill="white"/><path d="M 135.678 89.1207 L 493.624 89.1207 C 500.25142 89.1207 505.624 94.493283 505.624 101.1207 L 505.624 477.0257 C 505.624 483.65312 500.25142 489.0257 493.624 489.0257 L 135.678 489.0257 C 129.05058 489.0257 123.678 483.65312 123.678 477.0257 L 123.678 101.1207 C 123.678 94.493283 129.05058 89.1207 135.678 89.1207 Z" stroke="#999" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 530.289 141.229 L 734.049 141.229 L 734.049 281.1298 C 672.921 263.6422 591.417 333.5926 530.289 298.6174 Z" fill="white"/><path d="M 530.289 141.229 L 734.049 141.229 L 734.049 281.1298 C 672.921 263.6422 591.417 333.5926 530.289 298.6174 Z" stroke="silver" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(538.789 143.229)" fill="#7e7e7e"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="9" textLength="149.41406">------------------------------------ error path</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="20" textLength="5.0009766">..</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="31" textLength="38.53125">Step #14 </tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="38.373047" y="31" textLength="41.027344">Thread #1</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="42" textLength="193.5791">  oldclassic.java:95           event2.waitForEvent();</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="53" textLength="122.541504">  oldclassic.java:37         wait();</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="75" textLength="163.92041">------------------------------------ thread stacks</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="86" textLength="33.51709">Thread: </tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="33.358887" y="86" textLength="36.518555">Thread-0</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="97" textLength="181.58643">        at java.lang.Object.wait(Object.java:429)</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="108" textLength="184.57471">        at Event.waitForEvent(oldclassic.java:37)</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="119" textLength="27.505371">         ..</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="#7e7e7e" x="0" y="130" textLength="126.140625">========================</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#7e7e7e" x="0" y="142" textLength="114.49707">  1 Error Found: Deadlock</tspan></text><g filter="url(#Shadow_2)"><path d="M 30.1245 156.971 L 226.4705 156.971 C 233.09792 156.971 238.4705 162.34358 238.4705 168.971 L 238.4705 349.105 C 238.4705 355.73242 233.09792 361.105 226.4705 361.105 L 30.1245 361.105 C 23.497083 361.105 18.1245 355.73242 18.1245 349.105 L 18.1245 168.971 C 18.1245 162.34358 23.497083 156.971 30.1245 156.971 Z" fill="#ffd3db"/><path d="M 30.1245 156.971 L 226.4705 156.971 C 233.09792 156.971 238.4705 162.34358 238.4705 168.971 L 238.4705 349.105 C 238.4705 355.73242 233.09792 361.105 226.4705 361.105 L 30.1245 361.105 C 23.497083 361.105 18.1245 355.73242 18.1245 349.105 L 18.1245 168.971 C 18.1245 162.34358 23.497083 156.971 30.1245 156.971 Z" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 146.022 97.9336 L 482.417 97.9336 C 489.04442 97.9336 494.417 103.30618 494.417 109.9336 L 494.417 422.0106 C 494.417 428.63802 489.04442 434.0106 482.417 434.0106 L 146.022 434.0106 C 139.39458 434.0106 134.022 428.63802 134.022 422.0106 L 134.022 109.9336 C 134.022 103.30618 139.39458 97.9336 146.022 97.9336 Z" fill="#fffbbf" fill-opacity=".57"/><path d="M 146.022 97.9336 L 482.417 97.9336 C 489.04442 97.9336 494.417 103.30618 494.417 109.9336 L 494.417 422.0106 C 494.417 428.63802 489.04442 434.0106 482.417 434.0106 L 146.022 434.0106 C 139.39458 434.0106 134.022 428.63802 134.022 422.0106 L 134.022 109.9336 C 134.022 103.30618 139.39458 97.9336 146.022 97.9336 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g filter="url(#Shadow_2)"><path d="M 271.936 111.498 L 305.1668 111.498 C 311.79422 111.498 317.1668 116.87058 317.1668 123.498 L 317.1668 396.268 C 317.1668 402.89542 311.79422 408.268 305.1668 408.268 L 271.936 408.268 C 265.30858 408.268 259.936 402.89542 259.936 396.268 L 259.936 123.498 C 259.936 116.87058 265.30858 111.498 271.936 111.498 Z" fill="white"/><path d="M 271.936 111.498 L 305.1668 111.498 C 311.79422 111.498 317.1668 116.87058 317.1668 123.498 L 317.1668 396.268 C 317.1668 402.89542 311.79422 408.268 305.1668 408.268 L 271.936 408.268 C 265.30858 408.268 259.936 402.89542 259.936 396.268 L 259.936 123.498 C 259.936 116.87058 265.30858 111.498 271.936 111.498 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(264.936 242.883)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="10.7775094" y="14" textLength="25.675781">JPF</tspan><tspan font-family="Helvetica" font-size="14" font-weight="bold" x="7.667158" y="31" textLength="31.896484">Core</tspan></text></g><path d="M 346.726 196.737 L 410.101 196.737 C 414.976 196.737 414.976 196.737 414.976 198.91808 L 414.976 197.82754 C 417.4135 197.82754 417.4135 197.82754 417.4135 200.00862 L 417.4135 198.91808 C 419.851 198.91808 419.851 198.91808 419.851 201.09916 L 419.851 229.45322 C 419.851 231.6343 419.851 231.6343 414.976 231.6343 L 351.601 231.6343 C 346.726 231.6343 346.726 231.6343 346.726 230.54376 C 344.2885 230.54376 344.2885 230.54376 344.2885 229.45322 C 341.851 229.45322 341.851 229.45322 341.851 227.27214 L 341.851 198.91808 C 341.851 196.737 341.851 196.737 346.726 196.737 Z" fill="#fefff6"/><path d="M 346.726 196.737 L 410.101 196.737 C 414.976 196.737 414.976 196.737 414.976 198.91808 L 414.976 227.27214 C 414.976 229.45322 414.976 229.45322 410.101 229.45322 L 346.726 229.45322 C 341.851 229.45322 341.851 229.45322 341.851 227.27214 L 341.851 198.91808 C 341.851 196.737 341.851 196.737 346.726 196.737 M 414.976 197.82754 C 417.4135 197.82754 417.4135 197.82754 417.4135 200.00862 L 417.4135 229.45322 C 417.4135 230.54376 417.4135 230.54376 412.5385 230.54376 L 349.1635 230.54376 C 344.2885 230.54376 344.2885 230.54376 344.2885 229.45322 M 417.4135 198.91808 C 419.851 198.91808 419.851 198.91808 419.851 201.09916 L 419.851 229.45322 C 419.851 231.6343 419.851 231.6343 414.976 231.6343 L 351.601 231.6343 C 346.726 231.6343 346.726 231.6343 346.726 230.54376" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 198.35354)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="3.9934375" y="11" textLength="48.703125">bytecode</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="20.341094" y="25" textLength="16.0078125">set</tspan></text><path d="M 346.726 284.892 L 410.101 284.892 C 414.976 284.892 414.976 284.892 414.976 287.07308 L 414.976 285.98254 C 417.4135 285.98254 417.4135 285.98254 417.4135 288.16362 L 417.4135 287.07308 C 419.851 287.07308 419.851 287.07308 419.851 289.25416 L 419.851 317.60822 C 419.851 319.7893 419.851 319.7893 414.976 319.7893 L 351.601 319.7893 C 346.726 319.7893 346.726 319.7893 346.726 318.69876 C 344.2885 318.69876 344.2885 318.69876 344.2885 317.60822 C 341.851 317.60822 341.851 317.60822 341.851 315.42714 L 341.851 287.07308 C 341.851 284.892 341.851 284.892 346.726 284.892 Z" fill="#fefff6"/><path d="M 346.726 284.892 L 410.101 284.892 C 414.976 284.892 414.976 284.892 414.976 287.07308 L 414.976 315.42714 C 414.976 317.60822 414.976 317.60822 410.101 317.60822 L 346.726 317.60822 C 341.851 317.60822 341.851 317.60822 341.851 315.42714 L 341.851 287.07308 C 341.851 284.892 341.851 284.892 346.726 284.892 M 414.976 285.98254 C 417.4135 285.98254 417.4135 285.98254 417.4135 288.16362 L 417.4135 317.60822 C 417.4135 318.69876 417.4135 318.69876 412.5385 318.69876 L 349.1635 318.69876 C 344.2885 318.69876 344.2885 318.69876 344.2885 317.60822 M 417.4135 287.07308 C 419.851 287.07308 419.851 287.07308 419.851 289.25416 L 419.851 317.60822 C 419.851 319.7893 419.851 319.7893 414.976 319.7893 L 351.601 319.7893 C 346.726 319.7893 346.726 319.7893 346.726 318.69876" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 286.50854)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.336211" y="11" textLength="42.017578">listener/</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="6.334258" y="25" textLength="44.021484">property</tspan></text><path d="M 346.726 240.755 L 410.101 240.755 C 414.976 240.755 414.976 240.755 414.976 242.93608 L 414.976 241.84554 C 417.4135 241.84554 417.4135 241.84554 417.4135 244.02662 L 417.4135 242.93608 C 419.851 242.93608 419.851 242.93608 419.851 245.11716 L 419.851 273.47122 C 419.851 275.6523 419.851 275.6523 414.976 275.6523 L 351.601 275.6523 C 346.726 275.6523 346.726 275.6523 346.726 274.56176 C 344.2885 274.56176 344.2885 274.56176 344.2885 273.47122 C 341.851 273.47122 341.851 273.47122 341.851 271.29014 L 341.851 242.93608 C 341.851 240.755 341.851 240.755 346.726 240.755 Z" fill="#fefff6"/><path d="M 346.726 240.755 L 410.101 240.755 C 414.976 240.755 414.976 240.755 414.976 242.93608 L 414.976 271.29014 C 414.976 273.47122 414.976 273.47122 410.101 273.47122 L 346.726 273.47122 C 341.851 273.47122 341.851 273.47122 341.851 271.29014 L 341.851 242.93608 C 341.851 240.755 341.851 240.755 346.726 240.755 M 414.976 241.84554 C 417.4135 241.84554 417.4135 241.84554 417.4135 244.02662 L 417.4135 273.47122 C 417.4135 274.56176 417.4135 274.56176 412.5385 274.56176 L 349.1635 274.56176 C 344.2885 274.56176 344.2885 274.56176 344.2885 273.47122 M 417.4135 242.93608 C 419.851 242.93608 419.851 242.93608 419.851 245.11716 L 419.851 273.47122 C 419.851 275.6523 419.851 275.6523 414.976 275.6523 L 351.601 275.6523 C 346.726 275.6523 346.726 275.6523 346.726 274.56176" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 242.37154)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.329375" y="11" textLength="55.365234">publisher/ </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="18.343047" y="25" textLength="20.003906">-ext</tspan></text><path d="M 346.726 110.242 L 410.101 110.242 C 414.976 110.242 414.976 110.242 414.976 112.42308 L 414.976 111.33254 C 417.4135 111.33254 417.4135 111.33254 417.4135 113.51362 L 417.4135 112.42308 C 419.851 112.42308 419.851 112.42308 419.851 114.60416 L 419.851 142.95822 C 419.851 145.1393 419.851 145.1393 414.976 145.1393 L 351.601 145.1393 C 346.726 145.1393 346.726 145.1393 346.726 144.04876 C 344.2885 144.04876 344.2885 144.04876 344.2885 142.95822 C 341.851 142.95822 341.851 142.95822 341.851 140.77714 L 341.851 112.42308 C 341.851 110.242 341.851 110.242 346.726 110.242 Z" fill="#fefff6"/><path d="M 346.726 110.242 L 410.101 110.242 C 414.976 110.242 414.976 110.242 414.976 112.42308 L 414.976 140.77714 C 414.976 142.95822 414.976 142.95822 410.101 142.95822 L 346.726 142.95822 C 341.851 142.95822 341.851 142.95822 341.851 140.77714 L 341.851 112.42308 C 341.851 110.242 341.851 110.242 346.726 110.242 M 414.976 111.33254 C 417.4135 111.33254 417.4135 111.33254 417.4135 113.51362 L 417.4135 142.95822 C 417.4135 144.04876 417.4135 144.04876 412.5385 144.04876 L 349.1635 144.04876 C 344.2885 144.04876 344.2885 144.04876 344.2885 142.95822 M 417.4135 112.42308 C 419.851 112.42308 419.851 112.42308 419.851 114.60416 L 419.851 142.95822 C 419.851 145.1393 419.851 145.1393 414.976 145.1393 L 351.601 145.1393 C 346.726 145.1393 346.726 145.1393 346.726 144.04876" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 111.85854)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="11.00125" y="11" textLength="34.6875">choice</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.6604297" y="25" textLength="51.36914">generator</tspan></text><path d="M 346.726 328.15 L 410.101 328.15 C 414.976 328.15 414.976 328.15 414.976 330.33108 L 414.976 329.24054 C 417.4135 329.24054 417.4135 329.24054 417.4135 331.42162 L 417.4135 330.33108 C 419.851 330.33108 419.851 330.33108 419.851 332.51216 L 419.851 360.86622 C 419.851 363.0473 419.851 363.0473 414.976 363.0473 L 351.601 363.0473 C 346.726 363.0473 346.726 363.0473 346.726 361.95676 C 344.2885 361.95676 344.2885 361.95676 344.2885 360.86622 C 341.851 360.86622 341.851 360.86622 341.851 358.68514 L 341.851 330.33108 C 341.851 328.15 341.851 328.15 346.726 328.15 Z" fill="#fefff6"/><path d="M 346.726 328.15 L 410.101 328.15 C 414.976 328.15 414.976 328.15 414.976 330.33108 L 414.976 358.68514 C 414.976 360.86622 414.976 360.86622 410.101 360.86622 L 346.726 360.86622 C 341.851 360.86622 341.851 360.86622 341.851 358.68514 L 341.851 330.33108 C 341.851 328.15 341.851 328.15 346.726 328.15 M 414.976 329.24054 C 417.4135 329.24054 417.4135 329.24054 417.4135 331.42162 L 417.4135 360.86622 C 417.4135 361.95676 417.4135 361.95676 412.5385 361.95676 L 349.1635 361.95676 C 344.2885 361.95676 344.2885 361.95676 344.2885 360.86622 M 417.4135 330.33108 C 419.851 330.33108 419.851 330.33108 419.851 332.51216 L 419.851 360.86622 C 419.851 363.0473 419.851 363.0473 414.976 363.0473 L 351.601 363.0473 C 346.726 363.0473 346.726 363.0473 346.726 361.95676" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 329.76654)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.6721484" y="11" textLength="51.345703">serializer/</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.673125" y="25" textLength="41.34375">restorer</tspan></text><path d="M 154.52 285.36 L 217.895 285.36 C 222.77 285.36 222.77 285.36 222.77 287.54108 L 222.77 286.45054 C 225.2075 286.45054 225.2075 286.45054 225.2075 288.63162 L 225.2075 287.54108 C 227.645 287.54108 227.645 287.54108 227.645 289.72216 L 227.645 318.07622 C 227.645 320.2573 227.645 320.2573 222.77 320.2573 L 159.395 320.2573 C 154.52 320.2573 154.52 320.2573 154.52 319.16676 C 152.0825 319.16676 152.0825 319.16676 152.0825 318.07622 C 149.645 318.07622 149.645 318.07622 149.645 315.89514 L 149.645 287.54108 C 149.645 285.36 149.645 285.36 154.52 285.36 Z" fill="#fefff6"/><path d="M 154.52 285.36 L 217.895 285.36 C 222.77 285.36 222.77 285.36 222.77 287.54108 L 222.77 315.89514 C 222.77 318.07622 222.77 318.07622 217.895 318.07622 L 154.52 318.07622 C 149.645 318.07622 149.645 318.07622 149.645 315.89514 L 149.645 287.54108 C 149.645 285.36 149.645 285.36 154.52 285.36 M 222.77 286.45054 C 225.2075 286.45054 225.2075 286.45054 225.2075 288.63162 L 225.2075 318.07622 C 225.2075 319.16676 225.2075 319.16676 220.3325 319.16676 L 156.9575 319.16676 C 152.0825 319.16676 152.0825 319.16676 152.0825 318.07622 M 225.2075 287.54108 C 227.645 287.54108 227.645 287.54108 227.645 289.72216 L 227.645 318.07622 C 227.645 320.2573 227.645 320.2573 222.77 320.2573 L 159.395 320.2573 C 154.52 320.2573 154.52 320.2573 154.52 319.16676" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(156.205 293.97654)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".31960937" y="11" textLength="56.05078">annotation</tspan></text><line x1="249.343" y1="60.98" x2="249.343" y2="410.723" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="10,3,2,3"/><text transform="translate(130.465 327.122)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".47558594" y="11" textLength="106.04883"> (optional) in-source</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="17.148438" y="25" textLength="72.703125">property spec</tspan></text><path d="M 155.56714 196.608 L 221.63501 196.608 C 226.71716 196.608 226.71716 196.608 226.71716 198.78908 L 226.71716 197.69854 C 229.25823 197.69854 229.25823 197.69854 229.25823 199.87962 L 229.25823 198.78908 C 231.7993 198.78908 231.7993 198.78908 231.7993 200.97016 L 231.7993 229.32422 C 231.7993 231.5053 231.7993 231.5053 226.71716 231.5053 L 160.64929 231.5053 C 155.56714 231.5053 155.56714 231.5053 155.56714 230.41476 C 153.02607 230.41476 153.02607 230.41476 153.02607 229.32422 C 150.485 229.32422 150.485 229.32422 150.485 227.14314 L 150.485 198.78908 C 150.485 196.608 150.485 196.608 155.56714 196.608 Z" fill="#fefff6"/><path d="M 155.56714 196.608 L 221.63501 196.608 C 226.71716 196.608 226.71716 196.608 226.71716 198.78908 L 226.71716 227.14314 C 226.71716 229.32422 226.71716 229.32422 221.63501 229.32422 L 155.56714 229.32422 C 150.485 229.32422 150.485 229.32422 150.485 227.14314 L 150.485 198.78908 C 150.485 196.608 150.485 196.608 155.56714 196.608 M 226.71716 197.69854 C 229.25823 197.69854 229.25823 197.69854 229.25823 199.87962 L 229.25823 229.32422 C 229.25823 230.41476 229.25823 230.41476 224.17608 230.41476 L 158.10822 230.41476 C 153.02607 230.41476 153.02607 230.41476 153.02607 229.32422 M 229.25823 198.78908 C 231.7993 198.78908 231.7993 198.78908 231.7993 200.97016 L 231.7993 229.32422 C 231.7993 231.5053 231.7993 231.5053 226.71716 231.5053 L 160.64929 231.5053 C 155.56714 231.5053 155.56714 231.5053 155.56714 230.41476" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(157.11129 198.22454)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="10.0831523" y="11" textLength="39.357422">domain</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="1.7569804" y="25" textLength="56.009766">framework</tspan></text><text transform="translate(172.599 163.656)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".158203125" y="11" textLength="32.683594">model</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".1640625" y="25" textLength="32.671875">library</tspan></text><g id="id267_Graphic"><path d="M 32.187544 235.617 L 102.98351 235.617 C 108.429356 235.617 108.429356 235.617 108.429356 238.3499 L 108.429356 236.98345 C 111.15228 236.98345 111.15228 236.98345 111.15228 239.71634 L 111.15228 238.3499 C 113.8752 238.3499 113.8752 238.3499 113.8752 241.08279 L 113.8752 276.6104 C 113.8752 279.3433 113.8752 279.3433 108.429356 279.3433 L 37.633388 279.3433 C 32.187544 279.3433 32.187544 279.3433 32.187544 277.97685 C 29.464622 277.97685 29.464622 277.97685 29.464622 276.6104 C 26.7417 276.6104 26.7417 276.6104 26.7417 273.87751 L 26.7417 238.3499 C 26.7417 235.617 26.7417 235.617 32.187544 235.617 Z" fill="white"/><path d="M 32.187544 235.617 L 102.98351 235.617 C 108.429356 235.617 108.429356 235.617 108.429356 238.3499 L 108.429356 273.87751 C 108.429356 276.6104 108.429356 276.6104 102.98351 276.6104 L 32.187544 276.6104 C 26.7417 276.6104 26.7417 276.6104 26.7417 273.87751 L 26.7417 238.3499 C 26.7417 235.617 26.7417 235.617 32.187544 235.617 M 108.429356 236.98345 C 111.15228 236.98345 111.15228 236.98345 111.15228 239.71634 L 111.15228 276.6104 C 111.15228 277.97685 111.15228 277.97685 105.706434 277.97685 L 34.910466 277.97685 C 29.464622 277.97685 29.464622 277.97685 29.464622 276.6104 M 111.15228 238.3499 C 113.8752 238.3499 113.8752 238.3499 113.8752 241.08279 L 113.8752 276.6104 C 113.8752 279.3433 113.8752 279.3433 108.429356 279.3433 L 37.633388 279.3433 C 32.187544 279.3433 32.187544 279.3433 32.187544 277.97685" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(33.48437 248.18452)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="red" x=".57964937" y="11" textLength="63.339844">application</tspan></text></g><line x1="122.237904" y1="240.46846" x2="153.34371" y2="230.27844" marker-start="url(#FilledArrow_Marker_3)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="122.17559" y1="275.74486" x2="150.254375" y2="285.63264" marker-start="url(#FilledArrow_Marker_3)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.351" y1="127.69064" x2="316.791" y2="127.69" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.851" y1="170.78465" x2="316.816" y2="170.46" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.851" y1="214.18565" x2="316.816" y2="214.088" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.3511" y1="258.1939" x2="316.816" y2="257.715" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.35102" y1="302.33574" x2="316.816" y2="302.095" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="341.35116" y1="345.5861" x2="316.816" y2="344.97" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="23.7492" y1="70.7573" x2="238.504" y2="70.7573" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="132.098" y="60.4708" width="34" height="16" fill="white"/><text transform="translate(137.098 60.4708)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="23.841797">SuT</tspan></text><line x1="260.26599" y1="70.768543" x2="485.89801" y2="70.312557" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="304.843" y="53.864" width="82" height="32" fill="white"/><text transform="translate(309.843 53.864)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="55.649902">execution</tspan><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="29" textLength="71.538086">environment</tspan></text><path d="M 346.569 372.698 L 409.944 372.698 C 414.819 372.698 414.819 372.698 414.819 374.87908 L 414.819 373.78854 C 417.2565 373.78854 417.2565 373.78854 417.2565 375.96962 L 417.2565 374.87908 C 419.694 374.87908 419.694 374.87908 419.694 377.06016 L 419.694 405.41422 C 419.694 407.5953 419.694 407.5953 414.819 407.5953 L 351.444 407.5953 C 346.569 407.5953 346.569 407.5953 346.569 406.50476 C 344.1315 406.50476 344.1315 406.50476 344.1315 405.41422 C 341.694 405.41422 341.694 405.41422 341.694 403.23314 L 341.694 374.87908 C 341.694 372.698 341.694 372.698 346.569 372.698 Z" fill="#fefff6"/><path d="M 346.569 372.698 L 409.944 372.698 C 414.819 372.698 414.819 372.698 414.819 374.87908 L 414.819 403.23314 C 414.819 405.41422 414.819 405.41422 409.944 405.41422 L 346.569 405.41422 C 341.694 405.41422 341.694 405.41422 341.694 403.23314 L 341.694 374.87908 C 341.694 372.698 341.694 372.698 346.569 372.698 M 414.819 373.78854 C 417.2565 373.78854 417.2565 373.78854 417.2565 375.96962 L 417.2565 405.41422 C 417.2565 406.50476 417.2565 406.50476 412.3815 406.50476 L 349.0065 406.50476 C 344.1315 406.50476 344.1315 406.50476 344.1315 405.41422 M 417.2565 374.87908 C 419.694 374.87908 419.694 374.87908 419.694 377.06016 L 419.694 405.41422 C 419.694 407.5953 419.694 407.5953 414.819 407.5953 L 351.444 407.5953 C 346.569 407.5953 346.569 407.5953 346.569 406.50476" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.254 374.31454)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="10.336211" y="11" textLength="36.017578">search</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.0022266" y="25" textLength="42.685547">strategy</tspan></text><line x1="341.19404" y1="390.13999" x2="316.659" y2="389.813" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(265.13 289.673)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="10.326172" y="11" textLength="25.347656">Java</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="6.995117" y="25" textLength="32.009766">virtual</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".32128906" y="39" textLength="45.357422">machine</tspan></text><path d="M 118.697 248.87425 L 168.44068 248.87425 L 168.44068 239.294 L 185.0219 258.4545 L 168.44068 277.615 L 168.44068 268.03475 L 118.697 268.03475 Z" fill="#ebe8ff"/><path d="M 118.697 248.87425 L 168.44068 248.87425 L 168.44068 239.294 L 185.0219 258.4545 L 168.44068 277.615 L 168.44068 268.03475 L 118.697 268.03475 Z" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(123.348 250.672)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".1484375" y="11" textLength="48.703125">bytecode</tspan></text><text transform="translate(484.521 250.515)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".32617188" y="11" textLength="31.347656">report</tspan></text><text transform="translate(141.086 102.486)" fill="red"><tspan font-family="Helvetica" font-size="15" font-weight="500" fill="red" x="0" y="15" textLength="26.66748">JPF</tspan></text><line x1="627.333" y1="314.722" x2="627.333" y2="320.178" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="624.70977" y1="327.42927" x2="618.59018" y2="334.78068" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="613.34386" y1="341.08335" x2="607.2231" y2="348.43661" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="604.6" y1="355.688" x2="604.6" y2="361.143" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 597.527 364.956 C 594.45664 365.32263 591.13855 366.62494 588.315 366.056 C 585.49145 365.48706 582.53897 363.92343 580.584 361.542 C 578.62903 359.16057 576.91747 355.24282 576.584 351.766 C 576.25053 348.28918 577.16148 343.96717 578.583 340.679 C 580.00452 337.39083 582.44243 334.16829 585.114 332.035 C 587.78557 329.90171 591.53098 328.46127 594.614 327.878 C 597.69702 327.29473 600.5994 327.27322 603.614 328.535 C 605.1366 329.1723 606.66708 330.34182 608.19938 331.6403" marker-end="url(#FilledArrow_Marker_4)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="631.00532" y1="326.1034" x2="651.1297" y2="336.10659" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="652.24505" y1="341.1374" x2="635.57295" y2="362.0376" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="634.06917" y1="369.20604" x2="636.22385" y2="377.31395" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 651.872 350.006 C 649.0306 353.83895 644.38623 358.33382 643.347 361.506 C 642.30777 364.67818 643.85634 367.09003 645.636 369.041 C 647.41566 370.99197 651.09246 372.4959 654.026 373.213 C 656.95954 373.9301 660.37495 373.87278 663.239 373.344 C 666.10305 372.81522 669.20403 371.84615 671.212 370.04 C 673.21997 368.23385 675.44765 365.4452 675.288 362.506 C 675.12835 359.5668 672.3428 355.48602 670.254 352.403 C 669.2545 350.92772 668.0666 349.58246 666.82558 348.27392" marker-end="url(#FilledArrow_Marker_4)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="658.56888" y1="339.55327" x2="693.57913" y2="354.62173" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="628.534" y1="402.73847" x2="634.65403" y2="410.0915" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="646.02086" y1="402.73835" x2="639.9001" y2="410.09161" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="697.2711" y1="360.34232" x2="697.0259" y2="373.76468" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="696.951" y1="381.964" x2="696.951" y2="387.418" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="694.32786" y1="394.66935" x2="688.2071" y2="402.02261" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="682.9609" y1="408.32537" x2="676.84107" y2="415.6776" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="699.57414" y1="394.66935" x2="705.6949" y2="402.02261" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="627.333" cy="310.622" r="3.6000058" fill="yellow"/><circle cx="627.333" cy="310.622" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="627.333" cy="324.278" r="3.6000058" fill="yellow"/><circle cx="627.333" cy="324.278" r="3.6000058" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="615.967" cy="337.932" r="3.6000058" fill="white"/><circle cx="615.967" cy="337.932" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="604.6" cy="351.588" r="3.6000058" fill="white"/><circle cx="604.6" cy="351.588" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="604.6" cy="365.243" r="3.6000058" fill="blue"/><circle cx="604.6" cy="365.243" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="654.802" cy="337.932" r="3.6000058" fill="yellow"/><circle cx="654.802" cy="337.932" r="3.6000058" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="633.016" cy="365.243" r="3.6000058" fill="blue"/><circle cx="633.016" cy="365.243" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="697.346" cy="356.243" r="3.6000058" fill="yellow"/><circle cx="697.346" cy="356.243" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="637.277" cy="381.277" r="3.6000058" fill="white"/><circle cx="637.277" cy="381.277" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="648.644" cy="399.587" r="3.6000058" fill="white"/><circle cx="648.644" cy="399.587" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="625.911" cy="399.587" r="3.6000058" fill="white"/><circle cx="625.911" cy="399.587" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="637.277" cy="413.243" r="3.6000058" fill="white"/><circle cx="637.277" cy="413.243" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="696.951" cy="391.518" r="3.6000058" fill="white"/><circle cx="696.951" cy="391.518" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="708.318" cy="405.174" r="3.6000058" fill="white"/><circle cx="708.318" cy="405.174" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="685.584" cy="405.174" r="3.6000058" fill="white"/><circle cx="685.584" cy="405.174" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="674.218" cy="418.829" r="3.6000058" fill="white"/><circle cx="674.218" cy="418.829" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="696.951" cy="377.864" r="3.6000058" fill="red"/><circle cx="696.951" cy="377.864" r="3.6000058" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(581.584 332.815)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="2.0389578" y="11" textLength="20.021484">end</tspan></text><text transform="translate(645.152 355.212)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="1.5648078" y="11" textLength="26.021484">seen</tspan></text><text transform="translate(698.473 307.269)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="1.88925" y="11" textLength="52.6875">error-path</tspan></text><text transform="translate(703.341 369.88)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="6.2222578" y="11" textLength="47.35547">property </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="6.219328" y="25" textLength="44.027344">violation</tspan></text><path d="M 680.049 343.105 C 688.4935 338.63578 694.70723 336.80062 705.385 329.696 C 716.06277 322.59138 737.21352 311.11894 744.122 300.473 C 751.03048 289.82706 751.51837 281.20713 746.84 265.814 C 742.84603 252.67272 730.70992 233.928 720.8592 216.75722" marker-end="url(#FilledArrow_Marker_5)" stroke="#ff8987" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><line x1="618.13855" y1="341.41063" x2="630.84446" y2="361.76436" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="635.11422" y1="384.7611" x2="628.07376" y2="396.10288" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="639.4399" y1="384.76102" x2="646.4811" y2="396.10298" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 706.065 373.189 C 710.36857 367.52657 717.165 361.63612 718.977 356.2 C 720.789 350.76388 723.2802 345.6655 716.938 340.569 C 710.5958 335.4725 690.9995 330.71432 680.92 325.618 C 670.8405 320.52168 663.47663 316.3302 656.455 309.988 C 651.8553 305.83336 648.22683 300.7056 644.6155 295.56124" marker-end="url(#FilledArrow_Marker_5)" stroke="#ff8987" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(313.697 453.368)" fill="red"><tspan font-family="Helvetica" font-size="15" font-weight="500" fill="red" x="0" y="15" textLength="71.68213">host - JVM</tspan></text><line x1="429.938" y1="29.5677" x2="429.938" y2="410.892" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="10,3,2,3"/><rect x="225.929" y="30.9748" width="100" height="16" fill="white"/><text transform="translate(230.929 30.9748)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="89.59082">JPF distribution</tspan></text><path d="M 437.51213 154.523 L 483.03984 154.523 C 486.54197 154.523 486.54197 154.523 486.54197 156.70408 L 486.54197 155.61354 C 488.29303 155.61354 488.29303 155.61354 488.29303 157.79462 L 488.29303 156.70408 C 490.0441 156.70408 490.0441 156.70408 490.0441 158.88516 L 490.0441 187.23922 C 490.0441 189.4203 490.0441 189.4203 486.54197 189.4203 L 441.01426 189.4203 C 437.51213 189.4203 437.51213 189.4203 437.51213 188.32976 C 435.76107 188.32976 435.76107 188.32976 435.76107 187.23922 C 434.01 187.23922 434.01 187.23922 434.01 185.05814 L 434.01 156.70408 C 434.01 154.523 434.01 154.523 437.51213 154.523 Z" fill="#fefff6"/><path d="M 437.51213 154.523 L 483.03984 154.523 C 486.54197 154.523 486.54197 154.523 486.54197 156.70408 L 486.54197 185.05814 C 486.54197 187.23922 486.54197 187.23922 483.03984 187.23922 L 437.51213 187.23922 C 434.01 187.23922 434.01 187.23922 434.01 185.05814 L 434.01 156.70408 C 434.01 154.523 434.01 154.523 437.51213 154.523 M 486.54197 155.61354 C 488.29303 155.61354 488.29303 155.61354 488.29303 157.79462 L 488.29303 187.23922 C 488.29303 188.32976 488.29303 188.32976 484.7909 188.32976 L 439.2632 188.32976 C 435.76107 188.32976 435.76107 188.32976 435.76107 187.23922 M 488.29303 156.70408 C 490.0441 156.70408 490.0441 156.70408 490.0441 158.88516 L 490.0441 187.23922 C 490.0441 189.4203 490.0441 189.4203 486.54197 189.4203 L 441.01426 189.4203 C 437.51213 189.4203 437.51213 189.4203 437.51213 188.32976" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(440.13068 163.13954)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="13.953601" y="11" textLength="10.001953">...</tspan></text><path d="M 346.726 153.336 L 410.101 153.336 C 414.976 153.336 414.976 153.336 414.976 155.51708 L 414.976 154.42654 C 417.4135 154.42654 417.4135 154.42654 417.4135 156.60762 L 417.4135 155.51708 C 419.851 155.51708 419.851 155.51708 419.851 157.69816 L 419.851 186.05222 C 419.851 188.2333 419.851 188.2333 414.976 188.2333 L 351.601 188.2333 C 346.726 188.2333 346.726 188.2333 346.726 187.14276 C 344.2885 187.14276 344.2885 187.14276 344.2885 186.05222 C 341.851 186.05222 341.851 186.05222 341.851 183.87114 L 341.851 155.51708 C 341.851 153.336 341.851 153.336 346.726 153.336 Z" fill="#fefff6"/><path d="M 346.726 153.336 L 410.101 153.336 C 414.976 153.336 414.976 153.336 414.976 155.51708 L 414.976 183.87114 C 414.976 186.05222 414.976 186.05222 410.101 186.05222 L 346.726 186.05222 C 341.851 186.05222 341.851 186.05222 341.851 183.87114 L 341.851 155.51708 C 341.851 153.336 341.851 153.336 346.726 153.336 M 414.976 154.42654 C 417.4135 154.42654 417.4135 154.42654 417.4135 156.60762 L 417.4135 186.05222 C 417.4135 187.14276 417.4135 187.14276 412.5385 187.14276 L 349.1635 187.14276 C 344.2885 187.14276 344.2885 187.14276 344.2885 186.05222 M 417.4135 155.51708 C 419.851 155.51708 419.851 155.51708 419.851 157.69816 L 419.851 186.05222 C 419.851 188.2333 419.851 188.2333 414.976 188.2333 L 351.601 188.2333 C 346.726 188.2333 346.726 188.2333 346.726 187.14276" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.411 154.95254)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="12.334258" y="11" textLength="32.021484">native</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="16.336211" y="25" textLength="24.017578">peer</tspan></text><path d="M 437.68213 285.265 L 483.20984 285.265 C 486.71197 285.265 486.71197 285.265 486.71197 287.44608 L 486.71197 286.35554 C 488.46303 286.35554 488.46303 286.35554 488.46303 288.53662 L 488.46303 287.44608 C 490.2141 287.44608 490.2141 287.44608 490.2141 289.62716 L 490.2141 317.98122 C 490.2141 320.1623 490.2141 320.1623 486.71197 320.1623 L 441.18426 320.1623 C 437.68213 320.1623 437.68213 320.1623 437.68213 319.07176 C 435.93107 319.07176 435.93107 319.07176 435.93107 317.98122 C 434.18 317.98122 434.18 317.98122 434.18 315.80014 L 434.18 287.44608 C 434.18 285.265 434.18 285.265 437.68213 285.265 Z" fill="#fefff6"/><path d="M 437.68213 285.265 L 483.20984 285.265 C 486.71197 285.265 486.71197 285.265 486.71197 287.44608 L 486.71197 315.80014 C 486.71197 317.98122 486.71197 317.98122 483.20984 317.98122 L 437.68213 317.98122 C 434.18 317.98122 434.18 317.98122 434.18 315.80014 L 434.18 287.44608 C 434.18 285.265 434.18 285.265 437.68213 285.265 M 486.71197 286.35554 C 488.46303 286.35554 488.46303 286.35554 488.46303 288.53662 L 488.46303 317.98122 C 488.46303 319.07176 488.46303 319.07176 484.9609 319.07176 L 439.4332 319.07176 C 435.93107 319.07176 435.93107 319.07176 435.93107 317.98122 M 488.46303 287.44608 C 490.2141 287.44608 490.2141 287.44608 490.2141 289.62716 L 490.2141 317.98122 C 490.2141 320.1623 490.2141 320.1623 486.71197 320.1623 L 441.18426 320.1623 C 437.68213 320.1623 437.68213 320.1623 437.68213 319.07176" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(440.30068 293.88154)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="13.953601" y="11" textLength="10.001953">...</tspan></text><path d="M 437.68213 242.749 L 483.20984 242.749 C 486.71197 242.749 486.71197 242.749 486.71197 244.93008 L 486.71197 243.83954 C 488.46303 243.83954 488.46303 243.83954 488.46303 246.02062 L 488.46303 244.93008 C 490.2141 244.93008 490.2141 244.93008 490.2141 247.11116 L 490.2141 275.46522 C 490.2141 277.6463 490.2141 277.6463 486.71197 277.6463 L 441.18426 277.6463 C 437.68213 277.6463 437.68213 277.6463 437.68213 276.55576 C 435.93107 276.55576 435.93107 276.55576 435.93107 275.46522 C 434.18 275.46522 434.18 275.46522 434.18 273.28414 L 434.18 244.93008 C 434.18 242.749 434.18 242.749 437.68213 242.749 Z" fill="#fefff6"/><path d="M 437.68213 242.749 L 483.20984 242.749 C 486.71197 242.749 486.71197 242.749 486.71197 244.93008 L 486.71197 273.28414 C 486.71197 275.46522 486.71197 275.46522 483.20984 275.46522 L 437.68213 275.46522 C 434.18 275.46522 434.18 275.46522 434.18 273.28414 L 434.18 244.93008 C 434.18 242.749 434.18 242.749 437.68213 242.749 M 486.71197 243.83954 C 488.46303 243.83954 488.46303 243.83954 488.46303 246.02062 L 488.46303 275.46522 C 488.46303 276.55576 488.46303 276.55576 484.9609 276.55576 L 439.4332 276.55576 C 435.93107 276.55576 435.93107 276.55576 435.93107 275.46522 M 488.46303 244.93008 C 490.2141 244.93008 490.2141 244.93008 490.2141 247.11116 L 490.2141 275.46522 C 490.2141 277.6463 490.2141 277.6463 486.71197 277.6463 L 441.18426 277.6463 C 437.68213 277.6463 437.68213 277.6463 437.68213 276.55576" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(440.30068 251.36554)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="13.953601" y="11" textLength="10.001953">...</tspan></text><path d="M 482.577 248.71725 L 532.32068 248.71725 L 532.32068 239.137 L 548.9019 258.2975 L 532.32068 277.458 L 532.32068 267.87775 L 482.577 267.87775 Z" fill="#ebe8ff"/><path d="M 482.577 248.71725 L 532.32068 248.71725 L 532.32068 239.137 L 548.9019 258.2975 L 532.32068 277.458 L 532.32068 267.87775 L 482.577 267.87775 Z" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="439.75" y="31.1434" width="73" height="16" fill="white"/><text transform="translate(444.75 31.1434)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="62.149902">extensions</tspan></text><g id="id10_Graphic"><path d="M 277.115 548.62458 L 277.115 511.94762 C 277.115 509.4169 287.67938 507.363 300.6962 507.363 C 313.71302 507.363 324.2774 509.4169 324.2774 511.94762 L 324.2774 548.62458 C 324.2774 551.1553 313.71302 553.2092 300.6962 553.2092 C 287.67938 553.2092 277.115 551.1553 277.115 548.62458" fill="white"/><path d="M 277.115 548.62458 L 277.115 511.94762 C 277.115 509.4169 287.67938 507.363 300.6962 507.363 C 313.71302 507.363 324.2774 509.4169 324.2774 511.94762 L 324.2774 548.62458 C 324.2774 551.1553 313.71302 553.2092 300.6962 553.2092 C 287.67938 553.2092 277.115 551.1553 277.115 548.62458 M 277.115 511.94762 C 277.115 514.47833 287.67938 516.53224 300.6962 516.53224 C 313.71302 516.53224 324.2774 514.47833 324.2774 511.94762" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(282.115 524.0784)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="6.5191883" y="14" textLength="24.124023">*.jpf</tspan></text></g><g id="id312_Graphic"><rect x="407.819" y="548.116" width="1.0188" height=".125" fill="white"/><rect x="407.819" y="548.116" width="1.0188" height=".125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 77.7265 380.014 L 77.7265 519.852 C 77.7265 527.584 83.994514 533.852 91.7265 533.852 L 257.324 533.852" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 422.082 415.5 L 421.1825 519.97253 C 421.11634 527.6572 414.86798 533.852 407.18302 533.852 L 344.365 533.852" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 336.8 397.341 L 336.8 401.679 C 336.8 409.41099 343.06801 415.679 350.8 415.679 L 473.80245 415.679 C 481.53444 415.679 487.80245 409.41099 487.80245 401.679 C 487.80245 401.41993 487.79526 401.16092 487.7809 400.90225 L 487.583 397.341" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><line x1="299.826" y1="501.253" x2="298.89868" y2="401.12458" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(327.878 553.383)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="54.183594">JPF confi</tspan><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="54.183594" y="13" textLength="46.979004">guration</tspan></text><text transform="translate(496.924 250.84)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".32617188" y="11" textLength="31.347656">report</tspan></text><path d="M 139.874 445.218 L 99.1214 445.218 L 99.1214 291.09" marker-end="url(#FilledArrow_Marker_6)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 207.836 451.331 L 245.532 451.331 L 245.532 442.89" marker-end="url(#FilledArrow_Marker_6)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(202.648 452.006)" fill="blue"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="13" textLength="50.59082">standard</tspan><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" fill="blue" x="0" y="29" textLength="76.584473">Java libraries</tspan></text><g filter="url(#Shadow_2)"><path d="M 152.821 462.01758 L 152.821 425.34062 C 152.821 422.8099 163.38538 420.756 176.4022 420.756 C 189.41902 420.756 199.9834 422.8099 199.9834 425.34062 L 199.9834 462.01758 C 199.9834 464.5483 189.41902 466.6022 176.4022 466.6022 C 163.38538 466.6022 152.821 464.5483 152.821 462.01758" fill="white"/><path d="M 152.821 462.01758 L 152.821 425.34062 C 152.821 422.8099 163.38538 420.756 176.4022 420.756 C 189.41902 420.756 199.9834 422.8099 199.9834 425.34062 L 199.9834 462.01758 C 199.9834 464.5483 189.41902 466.6022 176.4022 466.6022 C 163.38538 466.6022 152.821 464.5483 152.821 462.01758 M 152.821 425.34062 C 152.821 427.87133 163.38538 429.92524 176.4022 429.92524 C 189.41902 429.92524 199.9834 427.87133 199.9834 425.34062" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(157.821 437.4714)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="6.1329578" y="14" textLength="24.896484">*.jar</tspan></text></g><g id="id320_Graphic"><path d="M 147.727 455.90558 L 147.727 419.22862 C 147.727 416.6979 158.29138 414.644 171.3082 414.644 C 184.32502 414.644 194.8894 416.6979 194.8894 419.22862 L 194.8894 455.90558 C 194.8894 458.4363 184.32502 460.4902 171.3082 460.4902 C 158.29138 460.4902 147.727 458.4363 147.727 455.90558" fill="white"/><path d="M 147.727 455.90558 L 147.727 419.22862 C 147.727 416.6979 158.29138 414.644 171.3082 414.644 C 184.32502 414.644 194.8894 416.6979 194.8894 419.22862 L 194.8894 455.90558 C 194.8894 458.4363 184.32502 460.4902 171.3082 460.4902 C 158.29138 460.4902 147.727 458.4363 147.727 455.90558 M 147.727 419.22862 C 147.727 421.75933 158.29138 423.81324 171.3082 423.81324 C 184.32502 423.81324 194.8894 421.75933 194.8894 419.22862" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(152.727 431.3594)" fill="black"><tspan font-family="Helvetica" font-size="14" font-weight="500" x="6.1329578" y="14" textLength="24.896484">*.jar</tspan></text></g></g></g></svg>
diff --git a/doc/graphics/jpf-layers.svg b/doc/graphics/jpf-layers.svg
new file mode 100644 (file)
index 0000000..ada4957
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="8 66 540 266" width="45pc" height="266pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-02 21:48:18 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="red"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-857.14286" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="576" height="734"/><g><title>Layer 1</title><g><xl:use xl:href="#id1_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id30_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id4_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id6_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id9_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id11_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id14_Graphic" filter="url(#Shadow)"/></g><g id="id1_Graphic"><rect x="100" y="281" width="287" height="34" fill="white"/><rect x="100" y="281" width="287" height="34" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(105 291)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="106.490234" y="11" textLength="64.01953">platform OS</tspan></text></g><g id="id30_Graphic"><path d="M 165 208 L 325 208 L 324 273 L 301 273 L 301 232 L 189 232 L 189 273 L 165 273 Z" fill="white"/><path d="M 165 208 L 325 208 L 324 273 L 301 273 L 301 232 L 189 232 L 189 273 L 165 273 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(217.5 213.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".4921875" y="11" textLength="50.015625">host JVM</tspan></text><g id="id4_Graphic"><path d="M 204 241 L 282 241 C 288 241 288 241 288 243.125 L 288 242.0625 C 291 242.0625 291 242.0625 291 244.1875 L 291 243.125 C 294 243.125 294 243.125 294 245.25 L 294 272.875 C 294 275 294 275 288 275 L 210 275 C 204 275 204 275 204 273.9375 C 201 273.9375 201 273.9375 201 272.875 C 198 272.875 198 272.875 198 270.75 L 198 243.125 C 198 241 198 241 204 241 Z" fill="white"/><path d="M 204 241 L 282 241 C 288 241 288 241 288 243.125 L 288 270.75 C 288 272.875 288 272.875 282 272.875 L 204 272.875 C 198 272.875 198 272.875 198 270.75 L 198 243.125 C 198 241 198 241 204 241 M 288 242.0625 C 291 242.0625 291 242.0625 291 244.1875 L 291 272.875 C 291 273.9375 291 273.9375 285 273.9375 L 207 273.9375 C 201 273.9375 201 273.9375 201 272.875 M 291 243.125 C 294 243.125 294 243.125 294 245.25 L 294 272.875 C 294 275 294 275 288 275 L 210 275 C 204 275 204 275 204 273.9375" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(204.92 242.215)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="20.029258" y="11" textLength="35.355469">native </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="15.034141" y="25" textLength="42.01172">libraries</tspan></text></g><rect x="100" y="228" width="287" height="18" fill="red" fill-opacity=".09"/><rect x="100" y="228" width="287" height="18" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id6_Graphic"><path d="M 204 167 L 282 167 C 288 167 288 167 288 169.125 L 288 168.0625 C 291 168.0625 291 168.0625 291 170.1875 L 291 169.125 C 294 169.125 294 169.125 294 171.25 L 294 198.875 C 294 201 294 201 288 201 L 210 201 C 204 201 204 201 204 199.9375 C 201 199.9375 201 199.9375 201 198.875 C 198 198.875 198 198.875 198 196.75 L 198 169.125 C 198 167 198 167 204 167 Z" fill="white"/><path d="M 204 167 L 282 167 C 288 167 288 167 288 169.125 L 288 196.75 C 288 198.875 288 198.875 282 198.875 L 204 198.875 C 198 198.875 198 198.875 198 196.75 L 198 169.125 C 198 167 198 167 204 167 M 288 168.0625 C 291 168.0625 291 168.0625 291 170.1875 L 291 198.875 C 291 199.9375 291 199.9375 285 199.9375 L 207 199.9375 C 201 199.9375 201 199.9375 201 198.875 M 291 169.125 C 294 169.125 294 169.125 294 171.25 L 294 198.875 C 294 201 294 201 288 201 L 210 201 C 204 201 204 201 204 199.9375" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(204.92 168.215)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="19.704062" y="11" textLength="36.00586">library </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="16.033164" y="25" textLength="40.013672">classes</tspan></text></g><path d="M 165 136 L 325 136 L 324 201 L 301 201 L 301 160 L 189 160 L 189 201 L 165 201 Z" fill="yellow"/><path d="M 165 136 L 325 136 L 324 201 L 301 201 L 301 160 L 189 160 L 189 201 L 165 201 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(179.5 140.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".14160156" y="11" textLength="118.7168">JPF (Java application)</tspan></text><g id="id9_Graphic"><path d="M 204 95 L 282 95 C 288 95 288 95 288 97.125 L 288 96.0625 C 291 96.0625 291 96.0625 291 98.1875 L 291 97.125 C 294 97.125 294 97.125 294 99.25 L 294 126.875 C 294 129 294 129 288 129 L 210 129 C 204 129 204 129 204 127.9375 C 201 127.9375 201 127.9375 201 126.875 C 198 126.875 198 126.875 198 124.75 L 198 97.125 C 198 95 198 95 204 95 Z" fill="white"/><path d="M 204 95 L 282 95 C 288 95 288 95 288 97.125 L 288 124.75 C 288 126.875 288 126.875 282 126.875 L 204 126.875 C 198 126.875 198 126.875 198 124.75 L 198 97.125 C 198 95 198 95 204 95 M 288 96.0625 C 291 96.0625 291 96.0625 291 98.1875 L 291 126.875 C 291 127.9375 291 127.9375 285 127.9375 L 207 127.9375 C 201 127.9375 201 127.9375 201 126.875 M 291 97.125 C 294 97.125 294 97.125 294 99.25 L 294 126.875 C 294 129 294 129 288 129 L 210 129 C 204 129 204 129 204 127.9375" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(204.92 96.215)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="13.024375" y="11" textLength="49.365234">modeled </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="16.033164" y="25" textLength="40.013672">classes</tspan></text></g><rect x="100" y="123" width="287" height="18" fill="red" fill-opacity=".2"/><rect x="100" y="123" width="287" height="18" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id11_Graphic"><path d="M 465 203.2 L 465 172.8 C 465 170.7024 475.528 169 488.5 169 C 501.472 169 512 170.7024 512 172.8 L 512 203.2 C 512 205.2976 501.472 207 488.5 207 C 475.528 207 465 205.2976 465 203.2" fill="white"/><path d="M 465 203.2 L 465 172.8 C 465 170.7024 475.528 169 488.5 169 C 501.472 169 512 170.7024 512 172.8 L 512 203.2 C 512 205.2976 501.472 207 488.5 207 C 475.528 207 465 205.2976 465 203.2 M 465 172.8 C 465 174.8976 475.528 176.6 488.5 176.6 C 501.472 176.6 512 174.8976 512 172.8" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(470 182.9)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="6.5" y="11" textLength="24">rt.jar</tspan></text></g><path d="M 464.51608 194.23311 C 443.01287 199.82151 425.91675 206.03935 400 211 C 374.08325 215.96065 330.83115 228.3329 309 224 C 291.02341 220.43213 284.5698 205.10993 275.4076 192.55293" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 464.5003 187.15195 C 425.33746 185.76811 380.41338 195.52407 347 183 C 316.59513 171.60358 295.70486 141.75243 271.34544 118.638844" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><g id="id14_Graphic"><path d="M 465 152.2 L 465 121.8 C 465 119.7024 475.528 118 488.5 118 C 501.472 118 512 119.7024 512 121.8 L 512 152.2 C 512 154.2976 501.472 156 488.5 156 C 475.528 156 465 154.2976 465 152.2" fill="white"/><path d="M 465 152.2 L 465 121.8 C 465 119.7024 475.528 118 488.5 118 C 501.472 118 512 119.7024 512 121.8 L 512 152.2 C 512 154.2976 501.472 156 488.5 156 C 475.528 156 465 154.2976 465 152.2 M 465 121.8 C 465 123.8976 475.528 125.6 488.5 125.6 C 501.472 125.6 512 123.8976 512 121.8" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><path d="M 464.51156 142.2503 C 428.34466 150.166075 387.9154 172.37441 356 166 C 327.09537 160.22692 305.15832 131.006355 280.72651 110.193924" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(104.5 227.125)" fill="red"><tspan font-family="Helvetica" font-size="14" font-weight="bold" fill="red" x=".106933594" y="14" textLength="21.786133">JNI</tspan></text><text transform="translate(104.5 122.125)" fill="red"><tspan font-family="Helvetica" font-size="14" font-weight="bold" fill="red" x=".33105469" y="14" textLength="23.33789">MJI</tspan></text><text transform="translate(470.5 131.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".328125" y="11" textLength="35.34375">*.class</tspan></text><text transform="translate(439 95.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x=".14746094" y="11" textLength="94.70508">system under test</tspan></text><text transform="translate(450.5 212.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="1.8095703" y="11" textLength="78.714844">standard Java </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="11.149414" y="25" textLength="56.701172">installation</tspan></text><text transform="translate(411.5 219.125) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="13" font-weight="500" x=".30761719" y="13" textLength="51.301758">CLASSP</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="50.65088" y="13" textLength="8.6708984">A</tspan><tspan font-family="Helvetica" font-size="13" font-weight="500" x="58.36328" y="13" textLength="17.329102">TH</tspan></text><line x1="57" y1="205" x2="366" y2="205" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="8,4,1,4"/><line x1="57" y1="132" x2="92" y2="132" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="8,4,1,4"/><line x1="138" y1="132" x2="365" y2="132" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="8,4,1,4"/><text transform="translate(54.5 75.125)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".31738281" y="14" textLength="72.365234">Model layer</tspan></text><text transform="translate(54.5 160.125)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".09667969" y="14" textLength="63.80664">Java layer</tspan></text><text transform="translate(54.5 256.125)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x=".041503906" y="14" textLength="73.91699">Native layer</tspan></text><text transform="translate(22.5 214.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x=".37890625" y="11" textLength="121.24219">&quot;Java Native Interface&quot;</tspan></text><text transform="translate(22.5 109.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x=".043945312" y="11" textLength="119.91211">&quot;Model Java Interface&quot;</tspan></text></g></g></svg>
diff --git a/doc/graphics/jpf-project.svg b/doc/graphics/jpf-project.svg
new file mode 100644 (file)
index 0000000..790b51b
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="46 25 545 750" width="545pt" height="750pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2009-09-09 23:36:00 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1200" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Lucida Grande" font-size="10" panose-1="2 11 6 0 4 5 2 2 2 4" units-per-em="1000" underline-position="-97.65625" underline-thickness="48.828125" slope="0" x-height="530.27344" cap-height="722.65625" ascent="966.7969" descent="-210.9375" font-weight="500"><font-face-src><font-face-name name="LucidaGrande"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1200" x-height="539.55078" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="bold"><font-face-src><font-face-name name="Helvetica-BoldOblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="13" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-923.0769" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="red"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1090.9091" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="588.0188" height="768.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id111_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id106_Graphic" filter="url(#Shadow)"/></g><g id="id111_Graphic"><path d="M 68.5938 640.352 L 229.3748 640.352 C 232.68851 640.352 235.3748 643.0383 235.3748 646.352 L 235.3748 751.813 C 235.3748 755.1267 232.68851 757.813 229.3748 757.813 L 68.5938 757.813 C 65.28009 757.813 62.5938 755.1267 62.5938 751.813 L 62.5938 646.352 C 62.5938 643.0383 65.28009 640.352 68.5938 640.352 Z" fill="#ebffed"/><path d="M 68.5938 640.352 L 229.3748 640.352 C 232.68851 640.352 235.3748 643.0383 235.3748 646.352 L 235.3748 751.813 C 235.3748 755.1267 232.68851 757.813 229.3748 757.813 L 68.5938 757.813 C 65.28009 757.813 62.5938 755.1267 62.5938 751.813 L 62.5938 646.352 C 62.5938 643.0383 65.28009 640.352 68.5938 640.352 Z" stroke="#9cffb1" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id106_Graphic"><path d="M 69.335548 61.38315 L 186.93809 61.38416 C 190.2518 61.384188 192.93807 64.070502 192.93804 67.38421 C 192.93804 67.38427 192.93804 67.384327 192.93804 67.384385 L 192.93781 73.569708 C 192.93769 76.88329 190.25149 79.56943 186.93791 79.569483 L 97.390935 79.57095 C 94.077265 79.571007 91.391034 82.25728 91.391034 85.57095 L 91.391034 388.09394 C 91.391034 391.39507 94.057715 394.07612 97.35879 394.09386 L 226.37644 394.78716 C 229.67752 394.8049 232.3442 397.48595 232.3442 400.78708 L 232.3442 493.42925 C 232.3442 496.73035 229.67755 499.4114 226.3765 499.42916 L 97.358877 500.1237 C 94.057834 500.14146 91.39119 502.82248 91.391176 506.12357 L 91.39106 529.77343 C 91.391047 533.08714 94.077325 535.77345 97.391034 535.77346 C 97.391053 535.77346 97.39107 535.77346 97.39109 535.77346 L 199.82096 535.77296 C 203.13467 535.77295 205.82097 538.45923 205.82099 541.77294 C 205.82099 541.773 205.82099 541.77308 205.82099 541.77315 L 205.82084 546.65464 C 205.82074 549.95652 203.1528 552.6378 199.85096 552.65438 L 96.69789 553.1722 C 93.38422 553.18885 90.71145 555.8886 90.728084 559.20226 C 90.728193 559.22398 90.72842 559.2457 90.728765 559.26742 L 91.297165 595.05585 C 91.349097 598.3257 94.01001 600.95175 97.28028 600.96055 L 155.35214 601.11666 C 158.53473 601.1252 161.15693 603.61723 161.3274 606.79526 L 161.52812 610.5372 C 161.70561 613.84616 159.16706 616.67248 155.858105 616.84997 C 155.75109 616.8557 155.64393 616.85858 155.53676 616.85858 L 67.852627 616.85907 C 64.53892 616.8591 61.852612 614.1728 61.852594 610.8591 C 61.852594 610.85363 61.8526 610.84816 61.852616 610.8427 L 63.33552 67.36678 C 63.344543 64.059455 66.02821 61.383123 69.335548 61.38315 Z" fill="#fffbe6"/><path d="M 69.335548 61.38315 L 186.93809 61.38416 C 190.2518 61.384188 192.93807 64.070502 192.93804 67.38421 C 192.93804 67.38427 192.93804 67.384327 192.93804 67.384385 L 192.93781 73.569708 C 192.93769 76.88329 190.25149 79.56943 186.93791 79.569483 L 97.390935 79.57095 C 94.077265 79.571007 91.391034 82.25728 91.391034 85.57095 L 91.391034 388.09394 C 91.391034 391.39507 94.057715 394.07612 97.35879 394.09386 L 226.37644 394.78716 C 229.67752 394.8049 232.3442 397.48595 232.3442 400.78708 L 232.3442 493.42925 C 232.3442 496.73035 229.67755 499.4114 226.3765 499.42916 L 97.358877 500.1237 C 94.057834 500.14146 91.39119 502.82248 91.391176 506.12357 L 91.39106 529.77343 C 91.391047 533.08714 94.077325 535.77345 97.391034 535.77346 C 97.391053 535.77346 97.39107 535.77346 97.39109 535.77346 L 199.82096 535.77296 C 203.13467 535.77295 205.82097 538.45923 205.82099 541.77294 C 205.82099 541.773 205.82099 541.77308 205.82099 541.77315 L 205.82084 546.65464 C 205.82074 549.95652 203.1528 552.6378 199.85096 552.65438 L 96.69789 553.1722 C 93.38422 553.18885 90.71145 555.8886 90.728084 559.20226 C 90.728193 559.22398 90.72842 559.2457 90.728765 559.26742 L 91.297165 595.05585 C 91.349097 598.3257 94.01001 600.95175 97.28028 600.96055 L 155.35214 601.11666 C 158.53473 601.1252 161.15693 603.61723 161.3274 606.79526 L 161.52812 610.5372 C 161.70561 613.84616 159.16706 616.67248 155.858105 616.84997 C 155.75109 616.8557 155.64393 616.85858 155.53676 616.85858 L 67.852627 616.85907 C 64.53892 616.8591 61.852612 614.1728 61.852594 610.8591 C 61.852594 610.85363 61.8526 610.84816 61.852616 610.8427 L 63.33552 67.36678 C 63.344543 64.059455 66.02821 61.383123 69.335548 61.38315 Z" stroke="#fffbe6" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><text transform="translate(86.8516 34.6328)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="24.438477">jpf-X</tspan></text><text transform="translate(122 91)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="16.516113">src</tspan></text><text transform="translate(122 253)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="26.27002">build</tspan></text><text transform="translate(122 226)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="43.403809">build.xml</tspan></text><text transform="translate(122 64)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="63.58838">jpf.properties</tspan></text><text transform="translate(140 397.094)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="bold" fill="blue" x="0" y="10" textLength="23.847656">X.jar</tspan></text><text transform="translate(140 415.094)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="bold" fill="blue" x="0" y="10" textLength="67.27295">X-classes.jar</tspan></text><text transform="translate(140 433.094)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="83.160645">X-annotations.jar</tspan></text><text transform="translate(149 109)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="23.842285">main</tspan></text><text transform="translate(149 127)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="27.516113">peers</tspan></text><text transform="translate(149 145)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="36.6792">classes</tspan></text><text transform="translate(149 163)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="56.879883">annotations</tspan></text><text transform="translate(149 181)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="23.22998">tests</tspan></text><text transform="translate(149 199)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="47.077637">examples</tspan></text><text transform="translate(122 460.094)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="12.831543">lib</tspan></text><text transform="translate(122 505.281)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="26.27539">tools</tspan></text><text transform="translate(122 583.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="bold" x="0" y="10" textLength="16.494629">bin</tspan></text><text transform="translate(140 538.438)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="39.735352">RunJPF</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="38.516113" y="10" textLength="15.280762">.jar</tspan></text><text transform="translate(140 556.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="51.970703">RunAnt.jar</tspan></text><text transform="translate(140 601.438)" fill="blue"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="11.617676">jpf</tspan></text><text transform="translate(140 619.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="15.291504">ant</tspan></text><text transform="translate(122 646.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="45.251465">nbproject</tspan></text><text transform="translate(140 664.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="53.1792">project.xml</tspan></text><text transform="translate(140 682.438)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="23.842285">ide-fi</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="23.842285" y="10" textLength="66.016113">le-targets.xml</tspan></text><text transform="translate(122 704.983)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="49.526855">.classpath</tspan></text><line x1="99.348286" y1="47.6328" x2="99" y2="730.842" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="130.445744" y1="104" x2="129.591" y2="206.401" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144" y1="115.48059" x2="130.350046" y2="115.465" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="133.637" x2="129.591" y2="133.674" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="151.818" x2="129.591" y2="151.855" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="170" x2="129.591" y2="170.037" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="188.182" x2="129.591" y2="188.219" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="206.364" x2="129.591" y2="206.401" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(149.091 271.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="23.842285">main</tspan></text><text transform="translate(149.091 289.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="27.516113">peers</tspan></text><text transform="translate(149.091 307.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="36.6792">classes</tspan></text><text transform="translate(149.091 325.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="56.879883">annotations</tspan></text><text transform="translate(149.091 343.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="23.22998">tests</tspan></text><text transform="translate(149.091 361.09)" fill="#626262"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#626262" x="0" y="10" textLength="47.077637">examples</tspan></text><line x1="129.596" y1="266.09" x2="129.682" y2="368.491" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.091" y1="277.5711" x2="129.60563" y2="277.555" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.182" y1="295.727" x2="129.682" y2="295.764" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.182" y1="313.908" x2="129.682" y2="313.945" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.182" y1="332.09" x2="129.682" y2="332.127" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.182" y1="350.272" x2="129.682" y2="350.309" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="144.182" y1="368.454" x2="129.682" y2="368.491" stroke="#626262" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="117.091" y1="259.597" x2="99.5542" y2="259.652" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="117.182" y1="466.795" x2="99.6451" y2="466.85" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="117.182" y1="512.177" x2="99.6451" y2="512.232" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="117.182" y1="590.608" x2="99.6451" y2="590.663" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="117.182" y1="653.926" x2="99.6451" y2="653.981" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(122.091 723.075)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="34.240723">eclipse</tspan></text><line x1="117.273" y1="730.564" x2="99.736" y2="730.619" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(140.091 741.075)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="66.649902">run-jpf.launch</tspan></text><text transform="translate(250.984 58.6875)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="105.5957">JPF project properties fi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="105.5957" y="10" textLength="7.783203">le</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="22" textLength="58.911133">(runtime def: </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="58.911133" y="22" textLength="74.492188">native_classpath</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="133.40332" y="22" textLength="5.5566406">, </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="138.95996" y="22" textLength="42.246094">classpath</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="181.20605" y="22" textLength="5.5566406">, </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="186.7627" y="22" textLength="49.47754">sourcepath</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="236.24023" y="22" textLength="3.3300781">)</tspan></text><text transform="translate(278.93 107.57)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="246.20117">host-VM executed classes (listeners, infrastructure etc.)</tspan></text><text transform="translate(279.023 125.664)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="229.52637">host-VM executed library classes (MJI native peers)</tspan></text><text transform="translate(279.117 144.258)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="135.60547">JPF executed library classes   </tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" fill="#20a356" x="135.60547" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="145.60547" y="10" textLength="49.47754">sourcepath</tspan></text><text transform="translate(279.211 161.852)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="145.08301">JPF processed Java annotations</tspan></text><text transform="translate(279.305 179.945)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="70.585938">regression tests</tspan></text><text transform="translate(279.398 198.539)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="35.57129">demos  </tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" fill="#20a356" x="35.57129" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="45.57129" y="10" textLength="49.47754">sourcepath</tspan></text><text transform="translate(251.641 225.133)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="71.694336">Ant build script (</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="red" x="71.694336" y="10" textLength="34.458008">compile</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="106.152344" y="10" textLength="5.5566406">, </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="red" x="111.708984" y="10" textLength="21.12793">build</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="132.836914" y="10" textLength="5.5566406">, </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="red" x="138.393555" y="10" textLength="16.118164">test</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="154.51172" y="10" textLength="5.5566406">, </tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="red" x="160.06836" y="10" textLength="23.90625">clean</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="183.97461" y="10" textLength="3.3300781">)</tspan></text><text transform="translate(263.414 305.969)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="140.60059">temporary build artifacts (classfi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="140.60059" y="10" textLength="16.113281">les)</tspan></text><text transform="translate(278.734 397.5)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="210.08789">main host-VM executed project jar (main,peers </tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" fill="#20a356" x="210.08789" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="220.08789" y="10" textLength="74.492188">native_classpath</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="294.58008" y="10" textLength="3.3300781">)</tspan></text><text transform="translate(278.828 415.594)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="201.75781">JPF executed library jar (classes,annotations </tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" fill="#20a356" x="201.75781" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="211.75781" y="10" textLength="42.246094">classpath</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="254.0039" y="10" textLength="3.3300781">)</tspan></text><text transform="translate(278.922 433.188)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="201.20117">separate anotations jar (for JPF external SUT</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="201.02539" y="10" textLength="27.231445"> exec)</tspan></text><text transform="translate(252.016 460.781)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="97.26074">required runtime jars  </tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" fill="#20a356" x="97.26074" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" fill="#20a356" x="107.26074" y="10" textLength="74.492188">native_classpath</tspan></text><text transform="translate(252.109 502.531)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="45.029297">build tools</tspan></text><text transform="translate(140.094 520.531)" fill="#404040"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="0" y="10" textLength="30.572266">ant.jar</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="29.970703" y="10" textLength="41.572266">, junit.jar</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="70.941406" y="10" textLength="15.280762">, ...</tspan></text><text transform="translate(140.188 477.898)" fill="#404040"><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="0" y="10" textLength="35.45996">bcel.jar</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="34.858398" y="10" textLength="27.510742">, antlr</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="61.767578" y="10" textLength="15.280762">.jar</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" fill="#404040" x="76.446777" y="10" textLength="12.2246094">, ..</tspan></text><text transform="translate(279.203 478.375)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="105.59082">(example) 3rd party jars</tspan></text><text transform="translate(250.531 253.578)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="58.916016">build artifacts</tspan></text><text transform="translate(264.836 379.477)" fill="blue"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="bold" fill="blue" x="0" y="10" textLength="119.46777">permanent build artifacts</tspan></text><text transform="translate(278.539 521.195)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="105.59082">(example) 3rd party jars</tspan></text><text transform="translate(278.539 537.391)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="122.82715">JPF startup jar (executable)</tspan></text><text transform="translate(278.633 555.484)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="113.38379">JPF build jar (executable)</tspan></text><text transform="translate(252.203 583.625)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="28.891602">scripts</tspan></text><text transform="translate(279.391 600.984)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="83.90625">JPF startup script (</tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" x="83.90625" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="93.90625" y="10" textLength="36.123047">RunJPF</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="128.9209" y="10" textLength="17.22168">.jar)</tspan></text><text transform="translate(279.485 619.077)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="74.46289">JPF build script (</tspan><tspan font-family="Lucida Grande" font-size="10" font-weight="500" x="74.46289" y="10" textLength="10">→</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="84.46289" y="10" textLength="50.576172">RunAnt.jar)</tspan></text><text transform="translate(252.391 704.602)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="88.930664">Eclipse project confi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="88.930664" y="10" textLength="36.137695">guration</tspan></text><line x1="117.185" y1="97.5308" x2="99.6479" y2="97.5858" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(250.625 91.5547)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="67.807617">project sources</tspan></text><text transform="translate(227.414 34.7109)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="91.14746">project root directory</tspan></text><text transform="translate(69.6719 415.508) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" x="0" y="13" textLength="101.88623">binary distribution</tspan></text><text transform="translate(252.484 663.562)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="100.61035">NetBeans project confi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="100.61035" y="10" textLength="36.137695">guration</tspan></text><text transform="translate(252.578 681.656)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="51.694336">NetBeans fi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="51.694336" y="10" textLength="115.04883">le actions (JPF execution)</tspan></text><text transform="translate(252.672 740.961)" fill="black"><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="0" y="10" textLength="88.38379">Eclipse launch confi</tspan><tspan font-family="Helvetica" font-size="10" font-style="italic" font-weight="500" x="88.38379" y="10" textLength="83.36426">gs (JPF execution)</tspan></text><text transform="translate(71.2344 731.805) rotate(-90)" fill="black"><tspan font-family="Helvetica" font-size="13" font-style="italic" font-weight="500" x="0" y="13" textLength="68.643555">IDE support</tspan></text><path d="M 204.516 159.898 L 215.25 159.898 C 221.87742 159.898 227.25 165.27058 227.25 171.898 L 227.25 308.86879 C 227.25 315.4962 221.87742 320.86879 215.25 320.86879 C 215.13236 320.86879 215.01472 320.86706 214.89713 320.8636 L 211.37972 320.76012" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 231.134 319.703 L 241.868 319.703 C 248.49542 319.703 253.868 325.07558 253.868 331.703 L 253.868 401.31326 C 253.868 407.94068 248.49542 413.31326 241.868 413.31326 C 241.75051 413.31326 241.63303 413.31154 241.5156 413.3081 L 237.99773 413.20473" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(212.578 256.102) rotate(-90)" fill="red"><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x="0" y="10" textLength="37.903809">compile</tspan></text><text transform="translate(239.586 371.32) rotate(-90)" fill="red"><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x="0" y="10" textLength="23.240723">build</tspan></text><path d="M 198.548 409.218 L 209.282 409.218 C 215.90942 409.218 221.282 403.84542 221.282 397.218 L 221.282 363.27672 C 221.282 356.78689 216.12235 351.473 209.63534 351.28192 L 205.41171 351.1575" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(207.5 387.266) rotate(-90)" fill="red"><tspan font-family="Helvetica" font-size="11" font-style="italic" font-weight="500" fill="red" x="0" y="10" textLength="17.72998">test</tspan></text></g></g></svg>
diff --git a/doc/graphics/listener-overview.svg b/doc/graphics/listener-overview.svg
new file mode 100644 (file)
index 0000000..8a58bd9
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="63 56 639 340" width="639pt" height="340pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-02 07:42:37 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="gray" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><filter id="Shadow_2" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in" result="color"/><feMerge><feMergeNode in="color"/><feMergeNode in="SourceGraphic"/></feMerge></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Courier" font-size="11" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Courier" font-size="11" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><font-face font-family="Apple Symbols" font-size="12" panose-1="2 0 0 0 0 0 0 0 0 0" units-per-em="1000" underline-position="-73.242188" underline-thickness="48.828125" slope="0" x-height="392.57812" cap-height="546.38672" ascent="666.5039" descent="-250" font-weight="500"><font-face-src><font-face-name name="AppleSymbols"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id25_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id26_Graphic" filter="url(#Shadow)"/></g><g filter="url(#Shadow_2)"><path d="M 107.658 161.374 L 226.991 161.374 C 229.75242 161.374 231.991 163.61258 231.991 166.374 L 231.991 337.001 C 231.991 339.76242 229.75242 342.001 226.991 342.001 L 107.658 342.001 C 104.896576 342.001 102.658 339.76242 102.658 337.001 L 102.658 166.374 C 102.658 163.61258 104.896576 161.374 107.658 161.374 Z" fill="#ffffcd"/><path d="M 107.658 161.374 L 226.991 161.374 C 229.75242 161.374 231.991 163.61258 231.991 166.374 L 231.991 337.001 C 231.991 339.76242 229.75242 342.001 226.991 342.001 L 107.658 342.001 C 104.896576 342.001 102.658 339.76242 102.658 337.001 L 102.658 166.374 C 102.658 163.61258 104.896576 161.374 107.658 161.374 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g filter="url(#Shadow_2)"><rect x="122.867" y="292.563" width="90" height="36" fill="white"/><rect x="122.867" y="292.563" width="90" height="36" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(127.867 303.563)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="19.987305" y="11" textLength="40.02539">Search</tspan></text></g><g filter="url(#Shadow_2)"><rect x="122.867" y="175.926" width="90" height="36" fill="white"/><rect x="122.867" y="175.926" width="90" height="36" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(127.867 186.926)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="31" y="11" textLength="18">VM</tspan></text></g><text transform="translate(119.191 239.52)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".49609375" y="11" textLength="22.007812">JPF</tspan></text><line x1="75.175" y1="136.649" x2="404.976" y2="136.649" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="8,4,1,4"/><rect x="106.7" y="74.525" width="121.25" height="36" fill="#ffe2dc"/><rect x="106.7" y="74.525" width="121.25" height="36" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(111.7 85.525)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="7.270508" y="11" textLength="96.708984">System under test</tspan></text><path d="M 274.98012 238.271 L 373.49575 238.271 C 381.07388 238.271 381.07388 238.271 381.07388 240.08095 L 381.07388 239.17598 C 384.86294 239.17598 384.86294 239.17598 384.86294 240.98592 L 384.86294 240.08095 C 388.652 240.08095 388.652 240.08095 388.652 241.8909 L 388.652 265.42025 C 388.652 267.2302 388.652 267.2302 381.07388 267.2302 L 282.55825 267.2302 C 274.98012 267.2302 274.98012 267.2302 274.98012 266.32522 C 271.19106 266.32522 271.19106 266.32522 271.19106 265.42025 C 267.402 265.42025 267.402 265.42025 267.402 263.6103 L 267.402 240.08095 C 267.402 238.271 267.402 238.271 274.98012 238.271 Z" fill="#bfffbe"/><path d="M 274.98012 238.271 L 373.49575 238.271 C 381.07388 238.271 381.07388 238.271 381.07388 240.08095 L 381.07388 263.6103 C 381.07388 265.42025 381.07388 265.42025 373.49575 265.42025 L 274.98012 265.42025 C 267.402 265.42025 267.402 265.42025 267.402 263.6103 L 267.402 240.08095 C 267.402 238.271 267.402 238.271 274.98012 238.271 M 381.07388 239.17598 C 384.86294 239.17598 384.86294 239.17598 384.86294 240.98592 L 384.86294 265.42025 C 384.86294 266.32522 384.86294 266.32522 377.28481 266.32522 L 278.76919 266.32522 C 271.19106 266.32522 271.19106 266.32522 271.19106 265.42025 M 384.86294 240.08095 C 388.652 240.08095 388.652 240.08095 388.652 241.8909 L 388.652 265.42025 C 388.652 267.2302 388.652 267.2302 381.07388 267.2302 L 282.55825 267.2302 C 274.98012 267.2302 274.98012 267.2302 274.98012 266.32522" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(274.827 244.23024)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="22.154688" y="11" textLength="49.359375">listeners</tspan></text><path d="M 213.367 193.9258 L 328.404 193.881 L 328.11532 227.87138" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 213.367 310.563 L 328.993 310.563 L 328.25879 277.62762" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="266.902" y1="252.75231" x2="222.76794" y2="252.90316" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="167.867" y1="292.063" x2="167.867" y2="221.826" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="167.867" y1="175.926" x2="167.50956" y2="120.92478" marker-end="url(#Arrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(283.027 113.374)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x=".31054688" y="11" textLength="89.378906">executed by JPF</tspan></text><text transform="translate(268.418 144.524)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x=".46972656" y="11" textLength="118.06055">executed by host JVM</tspan></text><text transform="translate(260.108 312.942)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="97.38867">search event notifi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="97.38867" y="11" textLength="38.021484">cations</tspan></text><text transform="translate(252.61 176.262)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="112.740234">execution event notifi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="112.740234" y="11" textLength="38.021484">cations</tspan></text><text transform="translate(402.525 247.554)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x=".48046875" y="11" textLength="25.347656">confi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="25.828125" y="11" textLength="30.691406">gured</tspan></text><text transform="translate(417.247 65.978)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="118.819336">- classLoaded (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="23" textLength="145.22363">- threadScheduled (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="36" textLength="138.62256">- threadNotified (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="49" textLength="33.005371">  ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="62" textLength="13.2021484">- </tspan><tspan font-family="Courier" font-size="11" font-weight="bold" fill="blue" x="13.2021484" y="62" textLength="118.819336">executeInstruction</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="132.021484" y="62" textLength="33.005371"> (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="75" textLength="13.2021484">- </tspan><tspan font-family="Courier" font-size="11" font-weight="bold" fill="blue" x="13.2021484" y="75" textLength="125.42041">instructionExecuted</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="138.62256" y="75" textLength="33.005371"> (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="88" textLength="33.005371">  ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="101" textLength="132.021484">- objectCreated (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="114" textLength="33.005371">  ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="127" textLength="138.62256">- exceptionThrown(vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="140" textLength="33.005371">  ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="153" textLength="198.03223">- choiceGeneratorAdvanced (vm)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="166" textLength="33.005371">  ...</tspan></text><text transform="translate(353.977 331.014)" fill="blue"><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="10" textLength="158.42578">- stateAdvanced (search)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="23" textLength="178.229">- stateBacktracked (search)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="36" textLength="178.229">- propertyViolated (search)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="49" textLength="165.02686">- searchFinished (search)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="blue" x="0" y="62" textLength="33.005371">  ...</tspan></text><text transform="translate(469.131 248.388)" fill="red"><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="10" textLength="184.83008">- +listener=&lt;listener-class&gt;</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="23" textLength="105.61719">- @JPFConfig(..)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="36" textLength="217.83545">- listener.autoload=&lt;annotations&gt;</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="49" textLength="138.62256">- jpf.addListener(..)</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="62" textLength="33.005371">  ...</tspan></text><g id="id25_Graphic"><rect x="274.264" y="283.835" width="108.986" height="20.5102" fill="white"/><rect x="274.264" y="283.835" width="108.986" height="20.5102" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(279.264 286.5901)" fill="black"><tspan font-family="Apple Symbols" font-size="12" font-weight="500" x=".5291328" y="11" textLength="8.800781">≪</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="9.329914" y="11" textLength="80.71289">SearchListener</tspan><tspan font-family="Apple Symbols" font-size="12" font-weight="500" x="90.042805" y="11" textLength="8.4140625">≫</tspan></text></g><g id="id26_Graphic"><rect x="274.432" y="200.503" width="108.986" height="20.5102" fill="white"/><rect x="274.432" y="200.503" width="108.986" height="20.5102" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(279.432 203.2581)" fill="black"><tspan font-family="Apple Symbols" font-size="12" font-weight="500" x="10.539875" y="11" textLength="8.800781">≪</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="19.340656" y="11" textLength="60.691406">VMListener</tspan><tspan font-family="Apple Symbols" font-size="12" font-weight="500" x="80.032062" y="11" textLength="8.4140625">≫</tspan></text></g></g></g></svg>
diff --git a/doc/graphics/listeners.svg b/doc/graphics/listeners.svg
new file mode 100644 (file)
index 0000000..681d1ad
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="61 72 661 400" width="661pt" height="400pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2009-04-08 19:52:28 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker_2" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="gray"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id10_Graphic" filter="url(#Shadow)"/></g><g id="id10_Graphic"><rect x="166.578" y="85.4216" width="130.422" height="29.3676" fill="white"/><rect x="166.578" y="85.4216" width="130.422" height="29.3676" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(171.578 87.1054)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="35.47223" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="25.866273" y="23" textLength="68.689453">JPFListener</tspan></text></g><rect x="72" y="197" width="171" height="98" fill="white"/><rect x="72" y="197" width="171" height="98" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 197)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="121.37695">executeInstruction(vm)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="119.384766">instructionExecuted(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="87.375">threadStarted(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="53" textLength="89.373047">objectCreated(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="67" textLength="121.38867">choiceGeneratorSet(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="81" textLength="156.75">choiceGeneratorAdvanced(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="95" textLength="16.669922">. . .</tspan></text><rect x="72" y="171" width="171" height="26" fill="white"/><rect x="72" y="171" width="171" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(77 171)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="55.76123" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="48.15918" y="23" textLength="64.68164">VMListener</tspan></text><rect x="261" y="197" width="135" height="84" fill="white"/><rect x="261" y="197" width="135" height="84" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(266 197)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="118.7168">searchStarted(search)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="94.04883">stateAdvanced(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="106.70508">stateBacktracked(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="53" textLength="52.02539">propertyV</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="51.814453" y="53" textLength="50.021484">iolated(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="67" textLength="96.035156">searchFinished(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="81" textLength="16.669922">. . .</tspan></text><rect x="261" y="171" width="135" height="26" fill="white"/><rect x="261" y="171" width="135" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(266 171)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="37.76123" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="19.146484" y="23" textLength="86.70703">SearchListener</tspan></text><rect x="594" y="197" width="117" height="28" fill="white"/><rect x="594" y="197" width="117" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(599 197)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="94.6875">check(search,vm)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="100.03125">getErrorMessage()</tspan></text><rect x="594" y="171" width="117" height="26" fill="white"/><rect x="594" y="171" width="117" height="26" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(599 171)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="28.76123" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="28.826172" y="23" textLength="49.347656">Property</tspan></text><rect x="594" y="284.737" width="117" height="28" fill="white"/><rect x="594" y="284.737" width="117" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(599 284.737)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="106.03711">check(search,vm) {}</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="16.669922">. . .</tspan></text><rect x="594" y="270.737" width="117" height="14" fill="white"/><rect x="594" y="270.737" width="117" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(599 270.737)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="6.4814453" y="11" textLength="94.03711">GenericProperty</tspan></text><rect x="180" y="373.263" width="171" height="42" fill="white"/><rect x="180" y="373.263" width="171" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(185 373.263)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="140.0625">instructionExecuted(vm) {}</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="130.06641">searchStarted(search) {}</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="16.669922">. . .</tspan></text><rect x="180" y="359.263" width="171" height="14" fill="white"/><rect x="180" y="359.263" width="171" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(185 359.263)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="34.489258" y="11" textLength="92.021484">ListenerAdapter</tspan></text><rect x="425.211" y="196.631" width="132.789" height="42" fill="white"/><rect x="425.211" y="196.631" width="132.789" height="42" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(430.211 196.631)" fill="#404040"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="#404040" x="0" y="11" textLength="120.058594">publishStart(publisher)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="#404040" x="0" y="25" textLength="98.04492">publishFinished(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="#404040" x="0" y="39" textLength="16.669922">. . .</tspan></text><rect x="425.211" y="170.631" width="132.789" height="26" fill="white"/><rect x="425.211" y="170.631" width="132.789" height="26" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(430.211 170.631)" fill="#404040"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="#404040" x="36.65573" y="10" textLength="49.47754">«interface»</tspan><tspan font-family="Helvetica" font-size="12" font-weight="bold" fill="#404040" x="5.3818047" y="23" textLength="112.02539">PublisherExtension</tspan></text><rect x="477" y="419" width="153" height="42" fill="white"/><rect x="477" y="419" width="153" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(482 419)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="140.0625">instructionExecuted(vm) {}</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="130.06641">searchStarted(search) {}</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="16.669922">. . .</tspan></text><rect x="477" y="405" width="153" height="14" fill="white"/><rect x="477" y="405" width="153" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(482 405)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".8154297" y="11" textLength="141.36914">PropertyListenerAdapter</tspan></text><line x1="652.5" y1="270.737" x2="652.5" y2="238.5" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="198" y1="360" x2="198" y2="310.5" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="306" y1="362.211" x2="306" y2="294.711" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="189" y1="169.526" x2="189" y2="129.026" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="279" y1="169.526" x2="279" y2="129.026" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 342 360 L 342 333 L 445.578 333 L 445.578 252.722" marker-end="url(#UMLInheritance_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="540" y1="405" x2="540" y2="252.722" marker-end="url(#UMLInheritance_Marker_2)" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 513 405 L 513 306 L 333 306 L 332.68563 294.70577" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="612" y1="405" x2="612" y2="328.5" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 495 405 L 495 324 L 225 324 L 224.78847 310.49834" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/mji-call.svg b/doc/graphics/mji-call.svg
new file mode 100644 (file)
index 0000000..d5db418
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="13 12 553 462" width="553pt" height="462pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-24 04:08:50 +0000</dc:date></metadata><defs><font-face font-family="Monaco" font-size="12" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><font-face font-family="Monaco" font-size="9" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-857.14286" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="588.0188" height="768.0188"/><g><title>Layer 1</title><text transform="translate(332 21.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="57.609375">package </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="57.609375" y="12" textLength="36.00586">x.y.z</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="93.615234" y="12" textLength="7.201172">;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="43.20703">class </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="43.20703" y="28" textLength="7.201172">C</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="50.408203" y="28" textLength="14.402344"> {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="36.00586">  ...</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="14.402344">  </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="14.402344" y="60" textLength="158.42578">native int foo (int p)</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="172.82812" y="60" textLength="7.201172">;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="7.201172">}</tspan></text><text transform="translate(84 352.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="43.20703">class </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="43.20703" y="12" textLength="79.21289">JPF_x_y_z_C</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="122.41992" y="12" textLength="14.402344"> {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="36.00586">  ...</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="14.402344">  </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="14.402344" y="44" textLength="432.0703">public static int foo__I__I (MJIEnv env, int thisRef, int p)</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="446.47266" y="44" textLength="14.402344"> {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="324.05273">    int d = env.getIntField(thisRef, &quot;data&quot;);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="43.20703">    ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="92" textLength="21.603516">  }</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="108" textLength="7.201172">}</tspan></text><text transform="translate(157 51.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="21.603516">...</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="122.41992">int a = c.foo(3);</tspan></text><text transform="translate(100 82.125)" fill="black"><tspan font-family="Monaco" font-size="9" font-weight="500" x="0" y="9" textLength="16.202637">...</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" x="0" y="21" textLength="37.806152">aload_1</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" x="0" y="33" textLength="37.806152">icont_3</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" x="0" y="45" textLength="86.41406">invokevirtual ..</tspan></text><text transform="translate(39 215.125)" fill="gray"><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="12" textLength="230.4375">executeMethod (ThreadInfo ti..){</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="28" textLength="216.03516">  MJIEnv env = ti.getMJIEnv();</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="44" textLength="237.63867">  Object[] args = getArguments();</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="60" textLength="28.804688">  ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="76" textLength="201.63281">  mth.invoke(peerCls, args);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="92" textLength="28.804688">  ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="108" textLength="7.201172">}</tspan></text><text transform="translate(328 198.125)" fill="gray"><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="12" textLength="108.01758">ClassInfo (..){</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="28" textLength="223.23633">  peerCls = loadNativePeer(..);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="44" textLength="28.804688">  ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="0" y="60" textLength="7.201172">}</tspan></text><rect x="314.25" y="168.5" width="103.75" height="14" fill="white"/><rect x="314.25" y="168.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(319.25 168.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="90.046875">executeMethod()</tspan></text><rect x="314.25" y="154.5" width="103.75" height="14" fill="white"/><rect x="314.25" y="154.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(319.25 154.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="41.34961">peerCls</tspan></text><rect x="314.25" y="140.5" width="103.75" height="14" fill="white"/><rect x="314.25" y="140.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(319.25 140.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="19.869141" y="11" textLength="54.01172">ClassInfo</tspan></text><rect x="186.25" y="187.5" width="103.75" height="14" fill="white"/><rect x="186.25" y="187.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(191.25 187.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="90.046875">executeMethod()</tspan></text><rect x="186.25" y="173.5" width="103.75" height="14" fill="white"/><rect x="186.25" y="173.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(191.25 173.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="46.02539">methods</tspan></text><rect x="186.25" y="159.5" width="103.75" height="14" fill="white"/><rect x="186.25" y="159.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(191.25 159.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="15.855469" y="11" textLength="62.039062">NativePeer</tspan></text><path d="M 323 53 C 308.0015 50.3336 291.9986 45.9999 278 45 C 264.0014 44.0001 250.6655 44.0003 239 47 C 230.64732 49.147832 223.66006 53.688744 216.57229 58.05222" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 175 89 C 170.0005 93.3329 164.6662 96.83385 160 102 C 157.38073 104.899905 154.97109 108.325474 152.59469 111.83394" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 139 136 C 127.0012 148.9987 119.3317 161.83465 103 175 C 86.6683 188.16535 53.49875 203.50115 41 215 C 28.50125 226.49885 25.50025 234.001 28 244 C 29.9417 251.7668 40.130086 259.93767 48.955984 268.04108" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 88 300 C 94.3327 309.3324 111.49955 319.6675 107 328 C 102.50045 336.3325 67.9993 342.3341 61 350 C 54.0007 357.6659 60.16715 366.83405 65 374 C 68.37733 379.00777 75.174067 383.2026 81.805417 387.43634" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="265.25" y="295.5" width="103.75" height="42" fill="white"/><rect x="265.25" y="295.5" width="103.75" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(270.25 295.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="65.35547">getXField(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="64.68164">setXField(..)</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="10.001953">...</tspan></text><rect x="265.25" y="281.5" width="103.75" height="14" fill="white"/><rect x="265.25" y="281.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(270.25 281.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="54.041016">threadInfo</tspan></text><rect x="265.25" y="267.5" width="103.75" height="14" fill="white"/><rect x="265.25" y="267.5" width="103.75" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(270.25 267.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="25.86914" y="11" textLength="42.01172">MJIEnv</tspan></text><rect x="393.25" y="266.5" width="90" height="14" fill="white"/><rect x="393.25" y="266.5" width="90" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(398.25 266.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="19.347656">env</tspan></text><rect x="393.25" y="252.5" width="90" height="14" fill="white"/><rect x="393.25" y="252.5" width="90" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(398.25 252.5)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="9.0009766" y="11" textLength="61.998047">ThreadInfo</tspan></text><line x1="394" y1="275" x2="378.9" y2="275" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="314" y1="163" x2="298.9" y2="163" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 490 376 C 500.3323 365.0011 512.6675 354.9988 521 343 C 529.3325 331.0012 538.3335 317.332 540 304 C 541.6665 290.668 535.6662 273.9989 531 263 C 527.83756 255.54567 523.1433 250.53967 518.4748 245.49341" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 441 89 C 458.3316 96.9992 481.3345 102.6677 493 113 C 504.6655 123.3323 510.0001 135.16825 511 151 C 511.83488 164.21889 505.69802 181.85666 501.32146 198.37727" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(482 115.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="10.555176" y="14" textLength="24.889648">JPF</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="7.051758" y="31" textLength="31.896484">class</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".4243164" y="48" textLength="45.151367">loading</tspan></text><text transform="translate(28 157.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="19.555176" y="14" textLength="24.889648">JPF</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="8.6518555" y="31" textLength="46.69629">method</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".4794922" y="48" textLength="63.041016">invocation</tspan></text><path d="M 422 412 C 431.3324 409.6669 443.50065 409.16625 450 405 C 456.49935 400.83375 462.3332 393.49935 461 387 C 459.6668 380.50065 455.6653 373.9992 442 366 C 430.2377 359.11475 407.60562 350.7459 388.13989 342.8098" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(400 317.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="9.555176" y="14" textLength="24.889648">JPF</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="3.3208008" y="31" textLength="37.358398">object</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".21386719" y="48" textLength="43.572266">access</tspan></text><text transform="translate(82 22.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".4897461" y="14" textLength="112.02051">JPF (model) class</tspan></text><text transform="translate(307 445.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".21386719" y="14" textLength="106.572266">JVM (Java) class</tspan></text><line x1="24" y1="133" x2="541" y2="133" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="10,3,2,3"/><text transform="translate(109 301.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".41748047" y="14" textLength="52.910156">Java refl</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="53.327637" y="14" textLength="62.254883">ection call</tspan></text><text transform="translate(488 310.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".09667969" y="14" textLength="19.448242">refl</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="19.544922" y="14" textLength="37.358398">ection</tspan></text></g></g></svg>
diff --git a/doc/graphics/mji-functions.svg b/doc/graphics/mji-functions.svg
new file mode 100644 (file)
index 0000000..bac9b5c
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="20 17 583 414" width="583pt" height="414pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-24 04:01:26 +0000</dc:date></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker" viewBox="-6 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M -5 0 L 3 3 L 0 0 L 0 0 L 3 -3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="SharpArrow_Marker_2" viewBox="-4 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 5 0 L -3 -3 L 0 0 L 0 0 L -3 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Monaco" font-size="12" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-857.14286" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><rect x="57" y="164" width="451" height="55" fill="red" fill-opacity=".11"/><rect x="57" y="164" width="451" height="55" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="288" y1="152.9" x2="288" y2="235" marker-start="url(#SharpArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="180" y1="155" x2="180" y2="228.1" marker-end="url(#SharpArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="37" y1="192" x2="540" y2="192" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" stroke-dasharray="10,3,2,3"/><text transform="translate(34 259.494)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="43.20703">class </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="43.20703" y="12" textLength="122.41992">JPF_x_y_z_MyClass</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="165.62695" y="12" textLength="14.402344"> {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="144.02344">  public static int </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="43.20703">      </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="43.20703" y="44" textLength="331.2539">foo__ILjava_lang_String_2__Ljava_lang_String_2</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="374.46094" y="44" textLength="180.0293"> (MJIEnv env, int objRef,</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="518.48438">                                                      int i, int sRef) {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="295.24805">    String s = env.getStringObject(sRef);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="92" textLength="43.20703">    ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="108" textLength="230.4375">    int ref = env.newString(..);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="124" textLength="108.01758">    return ref;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="140" textLength="21.603516">  }</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="156" textLength="7.201172">}</tspan></text><text transform="translate(83 31.125)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="12" textLength="57.609375">package </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="57.609375" y="12" textLength="36.00586">x.y.z</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="93.615234" y="12" textLength="7.201172">;</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="28" textLength="43.20703">class </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="43.20703" y="28" textLength="50.408203">MyClass</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="93.615234" y="28" textLength="14.402344"> {</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="44" textLength="28.804688">  ..</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="60" textLength="115.21875">  native String </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="115.21875" y="60" textLength="21.603516">foo</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="136.822266" y="60" textLength="14.402344"> (</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="151.22461" y="60" textLength="21.603516">int</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="172.82812" y="60" textLength="28.804688"> i, </tspan><tspan font-family="Monaco" font-size="12" font-weight="500" fill="red" x="201.63281" y="60" textLength="43.20703">String</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="244.83984" y="60" textLength="28.804688"> s);</tspan><tspan font-family="Monaco" font-size="12" font-weight="500" x="0" y="76" textLength="7.201172">}</tspan></text><path d="M 252 174 L 321 174 C 323.76142 174 326 176.23858 326 179 L 326 203 C 326 205.76142 323.76142 208 321 208 L 252 208 C 249.23858 208 247 205.76142 247 203 L 247 179 C 247 176.23858 249.23858 174 252 174 Z" fill="white"/><path d="M 252 174 L 321 174 C 323.76142 174 326 176.23858 326 179 L 326 203 C 326 205.76142 323.76142 208 321 208 L 252 208 C 249.23858 208 247 205.76142 247 203 L 247 179 C 247 176.23858 249.23858 174 252 174 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(252 183)" fill="black"><tspan font-family="Monaco" font-size="12" font-weight="500" x="12.896484" y="12" textLength="43.20703">MJIEnv</tspan></text><text transform="translate(351 169.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".43115234" y="14" textLength="73.137695">JPF objects</tspan></text><text transform="translate(349 195.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".08984375" y="14" textLength="77.820312">Java objects</tspan></text><path d="M 143 174 L 222 174 C 224.76142 174 227 176.23858 227 179 L 227 203 C 227 205.76142 224.76142 208 222 208 L 143 208 C 140.23858 208 138 205.76142 138 203 L 138 179 C 138 176.23858 140.23858 174 143 174 Z" fill="white"/><path d="M 143 174 L 222 174 C 224.76142 174 227 176.23858 227 179 L 227 203 C 227 205.76142 224.76142 208 222 208 L 143 208 C 140.23858 208 138 205.76142 138 203 L 138 179 C 138 176.23858 140.23858 174 143 174 Z" stroke="gray" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(143 183)" fill="gray"><tspan font-family="Monaco" font-size="12" font-weight="500" fill="gray" x="3.4941406" y="12" textLength="72.01172">NativePeer</tspan></text><text transform="translate(363 46.2916)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".20019531" y="14" textLength="85.59961">JPF executed</tspan></text><text transform="translate(388 380.062)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".46923828" y="14" textLength="112.061523">host VM executed</tspan></text><text transform="translate(110 116.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="86.05078">- method lookup</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="25" textLength="123.38672">- parameter conversion</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="39" textLength="61.365234">- invocation</tspan></text><text transform="translate(299 225.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="13.330078">- fi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="13.330078" y="11" textLength="56.695312">eld access</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="25" textLength="100.716797">- object conversion</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="39" textLength="119.35547">- JPF intrinsics access</tspan></text><text transform="translate(257 26.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".0166015625" y="14" textLength="4.9697266">&quot;</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="4.986328" y="14" textLength="38.13086">Model</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="43.117188" y="14" textLength="43.86621">&quot; Class</tspan></text><text transform="translate(248 404.125)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x=".45458984" y="14" textLength="4.9697266">&quot;</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="5.4243164" y="14" textLength="69.254883">NativePeer</tspan><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="blue" x="74.6792" y="14" textLength="43.86621">&quot; Class</tspan></text><text transform="translate(336 146.125)" fill="red"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" fill="red" x=".0546875" y="14" textLength="174.89062">MJI - &quot;Model Java Interface&quot;</tspan></text></g></g></svg>
diff --git a/doc/graphics/mji-mangling.svg b/doc/graphics/mji-mangling.svg
new file mode 100644 (file)
index 0000000..7e59270
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="62 86 481 369" width="481pt" height="369pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-02 21:54:02 +0000</dc:date></metadata><defs><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="blue"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="red"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="fuchsia"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_4" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="#ff8000"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-857.14286" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="576" height="734"/><g><title>Layer 1</title><text transform="translate(76 177.125)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="11" textLength="57.609375">package </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="57.609375" y="11" textLength="21.603516">x.y</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="79.21289" y="11" textLength="7.201172">;</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="39" textLength="43.20703">class </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="red" x="43.20703" y="39" textLength="50.408203">MyClass</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="93.615234" y="39" textLength="14.402344"> {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="53" textLength="93.615234">  native int </tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="67" textLength="36.00586">     </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="purple" x="36.00586" y="67" textLength="21.603516">foo</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="57.609375" y="67" textLength="14.402344"> (</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="#ff8000" x="72.01172" y="67" textLength="108.01758">int i, String s</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="180.0293" y="67" textLength="14.402344">);</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="81" textLength="7.201172">}</tspan></text><text transform="translate(309 191.125)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="11" textLength="72.01172">class JPF_</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="72.01172" y="11" textLength="21.603516">x_y</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="93.615234" y="11" textLength="7.201172">_</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="red" x="100.816406" y="11" textLength="50.408203">MyClass</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="151.22461" y="11" textLength="14.402344"> {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="39" textLength="144.02344">  public static int </tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="53" textLength="28.804688">    </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="purple" x="28.804688" y="53" textLength="21.603516">foo</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="50.408203" y="53" textLength="165.62695">__ILjava_lang_String_2 </tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="67" textLength="208.83398">     (MJIEnv env, int objref,</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="81" textLength="43.20703">      </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="#ff8000" x="43.20703" y="81" textLength="108.01758">int i, int sRef</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="151.22461" y="81" textLength="7.201172">)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="95" textLength="57.609375">    {..}</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="109" textLength="7.201172">}</tspan></text><path d="M 148 176 C 168.6646 166.6676 188.16885 153.6661 210 148 C 231.83115 142.3339 256.83555 139.83355 279 142 C 301.16445 144.16645 325.0018 153.0008 343 161 C 357.56026 167.47123 367.7608 176.12565 378.89088 184.31325" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 161 202 C 170.6657 191.0011 177.3346 179.49895 190 169 C 202.6654 158.50105 220.0017 145.83265 237 139 C 253.9983 132.16735 274.50175 128.3333 292 128 C 309.49825 127.6667 325.6683 132.0005 342 137 C 358.3317 141.9995 375.83475 149.83415 390 158 C 401.11917 164.40987 409.98157 172.25904 419.24353 179.85215" marker-end="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 126 250 C 149.6643 256.666 172.6691 267.0003 197 270 C 221.3309 272.9997 249.83555 272.16625 272 268 C 290.619 264.50019 305.24373 256.05959 320.90548 248.90456" marker-end="url(#FilledArrow_Marker_3)" stroke="fuchsia" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 196 254 C 211.3318 260.3327 226.0016 268.83375 242 273 C 257.9984 277.16625 275.0017 279.49995 292 279 C 305.83104 278.5932 320.10685 274.87584 334.28319 271.88872" marker-end="url(#FilledArrow_Marker_4)" stroke="#ff8000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><line x1="288" y1="97" x2="288" y2="331" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="16,9,1,9"/><text transform="translate(144 103.125)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="0" y="14" textLength="77.027344">Model Class</tspan></text><text transform="translate(359 103.125)" fill="black"><tspan font-family="Helvetica" font-size="14" font-style="italic" font-weight="500" x="0" y="14" textLength="112.041016">Native Peer Class</tspan></text><text transform="translate(332 310.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="96.708984">model parameters</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="25" textLength="94.61133">(refs become &apos;int&apos;)</tspan></text><text transform="translate(384 346.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="83.35547">MJI parameters</tspan></text><path d="M 452 279 L 452 318 L 437 318" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 491 269 L 491 354 L 476 354" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 532 248 L 532 390 L 517 390" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(372 382.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="133.39453">JNI conformant mangling</tspan></text><text transform="translate(145 319.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x=".29101562" y="11" textLength="42.708984">boolean</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="20.318359" y="25" textLength="22.68164">byte</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="19.65625" y="39" textLength="23.34375">char</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="16.322266" y="53" textLength="26.677734">short</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="30.326172" y="67" textLength="12.673828">int</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="20.3125" y="81" textLength="22.6875">long</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="20.318359" y="95" textLength="6">fl</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="26.31836" y="95" textLength="16.681641">oat</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="6.9648438" y="109" textLength="36.035156">double</tspan></text><text transform="translate(201 320.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="7.330078">Z</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="8.0039062">B</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="8.6660156">C</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="53" textLength="8.0039062">S</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="67" textLength="3.3339844">I</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="81" textLength="6">J</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="95" textLength="7.330078">F</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="109" textLength="8.6660156">D</tspan></text><text transform="translate(257 336.125)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="21.603516">&apos;_&apos;</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="21.603516">&apos;;&apos;</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="39" textLength="21.603516">&apos;[&apos;</tspan></text><text transform="translate(291 337.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="13.347656">_1</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="13.347656">_2</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="13.347656">_3</tspan></text><text transform="translate(224 383.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="36.697266">&lt;type&gt;</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="36.697266" y="11" textLength="3.3339844"> </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="40.03125" y="11" textLength="14.402344">[]</tspan></text><text transform="translate(289 383.125)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="11" textLength="7.201172">[</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="7.201172" y="11" textLength="36.697266">&lt;type&gt;</tspan></text><text transform="translate(250 400.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="15.333984">x.y</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="14.449219" y="11" textLength="10.6640625">.Z</tspan></text><text transform="translate(291 400.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="52.69922">Lx_y_Z_2</tspan></text><text transform="translate(224 417.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="0" y="11" textLength="36.697266">&lt;func&gt;</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="36.697266" y="11" textLength="17.994141"> (..)</tspan></text><text transform="translate(291 417.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="36.697266">&lt;func&gt;</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="36.697266" y="11" textLength="14.402344">__</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="51.09961" y="11" textLength="63.38086">&lt;signature&gt;</tspan></text></g></g></svg>
diff --git a/doc/graphics/new-testing.svg b/doc/graphics/new-testing.svg
new file mode 100644 (file)
index 0000000..4254ad1
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="10 15 718 319" width="718pt" height="319pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-05 00:34:03 +0000</dc:date></metadata><defs><font-face font-family="Courier" font-size="10" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><font-face font-family="Courier" font-size="10" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="756" height="553"/><g><title>Layer 1</title><text transform="translate(339.4 252.055)" fill="blue"><tspan font-family="Courier" font-size="10" font-weight="bold" fill="blue" x="0" y="10" textLength="30.004883">@Test</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="30.004883" y="10" textLength="78.012695"> public void </tspan><tspan font-family="Courier" font-size="10" font-weight="bold" fill="blue" x="108.01758" y="10" textLength="30.004883">testX</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="138.02246" y="10" textLength="24.003906">() {</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="22" textLength="216.03516">  if (verifyNoPropertyViolation()) {</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="34" textLength="24.003906">    </tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="red" x="24.003906" y="34" textLength="156.02539">String s = &quot;one&quot; + &quot; two&quot;;</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="red" x="0" y="46" textLength="186.03027">    assert &quot;one two&quot;.equals(s);</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="58" textLength="18.00293">  }</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="70" textLength="6.0009766">}</tspan></text><path d="M 361.58823 277.92 L 528.48 277.92 L 528.48 299.92 L 361.58823 299.92 Z" stroke="#fd7673" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 196.644 234.841 L 314.109 234.841 C 317.4227 234.841 320.109 237.52729 320.109 240.841 L 320.109 307.503 C 320.109 310.8167 317.4227 313.503 314.109 313.503 L 196.644 313.503 C 193.33029 313.503 190.644 310.8167 190.644 307.503 L 190.644 240.841 C 190.644 237.52729 193.33029 234.841 196.644 234.841 Z" fill="#e9ffe4"/><path d="M 196.644 234.841 L 314.109 234.841 C 317.4227 234.841 320.109 237.52729 320.109 240.841 L 320.109 307.503 C 320.109 310.8167 317.4227 313.503 314.109 313.503 L 196.644 313.503 C 193.33029 313.503 190.644 310.8167 190.644 307.503 L 190.644 240.841 C 190.644 237.52729 193.33029 234.841 196.644 234.841 Z" stroke="#aeffa8" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 85.6174 26.126 L 364.9384 26.126 C 368.2521 26.126 370.9384 28.812292 370.9384 32.126 L 370.9384 199.694 C 370.9384 203.00771 368.2521 205.694 364.9384 205.694 L 85.6174 205.694 C 82.30369 205.694 79.6174 203.00771 79.6174 199.694 L 79.6174 32.126 C 79.6174 28.812292 82.30369 26.126 85.6174 26.126 Z" fill="#fff8e0"/><path d="M 85.6174 26.126 L 364.9384 26.126 C 368.2521 26.126 370.9384 28.812292 370.9384 32.126 L 370.9384 199.694 C 370.9384 203.00771 368.2521 205.694 364.9384 205.694 L 85.6174 205.694 C 82.30369 205.694 79.6174 203.00771 79.6174 199.694 L 79.6174 32.126 C 79.6174 28.812292 82.30369 26.126 85.6174 26.126 Z" stroke="#ffcf9c" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(382.995 68.2183)" fill="blue"><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="10" textLength="48.007812">boolean </tspan><tspan font-family="Courier" font-size="10" font-weight="bold" fill="blue" x="48.007812" y="10" textLength="150.02441">verifyNoPropertyViolation</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="198.03223" y="10" textLength="132.021484"> (String... jpfArgs) {</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="22" textLength="30.004883">  ...</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="34" textLength="282.0459">  args = append(jpfArgs, caller.getClassName(),</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="46" textLength="294.04785">                         caller.getMethodName());</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="58" textLength="36.00586">    ..</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="70" textLength="216.03516">    JPF jpf = createAndRunJPF(args);</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="82" textLength="42.006836">    ...</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="94" textLength="210.03418">    errors = jpf.getSearchErrors();</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="106" textLength="156.02539">    if (!errors.isEmpty())</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="118" textLength="210.03418">      throw new AssertionError(..);</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="130" textLength="42.006836">    ...</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="142" textLength="276.04492">  return false; // -&gt; don&apos;t execute test block</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="154" textLength="6.0009766">}</tspan></text><text transform="translate(24.5953 63.4089)" fill="blue"><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="10" textLength="48.007812">boolean </tspan><tspan font-family="Courier" font-size="10" font-weight="bold" fill="blue" x="48.007812" y="10" textLength="150.02441">verifyNoPropertyViolation</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="198.03223" y="10" textLength="30.004883">(..){</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="22" textLength="84.01367">  return true;</tspan><tspan font-family="Courier" font-size="10" font-weight="500" fill="blue" x="0" y="34" textLength="6.0009766">}</tspan></text><rect x="251.28" y="45.6337" width="90" height="11" fill="white"/><rect x="251.28" y="45.6337" width="90" height="11" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(256.28 45.6337)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="23.497559">start()</tspan></text><rect x="251.28" y="34.6337" width="90" height="11" fill="white"/><rect x="251.28" y="34.6337" width="90" height="11" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(256.28 34.6337)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="bold" x="20.993652" y="9" textLength="38.012695">JPFShell</tspan></text><rect x="231.548" y="95.3416" width="129.465" height="88" fill="white"/><rect x="231.548" y="95.3416" width="129.465" height="88" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(236.548 95.3416)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="18.505371">runT</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="17.507812" y="9" textLength="71.507812">estsOfThisClass()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="20" textLength="79.532227">createAndRunJPF()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="31" textLength="7.501465">...</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="42" textLength="73.023926">verifyNoPropertyV</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="72.865723" y="42" textLength="34.514648">iolation()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="53" textLength="61.519043">verifyPropertyV</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="61.36084" y="53" textLength="34.514648">iolation()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="64" textLength="110.55762">verifyUnhandledException()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="75" textLength="85.012207">verifyAssertionError()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="86" textLength="7.501465">...</tspan></text><rect x="231.548" y="84.3416" width="129.465" height="11" fill="white"/><rect x="231.548" y="84.3416" width="129.465" height="11" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(236.548 84.3416)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="bold" x="42.558672" y="9" textLength="5.4975586">T</tspan><tspan font-family="Helvetica" font-size="9" font-weight="bold" x="47.392656" y="9" textLength="29.513672">estJPF</tspan></text><rect x="208.904" y="253.802" width="90" height="22" fill="white"/><rect x="208.904" y="253.802" width="90" height="22" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(213.904 253.802)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="26.503418">testX()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="20" textLength="7.501465">...</tspan></text><rect x="208.904" y="242.802" width="90" height="11" fill="white"/><rect x="208.904" y="242.802" width="90" height="11" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(213.904 242.802)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="bold" x="1.3193359" y="9" textLength="65.01709">MyJPFProjectT</tspan><tspan font-family="Helvetica" font-size="9" font-weight="bold" x="65.67285" y="9" textLength="13.0078125">est</tspan></text><line x1="296.28021" y1="84.3416" x2="296.28013" y2="70.1337" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="253.904" y1="242.802" x2="253.904" y2="196.842" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(96.2759 34.6337)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x=".32763672" y="10" textLength="33.344727">jpf-core</tspan></text><text transform="translate(202.017 293.506)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x=".20458984" y="10" textLength="80.59082">tested JPF project</tspan></text><rect x="87.5524" y="117.184" width="129.465" height="44" fill="white"/><rect x="87.5524" y="117.184" width="129.465" height="44" stroke="#4f4f4f" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(92.5524 117.184)" fill="black"><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="9" textLength="73.023926">verifyNoPropertyV</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="72.865723" y="9" textLength="34.514648">iolation()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="20" textLength="61.519043">verifyPropertyV</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="61.36084" y="20" textLength="34.514648">iolation()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="31" textLength="110.55762">verifyUnhandledException()</tspan><tspan font-family="Helvetica" font-size="9" font-weight="500" x="0" y="42" textLength="85.012207">verifyAssertionError()</tspan></text><rect x="87.5524" y="106.184" width="129.465" height="11" fill="white"/><rect x="87.5524" y="106.184" width="129.465" height="11" stroke="#4f4f4f" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(92.5524 106.184)" fill="#4f4f4f"><tspan font-family="Helvetica" font-size="9" font-weight="bold" fill="#4f4f4f" x="26.799883" y="9" textLength="37.015137">JPF_.._T</tspan><tspan font-family="Helvetica" font-size="9" font-weight="bold" fill="#4f4f4f" x="63.151445" y="9" textLength="29.513672">estJPF</tspan></text><line x1="217.0174" y1="133.75498" x2="231.548" y2="133.77092" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(127.285 89.2183)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x=".26123047" y="10" textLength="49.47754">native peer</tspan></text><text transform="translate(538.274 280.529)" fill="red"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="red" x=".20214844" y="10" textLength="45.576172">code verifi</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="red" x="45.77832" y="10" textLength="45.01953">ed by JPF</tspan></text><text transform="translate(39.189 166.149)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="121.40039">executed by JPF when</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="25" textLength="62.70703">running test</tspan></text><text transform="translate(355.357 34.6337)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="11" textLength="118.734375">executed outside JPF </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x="0" y="25" textLength="111.36914">(starting JPF on test)</tspan></text></g></g></svg>
diff --git a/doc/graphics/por-mark.svg b/doc/graphics/por-mark.svg
new file mode 100644 (file)
index 0000000..f47b060
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="109 71 356 231" width="356pt" height="231pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-12-02 07:35:36 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="blue"><g><path d="M 5.5 0 L 0 -2.0625 L 0 2.0625 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="black"><g><path d="M 5.5 0 L 0 -2.0625 L 0 2.0625 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-1 -4 8 8" markerWidth="8" markerHeight="8" color="red"><g><path d="M 5.5 0 L 0 -2.0625 L 0 2.0625 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="576" height="734"/><g><title>Layer 1</title><g><xl:use xl:href="#id30_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id18_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id20_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id21_Graphic" filter="url(#Shadow)"/></g><path d="M 143.59697 197.31011 C 114.34375 186.125 126.0092 91.96476 172.67462 108.03125 C 177.00414 76.712532 231.26975 81.795915 230.91499 108.03125 C 264.94118 74.475928 308.42462 141.38457 279.25828 174.93989 C 314.25644 191.20838 278.81664 278.8608 250.09375 264.21875 C 247.79505 288.62357 196.44716 297.1639 191.94026 264.21875 C 162.86442 299.40259 102.23666 245.30548 143.59697 197.31011 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id30_Graphic"><circle cx="192" cy="105" r="9.0000144" fill="white"/><circle cx="192" cy="105" r="9.0000144" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><circle cx="191" cy="157" r="9.0000144" fill="white"/><circle cx="191" cy="157" r="9.0000144" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(188.8 150)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="8.0039062">X</tspan></text><circle cx="238" cy="163" r="9.0000144" fill="white"/><circle cx="238" cy="163" r="9.0000144" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(235.8 156)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="8.0039062">X</tspan></text><circle cx="193" cy="215" r="9.0000144" fill="white"/><circle cx="193" cy="215" r="9.0000144" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(190.8 208)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="6.673828">1</tspan></text><circle cx="215" cy="243" r="9.0000144" fill="white"/><circle cx="215" cy="243" r="9.0000144" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(212.8 236)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="8.0039062">X</tspan></text><circle cx="178" cy="258" r="9.0000144" fill="white"/><circle cx="178" cy="258" r="9.0000144" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(175.8 251)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="8.0039062">X</tspan></text><line x1="206.19382" y1="246.57008" x2="193.66406" y2="251.64973" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="230" cy="199" r="9.0000144" fill="white"/><circle cx="230" cy="199" r="9.0000144" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(227.8 192)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="6.673828">1</tspan></text><circle cx="251" cy="227" r="9.0000144" fill="white"/><circle cx="251" cy="227" r="9.0000144" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(248.8 220)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="6.673828">2</tspan></text><line x1="221.27816" y1="202.77161" x2="208.51399" y2="208.29126" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="198.86986" y1="222.47071" x2="204.55829" y2="229.71052" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="242.31658" y1="230.85931" x2="230.44564" y2="236.1353" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="224" cy="130" r="9.0000144" fill="white"/><circle cx="224" cy="130" r="9.0000144" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(221.8 123)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="0" y="11" textLength="8.0039062">X</tspan></text><line x1="199.48694" y1="110.849185" x2="210.68166" y2="119.595074" marker-end="url(#FilledArrow_Marker_2)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="216.64689" y1="136.01617" x2="204.08037" y2="146.29784" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="227.71114" y1="138.747725" x2="231.39875" y2="147.43998" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id18_Graphic"><rect x="342.5" y="153.125" width="65" height="13" fill="white"/><rect x="342.5" y="153.125" width="65" height="13" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(347.5 153.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".29541016" y="10" textLength="34.22998">static fi</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="34.52539" y="10" textLength="20.179199">elds</tspan></text></g><path d="M 330.0725 188.69105 C 307.04069 182.125 316.2252 126.84966 352.9661 136.28125 C 356.37484 117.89607 399.09956 120.880195 398.82025 136.28125 C 425.60995 116.58311 459.8456 155.86081 436.88218 175.55895 C 464.43714 185.10912 436.53446 236.56415 413.92019 227.96875 C 412.11036 242.29523 371.68283 247.3087 368.13443 227.96875 C 345.24227 248.62289 297.50846 216.866 330.0725 188.69105 Z" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id20_Graphic"><rect x="343.5" y="179.125" width="23" height="13" fill="white"/><rect x="343.5" y="179.125" width="23" height="13" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.5 179.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".08154297" y="10" textLength="12.836914">T1</tspan></text></g><g id="id21_Graphic"><rect x="343.5" y="203.125" width="23" height="13" fill="white"/><rect x="343.5" y="203.125" width="23" height="13" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(348.5 203.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x=".08154297" y="10" textLength="12.836914">T2</tspan></text></g><line x1="342.00935" y1="153.1525" x2="240.58475" y2="133.2538" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="343.00284" y1="186.9087" x2="246.80439" y2="197.20193" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="343.00684" y1="211.62867" x2="267.66967" y2="224.21505" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(387.5 190.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x=".49023438" y="11" textLength="40.019531">root set</tspan></text><text transform="translate(158.5 181.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" x=".15234375" y="11" textLength="26.695312">heap</tspan></text><text transform="translate(288.5 117.125)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x=".1484375" y="11" textLength="42.703125">phase 1</tspan></text><text transform="translate(153.5 129.125)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x=".1484375" y="11" textLength="42.703125">phase 2</tspan></text><text transform="translate(282 257.125)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="164.484375">1 &amp; 2 : referencing thread number</tspan><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="23" textLength="53.195312">X : shared </tspan></text></g></g></svg>
diff --git a/doc/graphics/por-scheduling-relevance.svg b/doc/graphics/por-scheduling-relevance.svg
new file mode 100644 (file)
index 0000000..45c2886
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="33 31 534 383" width="534pt" height="383pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-11 18:17:44 +0000</dc:date></metadata><defs><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="black"><g><path d="M 4.4999995 0 L 0 -1.6874998 L 0 1.6874998 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><font-face font-family="Monaco" font-size="9" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="588.0188" height="768.0188"/><g><title>Layer 1</title><text transform="translate(199 40.8188)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".1171875" y="11" textLength="158.765625">executed bytecode instruction</tspan></text><text transform="translate(99.4328 104.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".31933594" y="11" textLength="6">fi</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="6.319336" y="11" textLength="41.361328">eld insn</tspan></text><text transform="translate(253 104.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".4892578" y="11" textLength="50.021484">sync insn</tspan></text><text transform="translate(394 104.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".48242188" y="11" textLength="60.035156">invoke insn</tspan></text><text transform="translate(367 165.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="2.163086" y="11" textLength="28.007812">sync </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="4.498047" y="25" textLength="20.003906">mth</tspan></text><text transform="translate(439 165.125)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="1.9804688" y="11" textLength="53.373047">threading </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="17.99707" y="25" textLength="18.005859">call</tspan></text><line x1="261.35365" y1="54.8188" x2="140.57915" y2="104.125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="278.5" y1="54.8188" x2="278.5" y2="104.125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="294.64376" y1="54.8188" x2="408.35624" y2="104.125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="123.432805" y1="118.125" x2="123.432995" y2="380.456" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="278.51266" y1="118.125" x2="278.98842" y2="381.177" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="420.0736" y1="118.125" x2="390.3534" y2="165.125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="428.77212" y1="118.125" x2="457.45633" y2="165.125" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="381.5" y1="193.125" x2="381.5" y2="381.826" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="466" y1="193.125" x2="466" y2="382.51" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="174" y="70.125" width="204" height="14" fill="#ffd0cd" fill-opacity=".78"/><rect x="174" y="70.125" width="204" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(179 70.125)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="19.618164" y="11" textLength="154.76367">scheduling relevant insn type</tspan></text><rect x="46" y="232.125" width="432.976" height="14" fill="#ffd0cd" fill-opacity=".78"/><rect x="46" y="232.125" width="432.976" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(51 232.125)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="151.113" y="11" textLength="120.75">other runnable threads</tspan></text><rect x="245" y="259.909" width="158" height="14" fill="#ffd0cd" fill-opacity=".78"/><rect x="245" y="259.909" width="158" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(250 259.909)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="34.323242" y="11" textLength="79.353516">recursive locks</tspan></text><rect x="46" y="287.692" width="358" height="14" fill="#ffd0cd" fill-opacity=".78"/><rect x="46" y="287.692" width="358" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(51 287.692)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="134.97656" y="11" textLength="78.046875">shared objects</tspan></text><rect x="46" y="327.142" width="136.5" height="14" fill="#ffd0cd" fill-opacity=".78"/><rect x="46" y="327.142" width="136.5" height="14" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(51 327.142)" fill="red"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="red" x="5.555664" y="11" textLength="115.38867">lock protected access</tspan></text><text transform="translate(108.433 390.26)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x=".0126953125" y="11" textLength="234.78516">scheduling relevant instruction (registeres a </tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="234.58691" y="11" textLength="133.40039">ThreadChoiceGenerator)</tspan></text><text transform="translate(63.4328 77.125)" fill="purple"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".48339844" y="11" textLength="56.033203">data races</tspan></text><text transform="translate(318.5 105.125)" fill="purple"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x="4.482422" y="11" textLength="54.035156">deadlocks</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".4951172" y="25" textLength="62.009766">(lock races)</tspan></text><rect x="153.077" y="305.692" width="150" height="14" fill="white" fill-opacity=".74"/><text transform="translate(158.077 305.692)" fill="purple"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".29980469" y="11" textLength="139.40039">tracking of access threads</tspan></text><rect x="43.75" y="343.26" width="141" height="14" fill="white" fill-opacity=".74"/><text transform="translate(48.75 343.26)" fill="purple"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".47558594" y="11" textLength="130.04883">lock distance &amp; statistics</tspan></text><rect x="409" y="192.125" width="141" height="36" fill="white" fill-opacity=".84"/><text transform="translate(414 192.125)" fill="blue"><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="9" textLength="129.62109">Thread. start(), yield()</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="21" textLength="124.220215">        sleep(), join()</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="33" textLength="118.819336">Object.wait(),notify()</tspan></text><text transform="translate(494.261 123.125)" fill="purple"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".98046875" y="11" textLength="25.347656">confi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x="26.328125" y="11" textLength="30.691406">gured</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x=".32421875" y="25" textLength="57.351562">class/mthd</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="purple" x="4.3203125" y="39" textLength="49.359375">attributes</tspan></text><text transform="translate(72.4142 127.125)" fill="blue"><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="9" textLength="43.20703">GETFIELD</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="21" textLength="43.20703">PUTFIELD</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="33" textLength="48.60791">GETSTATIC</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="45" textLength="48.60791">PUTSTATIC</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="57" textLength="32.405273">xALOAD</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="69" textLength="37.806152">xASTORE</tspan></text><rect x="248" y="127.125" width="75" height="24" fill="white" fill-opacity=".74"/><text transform="translate(253 127.125)" fill="blue"><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="9" textLength="64.810547">MONITORENTER</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="21" textLength="59.409668">MONITOREXIT</tspan></text><rect x="392" y="127.125" width="81" height="24" fill="white" fill-opacity=".83"/><text transform="translate(397 127.125)" fill="blue"><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="9" textLength="70.211426">INVOKEVIRTUAL</tspan><tspan font-family="Monaco" font-size="9" font-weight="500" fill="blue" x="0" y="21" textLength="64.810547">INVOKESTATIC</tspan></text><line x1="44.5056" y1="371.793" x2="508.586" y2="371.793" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="8,4,1,4"/></g></g></svg>
diff --git a/doc/graphics/properties.svg b/doc/graphics/properties.svg
new file mode 100644 (file)
index 0000000..ad56b87
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="31 4 710 529" width="710pt" height="529pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2011-05-11 18:07:23 +0000</dc:date></metadata><defs><font-face font-family="Courier" font-size="11" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="15" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="blue"><g><path d="M 6.3999996 0 L 0 -2.3999999 L 0 2.3999999 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="purple"><g><path d="M 6.4000006 0 L 0 -2.4000002 L 0 2.4000002 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="blue"><g><path d="M 6.4000006 0 L 0 -2.4000002 L 0 2.4000002 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="19" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker_2" viewBox="-1 -4 9 8" markerWidth="9" markerHeight="8" color="purple"><g><path d="M 6.3999996 0 L 0 -2.3999999 L 0 2.3999999 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><path d="M 58.801 34.7184 L 485.801 34.7184 C 491.32385 34.7184 495.801 39.195553 495.801 44.7184 L 495.801 51.4805 C 495.801 57.003347 491.32385 61.4805 485.801 61.4805 L 58.801 61.4805 C 53.278153 61.4805 48.801 57.003347 48.801 51.4805 L 48.801 44.7184 C 48.801 39.195553 53.278153 34.7184 58.801 34.7184 Z" fill="#faffb6"/><path d="M 58.801 34.7184 L 485.801 34.7184 C 491.32385 34.7184 495.801 39.195553 495.801 44.7184 L 495.801 51.4805 C 495.801 57.003347 491.32385 61.4805 485.801 61.4805 L 58.801 61.4805 C 53.278153 61.4805 48.801 57.003347 48.801 51.4805 L 48.801 44.7184 C 48.801 39.195553 53.278153 34.7184 58.801 34.7184 Z" stroke="#ffd7aa" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 217.927 76.1073 L 477.121 76.1073 C 482.64385 76.1073 487.121 80.584453 487.121 86.1073 L 487.121 196.8643 C 487.121 202.38715 482.64385 206.8643 477.121 206.8643 L 217.927 206.8643 C 212.40415 206.8643 207.927 202.38715 207.927 196.8643 L 207.927 86.1073 C 207.927 80.584453 212.40415 76.1073 217.927 76.1073 Z" fill="#faffb6"/><path d="M 217.927 76.1073 L 477.121 76.1073 C 482.64385 76.1073 487.121 80.584453 487.121 86.1073 L 487.121 196.8643 C 487.121 202.38715 482.64385 206.8643 477.121 206.8643 L 217.927 206.8643 C 212.40415 206.8643 207.927 202.38715 207.927 196.8643 L 207.927 86.1073 C 207.927 80.584453 212.40415 76.1073 217.927 76.1073 Z" stroke="#ffd7aa" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 277.238 247.408 L 500.267 247.408 C 505.78985 247.408 510.267 251.88515 510.267 257.408 L 510.267 358.199 C 510.267 363.72185 505.78985 368.199 500.267 368.199 L 277.238 368.199 C 271.71515 368.199 267.238 363.72185 267.238 358.199 L 267.238 257.408 C 267.238 251.88515 271.71515 247.408 277.238 247.408 Z" fill="#faffb6"/><path d="M 277.238 247.408 L 500.267 247.408 C 505.78985 247.408 510.267 251.88515 510.267 257.408 L 510.267 358.199 C 510.267 363.72185 505.78985 368.199 500.267 368.199 L 277.238 368.199 C 271.71515 368.199 267.238 363.72185 267.238 358.199 L 267.238 257.408 C 267.238 251.88515 271.71515 247.408 277.238 247.408 Z" stroke="#ffd7aa" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 52.3687 247.326 L 232.6457 247.326 C 238.16855 247.326 242.6457 251.80315 242.6457 257.326 L 242.6457 463.762 C 242.6457 469.28485 238.16855 473.762 232.6457 473.762 L 52.3687 473.762 C 46.845853 473.762 42.3687 469.28485 42.3687 463.762 L 42.3687 257.326 C 42.3687 251.80315 46.845853 247.326 52.3687 247.326 Z" fill="#faffb6"/><path d="M 52.3687 247.326 L 232.6457 247.326 C 238.16855 247.326 242.6457 251.80315 242.6457 257.326 L 242.6457 463.762 C 242.6457 469.28485 238.16855 473.762 232.6457 473.762 L 52.3687 473.762 C 46.845853 473.762 42.3687 469.28485 42.3687 463.762 L 42.3687 257.326 C 42.3687 251.80315 46.845853 247.326 52.3687 247.326 Z" stroke="#ffd7aa" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 268.558 400.709 L 508.946 400.709 C 514.46885 400.709 518.946 405.18615 518.946 410.709 L 518.946 511.5 C 518.946 517.02285 514.46885 521.5 508.946 521.5 L 268.558 521.5 C 263.03515 521.5 258.558 517.02285 258.558 511.5 L 258.558 410.709 C 258.558 405.18615 263.03515 400.709 268.558 400.709 Z" fill="#faffb6"/><path d="M 268.558 400.709 L 508.946 400.709 C 514.46885 400.709 518.946 405.18615 518.946 410.709 L 518.946 511.5 C 518.946 517.02285 514.46885 521.5 508.946 521.5 L 268.558 521.5 C 263.03515 521.5 258.558 517.02285 258.558 511.5 L 258.558 410.709 C 258.558 405.18615 263.03515 400.709 268.558 400.709 Z" stroke="#ffd7aa" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(266.835 408.697)" fill="black"><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="10" textLength="237.63867">jpf.home = ${user.home}/projects/jpf</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="36" textLength="52.808594">jpf-core</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="52.808594" y="36" textLength="151.82471"> = ${jpf.home}/jpf-core</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="49" textLength="46.20752">jpf-awt</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="46.20752" y="49" textLength="118.819336"> = ${jpf.home}/awt</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="62" textLength="59.409668">jpf-shell</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="59.409668" y="62" textLength="158.42578"> = ${jpf.home}/jpf-shell</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="75" textLength="59.409668">jpf-aprop</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="59.409668" y="75" textLength="39.606445"> = ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="88" textLength="19.803223">...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="101" textLength="66.010742">extensions</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="66.010742" y="101" textLength="178.229"> = ${jpf-core},${jpf-shell}</tspan></text><text transform="translate(50 262)" fill="black"><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="10" textLength="165.02686">jpf-core = ${config_path}</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="36" textLength="59.409668">jpf-core.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="59.409668" y="36" textLength="105.61719">native_classpath</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="165.02686" y="36" textLength="13.2021484">=\</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="49" textLength="191.43115">  ${jpf-core}/build/jpf.jar;\</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="62" textLength="33.005371">  ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="75" textLength="178.229">  ${jpf-core}/lib/bcel.jar;</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="101" textLength="59.409668">jpf-core.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="59.409668" y="101" textLength="59.409668">classpath</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="118.819336" y="101" textLength="13.2021484">=\</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="114" textLength="151.82471">  build/jpf-classes.jar</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="140" textLength="59.409668">jpf-core.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="59.409668" y="140" textLength="92.41504">test_classpath</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="151.82471" y="140" textLength="13.2021484">=\</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="153" textLength="85.813965">  build/tests</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="179" textLength="59.409668">jpf-core.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="59.409668" y="179" textLength="59.409668">sourcepat</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="118.819336" y="179" textLength="19.803223">h=\</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="192" textLength="85.813965">  src/classes</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="205" textLength="19.803223">...</tspan></text><text transform="translate(271.417 260.887)" fill="black"><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="10" textLength="198.03223">jpf-awt-shell = ${config_path}</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="36" textLength="39.606445">@using</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="39.606445" y="36" textLength="66.010742"> = jpf-awt</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="75" textLength="92.41504">jpf-awt-shell.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="92.41504" y="75" textLength="105.61719">native_classpath</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="198.03223" y="75" textLength="26.404297">=...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="88" textLength="92.41504">jpf-awt-shell.</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="92.41504" y="88" textLength="59.409668">classpath</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="151.82471" y="88" textLength="26.404297">=...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="101" textLength="19.803223">...</tspan></text><text transform="translate(215.481 84.5732)" fill="red"><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="10" textLength="39.606445">target</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="39.606445" y="10" textLength="99.016113"> = RobotManager</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="23" textLength="72.611816">target_args</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="72.611816" y="23" textLength="39.606445"> = ...</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="49" textLength="118.819336">@using = jpf-aprop</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="62" textLength="46.20752">@import</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="46.20752" y="62" textLength="118.819336"> = ./my.properties</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="88" textLength="33.005371">shell</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="33.005371" y="88" textLength="204.6333"> = .shell.basicshell.BasicShell</tspan><tspan font-family="Courier" font-size="11" font-weight="500" fill="red" x="0" y="101" textLength="52.808594">listener</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="52.808594" y="101" textLength="211.23438"> = .aprop.listener.SharedChecker</tspan><tspan font-family="Courier" font-size="11" font-weight="500" x="0" y="114" textLength="19.803223">...</tspan></text><text transform="translate(535.837 419.199)" fill="black"><tspan font-family="Helvetica" font-size="15" font-weight="500" x="0" y="15" textLength="110.88867">1. site properties</tspan></text><text transform="translate(535.837 269.806)" fill="black"><tspan font-family="Helvetica" font-size="15" font-weight="500" x="0" y="15" textLength="132.56836">2. project properties</tspan></text><text transform="translate(535.837 122.743)" fill="black"><tspan font-family="Helvetica" font-size="15" font-weight="500" x="0" y="15" textLength="159.26514">3. application properties</tspan></text><text transform="translate(535.837 37)" fill="black"><tspan font-family="Helvetica" font-size="15" font-weight="500" x="0" y="15" textLength="110.05371">4. command line</tspan></text><text transform="translate(553.837 446.199)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="158.42578">~/.jpf/site.properties</tspan></text><text transform="translate(553.837 296.806)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="172.82812">&lt;project&gt;/jpf.properties</tspan></text><text transform="translate(553.837 149.743)" fill="blue"><tspan font-family="Courier" font-size="12" font-weight="bold" fill="blue" x="0" y="11" textLength="136.822266">&lt;project&gt;/.../*.jpf</tspan></text><text transform="translate(57.8009 38.4467)" fill="black"><tspan font-family="Courier" font-size="12" font-weight="500" x="0" y="11" textLength="180.0293">&gt; bin/jpf [-log][-show] {</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="red" x="180.0293" y="11" textLength="86.41406">+log.info=..</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="266.44336" y="11" textLength="14.402344">} </tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="red" x="280.8457" y="11" textLength="144.02344">.../RobotManager.jpf</tspan><tspan font-family="Courier" font-size="12" font-weight="500" x="424.86914" y="11" textLength="14.402344">  </tspan></text><path d="M 254.897 507.803 L 197.74177 507.75322 C 194.43011 507.75034 191.747 505.0649 191.747 501.75322 L 191.747 469.593" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 117 252 L 117 240.0136 C 117 236.69989 119.68629 234.0136 123 234.0136 C 123.00453 234.0136 123.00906 234.0136 123.013585 234.01362 L 263.27802 234.3312" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(75.1602 478.429)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="97.38281">all jpf.properties in</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="44.689453">order of </tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="blue" x="44.689453" y="25" textLength="57.36914">extensions</tspan></text><text transform="translate(284.191 226.678)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="172.07227">jpf.properties in current directory</tspan></text><path d="M 473.835 234.806 L 494.8676 234.806 C 498.16894 234.806 500.85008 232.13898 500.86752 228.83768 L 501.55515 98.61408 C 501.57265 95.30042 498.90058 92.59998 495.58692 92.582484 C 495.57636 92.58243 495.5658 92.5824 495.55523 92.5824 L 435.684 92.5824" marker-end="url(#FilledArrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(579.345 469.372)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="94.04297">- project locations</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="110.05664">- pre-loaded projects</tspan></text><text transform="translate(579.345 319.979)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="106.71094">- project class paths</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="121.41211">- project dependencies</tspan></text><text transform="translate(579.345 172.915)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="103.359375">- system-under-test</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="89.36133">- listeners, shells</tspan></text><text transform="translate(579.345 58.6805)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="63.38672">- debugging</tspan></text><text transform="translate(252.533 18.202)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="182.08594">command line property arguments</tspan></text><path d="M 423.131 477.215 C 422.42507 484.2239 423.5989 485.0065 427.771 486.571 C 431.9431 488.1355 462.451 486.832 462.451 486.832 C 462.451 486.832 471.22213 485.7952 473.73633 493.13772" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 384.172 289.104 C 385.547 286.104 388.047 282.854 393.547 282.729 C 399.047 282.604 404.922 287.729 404.672 293.729 C 404.422 299.729 399.672 304.729 393.047 304.354 C 391.54602 304.26904 390.24395 303.84401 389.1277 303.23738" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><rect x="188.908" y="216.267" width="16" height="23" fill="white"/><text transform="translate(188.908 216.267)" fill="blue"><tspan font-family="Helvetica" font-size="19" font-weight="500" fill="blue" x="0" y="19" textLength="15.836426">...</tspan></text><path d="M 264.193 454.367 C 255.2799 454.037 253.41636 449.68932 251.553 440.062 C 249.68966 430.43468 250.62069 331.05585 255.279 323.913 C 259.9374 316.77015 261.80115 316.14904 268.944 313.354 C 276.08685 310.55896 329.5034 310.55858 340.373 310.248 C 347.94775 310.03157 353.4106 310.26777 355.18565 306.1217" marker-end="url(#Arrow_Marker_2)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(440.597 77.495)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="57.375">application</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="25" textLength="53.361328">properties</tspan></text><path d="M 387.466 131.784 C 388.841 128.784 391.341 125.534 396.841 125.409 C 402.341 125.284 408.216 130.409 407.966 136.409 C 407.716 142.409 402.966 147.409 396.341 147.034 C 394.84002 146.94904 393.53795 146.52401 392.4217 145.917375" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 564.52274 354.335 L 551.865 366.99273 L 560.2191 366.99273 L 560.2191 408.5825 L 568.8264 408.5825 L 568.8264 366.99273 L 577.1805 366.99273 Z" fill="#e8eef7"/><path d="M 564.52274 354.335 L 551.865 366.99273 L 560.2191 366.99273 L 560.2191 408.5825 L 568.8264 408.5825 L 568.8264 366.99273 L 577.1805 366.99273 Z" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width=".24"/><path d="M 564.20274 200.87 L 551.545 213.52773 L 559.8991 213.52773 L 559.8991 255.1175 L 568.5064 255.1175 L 568.5064 213.52773 L 576.8605 213.52773 Z" fill="#e8eef7"/><path d="M 564.20274 200.87 L 551.545 213.52773 L 559.8991 213.52773 L 559.8991 255.1175 L 568.5064 255.1175 L 568.5064 213.52773 L 576.8605 213.52773 Z" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width=".24"/><path d="M 564.20274 60.5442 L 551.545 73.20193 L 559.8991 73.20193 L 559.8991 114.7917 L 568.5064 114.7917 L 568.5064 73.20193 L 576.8605 73.20193 Z" fill="#e8eef7"/><path d="M 564.20274 60.5442 L 551.545 73.20193 L 559.8991 73.20193 L 559.8991 114.7917 L 568.5064 114.7917 L 568.5064 73.20193 L 576.8605 73.20193 Z" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width=".24"/><path d="M 487.875 49.625 C 487.875 49.625 504.125 48.75 508.125 55.125 C 512.125 61.49999 509.12492 68.125 508.097 69.4369 C 507.80348 69.81151 507.02888 70.620914 506.07187 71.58626" marker-end="url(#Arrow_Marker_2)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(373.153 385.456)" fill="blue"><tspan font-family="Helvetica" font-size="12" font-weight="500" fill="blue" x="0" y="11" textLength="75.36914">site properties</tspan></text></g></g></svg>
diff --git a/doc/graphics/report.svg b/doc/graphics/report.svg
new file mode 100644 (file)
index 0000000..cad3324
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="10 18 784 573" width="784pt" height="573pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2010-09-10 23:02:45 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="532.22656" cap-height="719.72656" ascent="770.01953" descent="-229.98047" font-weight="bold"><font-face-src><font-face-name name="Helvetica-Bold"/></font-face-src></font-face><font-face font-family="Monaco" font-size="11" units-per-em="1000" underline-position="-37.597656" underline-thickness="75.683594" slope="0" x-height="560.54688" cap-height="780.27344" ascent="1e3" descent="-250" font-weight="500"><font-face-src><font-face-name name="Monaco"/></font-face-src></font-face><font-face font-family="Courier" font-size="12" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Ball_Marker" viewBox="-7 -4 8 8" markerWidth="8" markerHeight="8" color="blue"><g><circle cx="-2.9999986" cy="0" r="2.9999973" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="UMLInheritance_Marker" viewBox="-1 -8 14 16" markerWidth="14" markerHeight="16" color="black"><g><path d="M 12 0 L 0 -7 L 0 7 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 -3 L 0 3 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="12" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="-1e3" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-style="italic" font-weight="500"><font-face-src><font-face-name name="Helvetica-Oblique"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="1536.0376" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id99_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id21_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id100_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id105_Graphic" filter="url(#Shadow)"/></g><g id="id99_Graphic"><rect x="601" y="156" width="151" height="70" fill="white"/><rect x="601" y="156" width="151" height="70" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id21_Graphic"><rect x="468" y="140" width="90" height="83" fill="white"/><rect x="468" y="140" width="90" height="83" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><rect x="119" y="134" width="146" height="42" fill="white"/><rect x="119" y="134" width="146" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(124 134)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="29.326172">JPF()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="130.740234">addPublisherExtension()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="73.365234">setPublisherT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="72.035156" y="39" textLength="36.00586">opics()</tspan></text><rect x="119" y="120" width="146" height="14" fill="white"/><rect x="119" y="120" width="146" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(124 120)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="42.017578">reporter</tspan></text><rect x="119" y="106" width="146" height="14" fill="white"/><rect x="119" y="106" width="146" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(124 106)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="56.996094" y="11" textLength="22.007812">JPF</tspan></text><text transform="translate(34.81 28.631245)" fill="blue"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="11" textLength="13.2021484">..</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="26" textLength="422.46875">reporter = config.getInstance(&quot;report.class&quot;, Reporter.class,..)</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="422.46875" y="26" textLength="7.201172">;</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="41" textLength="14.402344">..</tspan></text><path d="M 108.51673 140.52065 C 96.31494 139.82353 82.887197 139.963245 74 137 C 63.50105 133.49935 54.3331 130.9979 52 120 C 49.6669 109.0021 57.3336 87.3357 60 71.006" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="305" y="150" width="118" height="42" fill="white"/><rect x="305" y="150" width="118" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(310 150)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="82.69922">searchStarted()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="52.02539">propertyV</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="51.814453" y="25" textLength="43.353516">iolated()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="89.36719">searchFinished()</tspan></text><rect x="305" y="136" width="118" height="14" fill="white"/><rect x="305" y="136" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(310 136)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="54.697266">publishers</tspan></text><rect x="305" y="122" width="118" height="14" fill="white"/><rect x="305" y="122" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(310 122)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="28.995117" y="11" textLength="50.009766">Reporter</tspan></text><rect x="463.5" y="190" width="90" height="28" fill="white"/><rect x="463.5" y="190" width="90" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(468.5 190)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="71.36133">publishStart()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="44.015625">getOut()</tspan></text><rect x="463.5" y="148" width="90" height="42" fill="white"/><rect x="463.5" y="148" width="90" height="42" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(468.5 148)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="57.36914">extensions</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="31.347656">topics</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="16.681641">out</tspan></text><rect x="463.5" y="134" width="90" height="14" fill="white"/><rect x="463.5" y="134" width="90" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(468.5 134)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="12.660156" y="11" textLength="54.679688">Publisher</tspan></text><rect x="596" y="165" width="151" height="56" fill="white"/><rect x="596" y="165" width="151" height="56" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(601 165)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="71.36133">publishStart()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="45.357422">publishT</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="44.91797" y="25" textLength="53.34961">ransition()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="39" textLength="91.38281">publishPropertyV</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="91.171875" y="39" textLength="46.01953">iolation()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="53" textLength="91.376953">publishFinished()</tspan></text><rect x="596" y="151" width="151" height="14" fill="white"/><rect x="596" y="151" width="151" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(601 151)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x=".4716797" y="11" textLength="140.05664">&lt;&lt;PublisherExtension&gt;&gt;</tspan></text><g id="id100_Graphic"><rect x="583" y="343" width="39" height="30" fill="white"/><rect x="583" y="343" width="39" height="30" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(588 351)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="9.4990234" y="11" textLength="10.001953">...</tspan></text></g><line x1="676" y1="328" x2="676" y2="240.506" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 606 343 L 606 328 L 704 328 L 704 343" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id105_Graphic"><rect x="409" y="284" width="39" height="30" fill="white"/><rect x="409" y="284" width="39" height="30" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(414 292)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="9.4990234" y="11" textLength="10.001953">...</tspan></text></g><line x1="502.00002" y1="269" x2="502.00002" y2="240.506" marker-end="url(#UMLInheritance_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><path d="M 432 284 L 432 269 L 530 269 L 530 284" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="535" y1="157" x2="586.1" y2="157" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="402" y1="141" x2="453.1" y2="141" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="245" y1="128" x2="296.1" y2="128" marker-end="url(#FilledArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(102.08 217.37988)" fill="blue"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="11" textLength="13.2021484">..</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="26" textLength="204.6333">for (Publisher p : publishers){</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="41" textLength="118.819336">  p.openChannel();</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="56" textLength="26.404297">  ..</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="71" textLength="125.42041">  p.publishStart();</tspan><tspan font-family="Courier" font-size="12" font-weight="500" fill="blue" x="0" y="86" textLength="14.402344">..</tspan></text><path d="M 298.63573 160.68016 C 291.20484 165.53147 283.20964 170.12326 278 176 C 271.50065 183.3316 266.49985 192.49985 265 201 C 263.50015 209.50015 267.6668 218.3382 269 227.006" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(200.64 328.44478)" fill="blue"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="11" textLength="191.43115"> public void publishStart() {</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="26" textLength="250.84082">    for (String topic : startTopics) {</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="41" textLength="204.6333">      if (&quot;jpf&quot;.equals(topic)){</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="56" textLength="138.62256">        publishJPF();</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="71" textLength="33.005371">  ...</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="86" textLength="204.6333">    for (PublisherExtension e :</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="101" textLength="237.63867">                       extensions) {</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="116" textLength="191.43115">        e.publishStart(this);</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="131" textLength="46.20752">      }</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="146" textLength="33.005371">  ...</tspan></text><path d="M 448.95183 200.3809 C 432.62623 206.25465 414.28666 209.15743 402 221 C 388.16805 234.332 378.68406 260.67906 372 278 C 365.31594 295.32094 365.26076 309.29224 361.89164 324.936" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(395.8 477.35088)" fill="blue"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="11" textLength="211.23438">out.println(&quot;JPF version&quot; + ..);</tspan></text><rect x="475" y="297.936" width="118" height="28" fill="white"/><rect x="475" y="297.936" width="118" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(480 297.936)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="67.353516">publishJPF()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><rect x="475" y="283.936" width="118" height="14" fill="white"/><rect x="475" y="283.936" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(480 283.936)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="2.991211" y="11" textLength="102.01758">ConsolePublisher</tspan></text><path d="M 533.46314 321.31366 C 530.55445 334.16368 528.93123 346.2254 522.891 361 C 515.87354 378.16495 499.575 400.67907 492.891 418 C 486.207 435.32093 486.152 449.29223 482.783 464.936" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="647" y="357" width="118" height="28" fill="white"/><rect x="647" y="357" width="118" height="28" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(652 357)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="11" textLength="91.376953">publishFinished()</tspan><tspan font-family="Helvetica" font-size="12" font-weight="500" x="0" y="25" textLength="10.001953">...</tspan></text><rect x="647" y="343" width="118" height="14" fill="white"/><rect x="647" y="343" width="118" height="14" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(652 343)" fill="black"><tspan font-family="Helvetica" font-size="12" font-weight="bold" x="2.3115234" y="11" textLength="103.376953">DeadlockAnalyzer</tspan></text><text transform="translate(555.78 414.35063)" fill="blue"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="11" textLength="118.819336">PrintWriter out = </tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="26" textLength="191.43115">          publisher,getOut();</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="blue" x="0" y="41" textLength="158.42578">printTraceAnalysis(out);</tspan></text><path d="M 714.47304 378.3917 C 713.93286 385.00501 713.7042 391.82623 711 397 C 707.3972 403.89298 699.3982 407.86588 693.59814 413.298" marker-start="url(#Ball_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(306 80)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="76.716797">data collection</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="122.07422">publisher management</tspan></text><text transform="translate(468 70)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="80.04492">data formatting</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="98.72461">topic management</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="39" textLength="78.73242">output channel</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="53" textLength="80.04492">   management</tspan></text><text transform="translate(620.5 109.506)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="86.03906">property/listener</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="34.013672">specifi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="34.013672" y="25" textLength="77.378906">c output topics</tspan></text><text transform="translate(50.42 512.3625)" fill="#390"><tspan font-family="Monaco" font-size="11" font-weight="500" fill="#390" x="0" y="11" textLength="191.43115">report.class=.report.Reporter</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="#390" x="0" y="26" textLength="178.229">report.publisher=console,..</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="#390" x="0" y="41" textLength="297.04834">report.console.class=.report.ConsolePublisher</tspan><tspan font-family="Monaco" font-size="11" font-weight="500" fill="#390" x="0" y="56" textLength="178.229">report.console.start=jpf,..</tspan></text><text transform="translate(91 477)" fill="red"><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="11" textLength="50.015625">JPF confi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="50.015625" y="11" textLength="43.365234">guration</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="0" y="25" textLength="144.06445">(e.g. jpf.properties or *.jpf fi</tspan><tspan font-family="Helvetica" font-size="12" font-style="italic" font-weight="500" fill="red" x="144.06445" y="25" textLength="19.335938">les)</tspan></text><path d="M 135.75 467.749 L 135.75 425.749 L 126 425.749 L 145.5 411.749 L 165 425.749 L 155.25 425.749 L 155.25 467.749 Z" fill="white"/><path d="M 135.75 467.749 L 135.75 425.749 L 126 425.749 L 145.5 411.749 L 165 425.749 L 155.25 425.749 L 155.25 467.749 Z" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/states-mc.svg b/doc/graphics/states-mc.svg
new file mode 100644 (file)
index 0000000..8d9f5fd
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="111 18 212 194" width="212pt" height="194pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2009-09-06 18:37:27 +0000</dc:date></metadata><defs><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="blue"><g><path d="M 4.7999992 0 L 0 -1.7999997 L 0 1.7999997 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -2 5 4" markerWidth="5" markerHeight="4" color="red"><g><path d="M 2.4000001 0 L 0 -.90000004 L 0 .90000004 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-4 -2 5 4" markerWidth="5" markerHeight="4" color="red"><g><path d="M -2.4000001 0 L 0 .90000004 L 0 -.90000004 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_3" viewBox="-1 -2 3 4" markerWidth="3" markerHeight="4" color="red"><g><path d="M .96 0 L 0 -.36 L 0 .36 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Arial Unicode MS" font-size="14" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Apple Symbols" font-size="13" panose-1="2 0 0 0 0 0 0 0 0 0" units-per-em="1000" underline-position="-73.242188" underline-thickness="48.828125" slope="0" x-height="392.57812" cap-height="546.38672" ascent="666.5039" descent="-250" font-weight="500"><font-face-src><font-face-name name="AppleSymbols"/></font-face-src></font-face><font-face font-family="Zapf Dingbats" font-size="14" panose-1="5 2 1 2 1 7 4 2 6 9" units-per-em="1000" underline-position="-100.097656" underline-thickness="60.058594" slope="0" x-height="542.48047" cap-height="723.14453" ascent="813.96484" descent="-176.75781" font-weight="500"><font-face-src><font-face-name name="ZapfDingbatsITC"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><path d="M 199.037 162.428 C 200.57818 159.34531 201.0281 157.22493 203.661 153.179 C 206.2939 149.13307 210.95072 142.5169 214.836 138.15 C 218.72128 133.7831 225.04836 130.86028 226.975 126.975 C 228.90164 123.08972 229.60801 119.138736 226.397 114.836 C 224.05647 111.69971 218.50761 108.289804 213.25864 104.90571" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><circle cx="199.97595" cy="92.61395" r="7.000961" fill="#fffe71"/><circle cx="199.97595" cy="92.61395" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="181.81895" cy="119.45695" r="7.000961" fill="#fffe71"/><circle cx="181.81895" cy="119.45695" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="172.39495" cy="145.98695" r="7.000961" fill="#fffe71"/><circle cx="172.39495" cy="145.98695" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="191.28895" cy="172.51695" r="7.000961" fill="#a5ff78"/><circle cx="191.28895" cy="172.51695" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="214.71395" cy="119.26395" r="7.000961" fill="#fffe71"/><circle cx="214.71395" cy="119.26395" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="247.76695" cy="119.93295" r="7.000961" fill="#fffe71"/><circle cx="247.76695" cy="119.93295" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="225.36195" cy="147.66995" r="7.000961" fill="#fffe71"/><circle cx="225.36195" cy="147.66995" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="193.08327" y1="102.80395" x2="188.43147" y2="109.68109" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="177.86826" y1="130.578685" x2="176.34559" y2="134.865196" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="205.92984" y1="103.380056" x2="209.00205" y2="108.93538" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="218.85671" y1="130.315664" x2="221.21924" y2="136.61822" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="179.24131" y1="155.600255" x2="184.4426" y2="162.90364" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="215.647" y1="154.37152" x2="205.27354" y2="161.52735" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="261.29795" cy="146.77595" r="7.000961" fill="#fffe71"/><circle cx="261.29795" cy="146.77595" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="206.92362" y1="96.58548" x2="236.28745" y2="113.37086" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="251.36924" y1="127.07921" x2="255.34603" y2="134.9684" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="261.20595" cy="174.51295" r="7.000961" fill="red"/><circle cx="261.20595" cy="174.51295" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="261.27142" y1="154.77686" x2="261.24816" y2="161.79207" marker-end="url(#FilledArrow_Marker_3)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="204.73078" y1="125.559554" x2="186.87417" y2="136.82032" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="239.8446" y1="129.34356" x2="235.5243" y2="134.47547" marker-end="url(#FilledArrow_Marker)" marker-start="url(#FilledArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(255.97 164.705)" fill="yellow"><tspan font-family="Arial Unicode MS" font-size="14" font-weight="500" fill="yellow" x="0" y="15" textLength="10.5">☠</tspan></text><text transform="translate(149 27)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="79.486816">model checking:</tspan></text><text transform="translate(176 46.4739)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="10" textLength="132.84668">all program state are explored</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="22" textLength="131.75781"> until none left or defect found</tspan></text><path d="M 183.622 166.859 C 178.29187 167.05165 173.02446 169.4278 167.63 167.437 C 162.23554 165.4462 154.65566 161.23853 151.252 154.913 C 147.84834 148.58747 145.43984 137.4432 147.206 129.48 C 148.97216 121.5168 155.10684 112.587704 161.85 107.129 C 167.00219 102.95821 174.3303 101.111333 181.33995 98.92518" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 183.237 142.775 C 188.43881 142.83899 194.41278 145.3111 198.844 142.967 C 201.71564 141.44791 203.9405 137.9056 206.14715 134.306975" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><path d="M 235.645 139.692 C 237.44315 138.9854 239.37017 139.27383 241.04 137.572 C 241.57356 137.02821 242.08097 136.281115 242.57475 135.42817" marker-end="url(#Arrow_Marker)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><text transform="translate(125.853 90.4145)" fill="blue"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="blue" x="0" y="10" textLength="42.79297">backtrack</tspan></text><text transform="translate(177.64 135.638)" fill="purple"><tspan font-family="Apple Symbols" font-size="13" font-weight="500" fill="purple" x="0" y="9" textLength="7.445801">≡</tspan></text><text transform="translate(195.944 160.694)" fill="purple"><tspan font-family="Apple Symbols" font-size="13" font-weight="500" fill="purple" x="0" y="9" textLength="7.445801">≡</tspan></text><text transform="translate(228.507 134.495)" fill="purple"><tspan font-family="Apple Symbols" font-size="13" font-weight="500" fill="purple" x="0" y="9" textLength="7.445801">≡</tspan></text><text transform="translate(202.738 164.231)" fill="purple"><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="purple" x="0" y="10" textLength="27.231445">match</tspan></text><text transform="translate(254.721 187.554)" fill="red"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="red" x=".26269531" y="11" textLength="9.4746094">✘</tspan></text></g></g></svg>
diff --git a/doc/graphics/states-testing.svg b/doc/graphics/states-testing.svg
new file mode 100644 (file)
index 0000000..12e5396
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="143 28 197 186" width="197pt" height="186pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-11-19 20:33:35 +0000</dc:date></metadata><defs><filter id="Shadow" filterUnits="userSpaceOnUse"><feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="1.308"/><feOffset in="blur" result="offset" dx="0" dy="2"/><feFlood flood-color="black" flood-opacity=".5" result="flood"/><feComposite in="flood" in2="offset" operator="in"/></filter><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="red"><g><path d="M 4.8000002 0 L 0 -1.8000001 L 0 1.8000001 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Arial Unicode MS" font-size="14" panose-1="2 11 6 4 2 2 2 2 2 4" units-per-em="1000" underline-position="-100.097656" underline-thickness="49.804688" slope="0" x-height="529.78516" cap-height="728.02734" ascent="1068.84766" descent="-270.9961" font-weight="500"><font-face-src><font-face-name name="ArialUnicodeMS"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="10" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Zapf Dingbats" font-size="14" panose-1="5 2 1 2 1 7 4 2 6 9" units-per-em="1000" underline-position="-100.097656" underline-thickness="60.058594" slope="0" x-height="542.48047" cap-height="723.14453" ascent="813.96484" descent="-176.75781" font-weight="500"><font-face-src><font-face-name name="ZapfDingbatsITC"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="11" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><font-face font-family="Helvetica" font-size="14" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="FilledArrow_Marker_2" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="blue"><g><path d="M 4.7999992 0 L 0 -1.7999997 L 0 1.7999997 Z" fill="currentColor" stroke="currentColor" stroke-width="1"/></g></marker></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><g><xl:use xl:href="#id24_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id25_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id28_Graphic" filter="url(#Shadow)"/><xl:use xl:href="#id36_Graphic" filter="url(#Shadow)"/></g><circle cx="200.11895" cy="97.06455" r="7.000961" fill="#faff8b"/><circle cx="200.11895" cy="97.06455" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g id="id24_Graphic"><circle cx="181.96195" cy="123.90795" r="7.000961" fill="white"/><circle cx="181.96195" cy="123.90795" r="7.000961" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><g id="id25_Graphic"><circle cx="172.53795" cy="150.43695" r="7.000961" fill="white"/><circle cx="172.53795" cy="150.43695" r="7.000961" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><circle cx="191.43195" cy="176.96795" r="7.000961" fill="#a5ff78"/><circle cx="191.43195" cy="176.96795" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><circle cx="214.85695" cy="123.71495" r="7.000961" fill="#faff8b"/><circle cx="214.85695" cy="123.71495" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><g id="id28_Graphic"><circle cx="247.90995" cy="124.38295" r="7.000961" fill="white"/><circle cx="247.90995" cy="124.38295" r="7.000961" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><circle cx="225.50495" cy="152.11995" r="7.000961" fill="#faff8b"/><circle cx="225.50495" cy="152.11995" r="7.000961" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="195.63552" y1="103.692876" x2="186.16524" y2="117.69378" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="179.45053" y1="130.97771" x2="175.04936" y2="143.36719" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="203.99181" y1="104.067766" x2="207.74168" y2="110.84856" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="217.66598" y1="131.20841" x2="220.34415" y2="138.3528" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="176.88977" y1="156.54779" x2="186.79009" y2="170.44984" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="219.0396" y1="156.83486" x2="203.31072" y2="168.30526" marker-end="url(#FilledArrow_Marker)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><g id="id36_Graphic"><circle cx="261.44095" cy="151.22695" r="7.000961" fill="white"/><circle cx="261.44095" cy="151.22695" r="7.000961" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g><line x1="207.06666" y1="101.036017" x2="241.39632" y2="120.65962" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="251.28707" y1="131.08278" x2="258.06383" y2="144.52712" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><circle cx="261.34895" cy="178.96395" r="7.000961" fill="#ffafb4"/><circle cx="261.34895" cy="178.96395" r="7.000961" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="261.41607" y1="158.72786" x2="261.37383" y2="171.46304" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="208.09054" y1="127.98755" x2="178.88159" y2="146.43131" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="243.19621" y1="130.21848" x2="230.53287" y2="145.895465" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(256.333 169.281)" fill="black"><tspan font-family="Arial Unicode MS" font-size="14" font-weight="500" x="0" y="15" textLength="10.5">☠</tspan></text><text transform="translate(228.228 55.8818)" fill="black"><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="10" textLength="84.506836">based on input set </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="blue" x="84.506836" y="10" textLength="12.241211">{d}</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="22" textLength="40.585938">only one </tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" fill="red" x="40.585938" y="22" textLength="19.462891">path</tspan><tspan font-family="Helvetica" font-size="10" font-weight="500" x="0" y="34" textLength="81.713867">executed at a time</tspan></text><text transform="translate(176 190)" fill="#00c300"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="#00c300" x=".076660156" y="11" textLength="11.84668">✔</tspan></text><text transform="translate(203.93402 37.205296)" fill="black"><tspan font-family="Helvetica" font-size="11" font-weight="500" x="0" y="10" textLength="35.465332">testing:</tspan></text><text transform="translate(157.263 64)" fill="blue"><tspan font-family="Helvetica" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="17.137695">{d}</tspan></text><path d="M 180.263 69.663568 C 186.02242 68.496695 194.58713 64.273384 197.543 66.1626 C 199.34019 67.31126 199.0646 70.719975 198.60663 74.327384" marker-end="url(#FilledArrow_Marker_2)" stroke="blue" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/></g></g></svg>
diff --git a/doc/graphics/sw-model-checking-2.svg b/doc/graphics/sw-model-checking-2.svg
new file mode 100644 (file)
index 0000000..a0575f7
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="34 236 374 190" width="374pt" height="190pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2015-01-05 22:19:29 +0000</dc:date></metadata><defs><font-face font-family="Courier" font-size="14" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><font-face font-family="Courier" font-size="14" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 0 M 0 -3 L 8 0 L 0 3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker_2" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="red"><g><path d="M 4.8 0 L 0 0 M 0 -1.8 L 4.8 0 L 0 1.8" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Zapf Dingbats" font-size="14" panose-1="5 2 1 2 1 7 4 2 6 9" units-per-em="1000" underline-position="-100.097656" underline-thickness="60.058594" slope="0" x-height="542.48047" cap-height="723.14453" ascent="813.96484" descent="-176.75781" font-weight="500"><font-face-src><font-face-name name="ZapfDingbatsITC"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="Arrow_Marker" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="purple"><g><path d="M 4.7999992 0 L 0 -1.7999997 L 0 1.7999997 Z" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Helvetica" font-size="9" units-per-em="1000" underline-position="-75.683594" underline-thickness="49.316406" slope="0" x-height="522.94922" cap-height="717.28516" ascent="770.01953" descent="-229.98047" font-weight="500"><font-face-src><font-face-name name="Helvetica"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><text transform="translate(97.5 288)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x="0" y="14" textLength="25.204102">a=0</tspan></text><text transform="translate(51.5 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">b=0</tspan></text><text transform="translate(117 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">b=1</tspan></text><text transform="translate(182 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x="0" y="14" textLength="25.204102">b=2</tspan></text><line x1="102.18085" y1="305" x2="79.743833" y2="327.92478" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="114.026596" y1="305" x2="122.67953" y2="325.8558" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="125.781915" y1="305" x2="168.4446" y2="328.72954" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(51.5 380)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">c=0</tspan></text><text transform="translate(117 380)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">c=0</tspan></text><text transform="translate(174.5 380)" fill="red"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="red" x="0" y="14" textLength="42.006836">c=0/0</tspan></text><line x1="64.5" y1="352" x2="64.5" y2="370.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="130" y1="352" x2="130" y2="370.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="195.18889" y1="352" x2="195.52452" y2="367.10318" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(200 245)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x="0" y="14" textLength="42.006836">start</tspan></text><line x1="199.55814" y1="262" x2="140.52895" y2="284.86716" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(304.5 288)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">a=1</tspan></text><text transform="translate(258.5 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">b=0</tspan></text><text transform="translate(312.5 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">b=1</tspan></text><text transform="translate(366.5 335)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">b=2</tspan></text><line x1="309.18085" y1="305" x2="286.74383" y2="327.92478" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="318.9468" y1="305" x2="322.39198" y2="325.24037" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="328.71277" y1="305" x2="360.39788" y2="329.01936" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(254.5 380.5)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="33.605469">c=-1</tspan></text><text transform="translate(304 380.5)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="42.006836">c=1/0</tspan></text><text transform="translate(367.5 380)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x="0" y="14" textLength="25.204102">c=1</tspan></text><line x1="271.5" y1="352" x2="271.5" y2="370.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="325.5" y1="352" x2="325.5" y2="370.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="379.68912" y1="352" x2="380.0919" y2="370.10245" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="240.47674" y1="262" x2="290.46495" y2="284.39055" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(190 402)" fill="red"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="red" x="0" y="11" textLength="9.4746094">✘</tspan></text><path d="M 195 255.53846 C 184.3344 256.35889 171.9991 255.75663 163 258 C 154.0009 260.24337 148.17807 264.0005 141 269 C 135.49167 272.83653 130.1641 278.24436 124.8097 283.41438" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="165.29035" y="251.01964" width="16" height="11" fill="white"/><text transform="translate(170.29035 251.01964)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">1</tspan></text><path d="M 93.036364 305 C 80.025544 311.3327 61.83861 316.91738 54 324 C 46.16139 331.08262 44.88934 338.1676 46 347.5 C 46.909354 355.1409 52.88361 364.79484 57.70203 373.98866" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="59.46112" y="310.53899" width="16" height="11" fill="white"/><text transform="translate(64.46112 310.53899)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">2</tspan></text><path d="M 67.75532 380 C 72.66972 367.16795 76.257992 353.99875 82.5 341.5 C 87.77279 330.94197 94.94186 320.857 101.60377 310.64679" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="66.688773" y="354.95894" width="16" height="11" fill="white"/><text transform="translate(71.688773 354.95894)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">3</tspan></text><path d="M 110.03297 305 C 109.35538 317.3321 107.672194 331.58438 108 342 C 108.327806 352.41562 109.547864 361.1673 112 367.5 C 113.41764 371.1611 115.5836 373.37442 117.84884 375.39535" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="100.541785" y="320.0904" width="16" height="11" fill="white"/><text transform="translate(105.541785 320.0904)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">4</tspan></text><path d="M 133.36264 380 C 138.24127 367.6679 150.6677 355.49875 148 343 C 145.68608 332.1588 132.01485 321.06398 121.76401 310.04676" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="136.39532" y="354.41076" width="16" height="11" fill="white"/><text transform="translate(141.39532 354.41076)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">5</tspan></text><text transform="translate(59 402)" fill="#00c300"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="#00c300" x="0" y="11" textLength="11.84668">✔</tspan></text><text transform="translate(124 402)" fill="#00c300"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="#00c300" x="0" y="11" textLength="11.84668">✔</tspan></text><path d="M 128.5 303.40226 C 144.66505 309.60088 162.91808 315.15106 177 322 C 191.08192 328.84894 209.28636 334.8343 213 344.5 C 216.09567 352.55727 207.67603 363.62784 202.14012 373.94288" marker-end="url(#Arrow_Marker)" stroke="purple" stroke-linecap="round" stroke-linejoin="round" stroke-width="1" stroke-dasharray="2,2"/><rect x="152.59596" y="309.56811" width="16" height="11" fill="white"/><text transform="translate(157.59596 309.56811)" fill="purple"><tspan font-family="Helvetica" font-size="9" font-weight="500" fill="purple" x="0" y="9" textLength="5.005371">6</tspan></text></g></g></svg>
diff --git a/doc/graphics/sw-model-checking.svg b/doc/graphics/sw-model-checking.svg
new file mode 100644 (file)
index 0000000..1fd0a54
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xl="http://www.w3.org/1999/xlink" version="1.1" viewBox="32 9 642 191" width="642pt" height="191pt" xmlns:dc="http://purl.org/dc/elements/1.1/"><metadata> Produced by OmniGraffle 6.1 <dc:date>2014-11-19 20:40:12 +0000</dc:date></metadata><defs><font-face font-family="Courier" font-size="13" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><font-face font-family="Hiragino Kaku Gothic Pro" font-size="14" panose-1="2 11 3 0 0 0 0 0 0 0" units-per-em="1000" underline-position="-60" underline-thickness="63" slope="0" x-height="545" cap-height="766" ascent="880.0018" descent="-120.00024" font-weight="400"><font-face-src><font-face-name name="HiraKakuPro-W3"/></font-face-src></font-face><font-face font-family="Courier" font-size="14" units-per-em="1000" underline-position="-178.22266" underline-thickness="57.617188" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="500"><font-face-src><font-face-name name="Courier"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker" viewBox="-1 -4 10 8" markerWidth="10" markerHeight="8" color="black"><g><path d="M 8 0 L 0 0 M 0 -3 L 8 0 L 0 3" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Courier" font-size="14" units-per-em="1000" underline-position="-144.04297" underline-thickness="91.796875" slope="0" x-height="462.40234" cap-height="594.72656" ascent="753.90625" descent="-246.09375" font-weight="bold"><font-face-src><font-face-name name="Courier-Bold"/></font-face-src></font-face><marker orient="auto" overflow="visible" markerUnits="strokeWidth" id="StickArrow_Marker_2" viewBox="-1 -3 7 6" markerWidth="7" markerHeight="6" color="red"><g><path d="M 4.8 0 L 0 0 M 0 -1.8 L 4.8 0 L 0 1.8" fill="none" stroke="currentColor" stroke-width="1"/></g></marker><font-face font-family="Zapf Dingbats" font-size="14" panose-1="5 2 1 2 1 7 4 2 6 9" units-per-em="1000" underline-position="-100.097656" underline-thickness="60.058594" slope="0" x-height="542.48047" cap-height="723.14453" ascent="813.96484" descent="-176.75781" font-weight="500"><font-face-src><font-face-name name="ZapfDingbatsITC"/></font-face-src></font-face></defs><g stroke="none" stroke-opacity="1" stroke-dasharray="none" fill="none" fill-opacity="1"><title>Canvas 1</title><rect fill="white" width="768.0188" height="588.0188"/><g><title>Layer 1</title><text transform="translate(432.5 20)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x="4.2822266" y="13" textLength="226.23682">Random random = new Random() </tspan></text><text transform="translate(432.5 65.5)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x=".48413086" y="13" textLength="195.03174">int a = random.nextInt(2)</tspan></text><text transform="translate(432.5 110)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x=".48413086" y="13" textLength="195.03174">int b = random.nextInt(3)</tspan></text><text transform="translate(432.5 155)" fill="blue"><tspan font-family="Courier" font-size="13" font-weight="500" fill="blue" x=".28857422" y="13" textLength="140.42285">int c = a/(b+a -2)</tspan></text><text transform="translate(411 24)" fill="black"><tspan font-family="Hiragino Kaku Gothic Pro" font-size="14" font-weight="400" x="0" y="12" textLength="14">①</tspan></text><text transform="translate(411 68)" fill="black"><tspan font-family="Hiragino Kaku Gothic Pro" font-size="14" font-weight="400" x="0" y="12" textLength="14">②</tspan></text><text transform="translate(411 112)" fill="black"><tspan font-family="Hiragino Kaku Gothic Pro" font-size="14" font-weight="400" x="0" y="12" textLength="14">③</tspan></text><text transform="translate(411 156)" fill="black"><tspan font-family="Hiragino Kaku Gothic Pro" font-size="14" font-weight="400" x="0" y="12" textLength="14">④</tspan></text><text transform="translate(92 61)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">a=0</tspan></text><text transform="translate(46 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">b=0</tspan></text><text transform="translate(100 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">b=1</tspan></text><text transform="translate(154 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">b=2</tspan></text><line x1="96.68085" y1="78" x2="74.243835" y2="100.92478" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="106.446806" y1="78" x2="109.89197" y2="98.24037" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="116.21276" y1="78" x2="147.89787" y2="102.01936" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(46 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">c=0</tspan></text><text transform="translate(100 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">c=0</tspan></text><text transform="translate(146.5 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".49658203" y="14" textLength="42.006836">c=0/0</tspan></text><line x1="59" y1="125" x2="59" y2="143.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="113" y1="125" x2="113" y2="143.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="167.18889" y1="125" x2="167.59116" y2="143.10244" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(194.5 18)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x=".49658203" y="14" textLength="42.006836">start</tspan></text><line x1="194.05814" y1="35" x2="132.23152" y2="58.95085" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(299 61)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x=".39794922" y="14" textLength="25.204102">a=1</tspan></text><text transform="translate(253 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x=".39794922" y="14" textLength="25.204102">b=0</tspan></text><text transform="translate(307 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">b=1</tspan></text><text transform="translate(361 108)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">b=2</tspan></text><line x1="303.68085" y1="78" x2="283.34222" y2="98.780774" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="313.4468" y1="78" x2="316.89197" y2="98.24037" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="323.21276" y1="78" x2="354.89787" y2="102.01936" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><text transform="translate(249 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="bold" fill="blue" x=".19726562" y="14" textLength="33.605469">c=-1</tspan></text><text transform="translate(298.5 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".49658203" y="14" textLength="42.006836">c=1/0</tspan></text><text transform="translate(362 153)" fill="blue"><tspan font-family="Courier" font-size="14" font-weight="500" fill="blue" x=".39794922" y="14" textLength="25.204102">c=1</tspan></text><line x1="266" y1="125" x2="266" y2="140.1" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><line x1="320" y1="125" x2="320" y2="143.1" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="374.18889" y1="125" x2="374.59116" y2="143.10244" marker-end="url(#StickArrow_Marker)" stroke="black" stroke-linecap="round" stroke-linejoin="round" stroke-width="1"/><line x1="234.97674" y1="35" x2="282.22705" y2="56.1642" marker-end="url(#StickArrow_Marker_2)" stroke="red" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/><text transform="translate(260 176)" fill="#00c300"><tspan font-family="Zapf Dingbats" font-size="14" font-weight="500" fill="#00c300" x=".076660156" y="11" textLength="11.84668">✔</tspan></text></g></g></svg>
diff --git a/doc/index.md b/doc/index.md
new file mode 100644 (file)
index 0000000..a40d030
--- /dev/null
@@ -0,0 +1,66 @@
+## Welcome to the Java™Pathfinder System ##
+
+This is the main page for Java™ Pathfinder (JPF). JPF is an extensible software model checking framework for Java™ bytecode programs. The system was developed at the [NASA Ames Research Center](http://arc.nasa.gov), open sourced in 2005, and is freely available on this server under the [Apache-2.0 license](http://www.apache.org/licenses/LICENSE-2.0).
+
+
+This page is our primary source of documentation, and is divided into the following sections.
+
+   ---
+
+  * [Introduction](intro/index) -- a brief introduction into model checking and JPF
+    * [What is JPF?](intro/what_is_jpf)
+    * [Testing vs. Model Checking](intro/testing_vs_model_checking)
+         - [Random value example](intro/random_example)
+         - [Data race example](intro/race_example)
+    * [JPF key features](intro/classification)
+    
+    ---
+
+  * [How to obtain and install JPF](install/index) -- everything to get it running on your machine
+    - [System requirements](install/requirements)
+    - Downloading [binary snapshots](install/snapshot) and [sources](install/repositories)
+    - [Creating a site properties file](install/site-properties)
+    - [Building, testing, and running](install/build)
+    - Installing the JPF plugins
+         - [Eclipse](install/eclipse-plugin) 
+         - [NetBeans](install/netbeans-plugin)
+    
+    ---
+         
+  * [How to use JPF](user/index) -- the user manual for JPF    
+    - [Different applications of JPF](user/application_types)
+    - [JPF's runtime components](user/components)
+    - [Starting JPF](user/run)
+    - [Configuring JPF](user/config)
+    - [Understanding JPF output](user/output)
+    - [Using JPF's Verify API in the system under test](user/api)
+    
+    ---
+        
+  * [Developer guide](devel/index) -- what's under the hood
+    * [Top-level design](devel/design)
+    * Key mechanisms, such as 
+        - [ChoiceGenerators](devel/choicegenerator)
+        - [Partial order reduction](devel/partial_order_reduction)
+        - [Slot and field attributes](devel/attributes)
+    * Extension mechanisms, such as
+        - [Listeners](devel/listener)
+        - [Search Strategies](devel/design)
+        - [Model Java Interface (MJI)](devel/mji)
+        - [Bytecode Factories](devel/bytecode_factory)
+    * Common utility infrastructures, such as
+        - [Logging system](devel/logging)
+        - [Reporting system](devel/report)
+    * [Running JPF from within your application](devel/embedded)
+    * [Writing JPF tests](devel/jpf_tests)
+    * [Coding conventions](devel/coding_conventions)
+    * [Hosting an Eclipse plugin update site](devel/eclipse_plugin_update) 
+        
+    ---
+        
+  * [JPF core project](jpf-core/index) -- description and link to jpf-core
+    
+    ---
+      
+  * [Related research and publications](papers/index)    
+
diff --git a/doc/install/build.md b/doc/install/build.md
new file mode 100644 (file)
index 0000000..9e8d3d6
--- /dev/null
@@ -0,0 +1,112 @@
+# Building, Testing, and Running #
+
+If you downloaded binary snapshots, you don't have anything to build (except of creating/updating you [site.properties](./site-properties) file). 
+
+## Building JPF ##
+
+If you have cloned the project repositories you are interested in (which at least includes [jpf-core](../jpf-core/index)), you can build and test each of them by means of their included [Ant](http://ant.apache.org) `build.xml` scripts. Note that you have to install Ant and JUnit separately, e.g. following directions [here](../install/requirements).
+
+
+### Using the command line ###
+
+~~~~~~~~ {.bash}
+> cd jpf-core
+> ant test
+
+... lots of output, at the end you should see something like:
+BUILD SUCCESSFUL
+Total time: 2 minutes 31 seconds
+~~~~~~~~
+
+### Within NetBeans ###
+
+ 1. run **File/Open Project..** from the application menu, entering the JPF project you just downloaded (e.g. "jpf-core")
+ 1. select the project that appears in our project pane (e.g. "jpf-core")
+ 1. run **Build** from the project context menu
+
+### Within Eclipse ###
+
+* Ensure that the `JAVA_HOME` environment variable points to the jdk1.6xxx directory. If it is empty or points to a JRE then errors such as **javac not found** maybe seen. If you do not want the system Java settings to point to jdk1.6xxx, you can also set project specific settings in eclipse.
+
+* If you eclipse settings are set to **Build Automatically** then the project after being cloned will be built.
+
+* To build a particular project in the Project menu select **Build Project**. All the dependencies for the project will be built automatically. 
+
+#### Project Specific JDK settings within Eclipse ####
+1. In Eclipse go to **Project** -> **Properties** 
+
+2. Select **Builders**
+
+3. Pick **Ant_Builder** -> click **Edit**
+
+4. Click on the **JRE** tab
+
+5. Select **Separate JRE** -> **Installed JREs**
+
+6. On Windows and Unix-based systems pick JDK1.6xxx. If it is not listed under the installed JREs, click on **Add**, browse your file system to where JDK1.6xxx resides and select. On OSx systems pick the JVM 1.6.0. 
+
+
+## Running JPF ##
+
+### Using the command line ###
+
+
+~~~~~~~~ {.bash}
+> cd jpf-core
+> java -jar build/RunJPF.jar src/examples/Racer.jpf
+JavaPathfinder v5.0 - (C) 1999-2007 RIACS/NASA Ames Research Center
+.....
+====================================================== statistics
+elapsed time:       0:00:00
+states:             new=9, visited=1, backtracked=4, end=2
+search:             maxDepth=5, constraints=0
+choice generators:  thread=8, data=0
+heap:               gc=8, new=291, free=32
+instructions:       3112
+max memory:         79MB
+loaded code:        classes=73, methods=1010
+
+====================================================== search finished: 1/12/10 2:30 PM
+~~~~~~~~
+
+### Using eclipse plugin ###
+
+ 1. Right click on a .jpf file. Examples can be found in the src\examples directory in jpf-core
+ 1. If the eclipse plugin is correctly installed, a Verify option will appear. 
+ 1. Select the Verify option and the verification process of the system specified in the .jpf file begins
+
+Note that the Application.jpf file essentially replaces previous uses of eclipse launch configurations. The required element of a .jpf file is the `target=MAIN_CLASS` where `MAIN_CLASS` is the class containing main method of the system under test. Any other configurations that need to be specified can be added here. for example `listener=gov.nasa.jpf.tools.SearchMonitor`.   
+
+Specify `classpath=PATH_TO_BIN_DIRECTORY` to add the class files for the program under test to JPF's class path.  Windows users will need to use the double-backslash notation in specifying paths in the .jpf file.  An example .jpf file for the Windows platform is included below for convenience:
+
+~~~~~~~~ {.bash}
+target=mutex.DekkerTestMain
+report.console.property_violation=error,trace,snapshot
+listener=gov.nasa.jpf.listener.EndlessLoopDetector
+classpath=C:\\Users\\fred\\Documents\\ch02-mutex\\bin
+sourcepath=C:\\Users\\fred\\Documents\\ch02-mutex\\src,C:\\Users\\Fred\\Documents\\ch02-mutex\\src-test
+~~~~~~~~
+
+The .jpf file not only indicates the `target` and `classpath`, but it also turns on error reporting with trace generation (`report.console.property_violation`) and configures the source path (`sourcepath`).  Note that multiple source directories are specified using the comma separator.
+
+### Using eclipse Run Configuration ###
+
+ 1. Select a .jpf file by clicking on it in the Package Explorer
+ 1. Click **Run** -> **Run Configurations** -> **run-JPF-core**. It is important the correct .jpf file is selected when the configuration is run. 
+
+#### Windows users ####
+After a fresh install of jpf-core you may see the following when trying to use the Eclipse Run-jpf-core configuration:
+
+~~~~~~~~
+java.lang.NoClassDefFoundError: gov/nasa/jpf/Main
+Caused by: java.lang.ClassNotFoundException: gov.nasa.jpf.Main
+    at java.net.URLClassLoader$1.run(Unknown Source)
+    at java.security.AccessController.doPrivileged(Native Method)
+    at java.net.URLClassLoader.findClass(Unknown Source)
+    at java.lang.ClassLoader.loadClass(Unknown Source)
+    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
+    at java.lang.ClassLoader.loadClass(Unknown Source)
+Exception in thread "main" 
+~~~~~~~~
+
+In this particular case, the error was generated after the initial build after the clone had completed.  To resolve the issue, **refresh the Eclipse workspace** (F5 or right click Refresh).  After the refresh, the Run-jpf-core configuration should work as described above.
diff --git a/doc/install/eclipse-jpf.md b/doc/install/eclipse-jpf.md
new file mode 100644 (file)
index 0000000..bae977d
--- /dev/null
@@ -0,0 +1,24 @@
+# eclipse-jpf #
+
+This is a plugin to launch JPF on selected application property files (*.jpf) from within Eclipse. This plugin is the Eclipse analog to netbeans-jpf. This plugin is minimal by design and serves mostly to start an external JPF process from within Eclipse.
+
+To this end, the plugin adds a "Verify.." popup menu item for *.jpf files that are selected in the package explorer window. 
+
+Depending on the selected application property file, output will appear either in the eclipse console view or a jpf-shell.
+
+The site.properties location can be entered in the "JPF Preferences" pane within the normal Eclipse Preferences window. The default location is `$HOME/.jpf/site.properties`.
+
+This project is *not* a normal JPF project, i.e. it does not follow the usual JPF directory structure and artifacts. This is an Eclipse plugin project that builds into a jar file (attached) that installs into Eclipse as a plugin. Of course that means you need an Eclipse installation that includes the Plugin Development Environment (PDE) in order to build this project.
+
+### Repository ###
+The eclipse-jpf source repository is located on http://babelfish.arc.nasa.gov/hg/jpf/eclipse-jpf
+
+### Installation ###
+If you have the eclipse PDE installed, the preferred way is to get the eclipse-jpf sources from the repository, build via the eclipse plugin Export Wizard into the <eclipse-home>/dropins folder and restart eclipse.
+
+If you don't have the eclipse PDE, you can either install this plugin via "Eclipse/Help/Install New Software..." by entering the the eclipse-jpf update site URL: 
+
+      http://babelfish.arc.nasa.gov/trac/jpf/raw-attachment/wiki/projects/eclipse-jpf/update
+
+or download the attached eclipse-jpf_<version>.jar file and move it into your <eclipse-home>/dropins folder. In both cases, you need to restart eclipse in order to load the plugin. Note however that this might not be the latest eclipse-jpf version and it might or might not work with your eclipse.
\ No newline at end of file
diff --git a/doc/install/eclipse-plugin.md b/doc/install/eclipse-plugin.md
new file mode 100644 (file)
index 0000000..2bcbb3a
--- /dev/null
@@ -0,0 +1,15 @@
+# Installing the Eclipse JPF plugin #
+
+Note that this is assuming the latest Eclipse and Java versions, which is Eclipse 4.3.x and Java 7 at the time of this writing. Older versions might or might not work.
+There are three different ways to install the plugin, which are listed in the order of preference if you want to ensure that you are using the latest plugin version:
+
+### Build from sources ###
+If you have the Eclipse Plugin Development Environment installed (PDE -comes with standard Eclipse distribution), the preferred way is to download the [eclipse-jpf](./eclipse-jpf) sources from the repository, build the plugin into your <eclipse>/dropins/plugins directory, and restart Eclipse. This ensures you are using the latest version of the plugin, and the build process will tell you if your Eclipse is compatible.
+
+### Install from attached plugin jar ###
+Alternatively, you can download one of the attached eclipse-jpf_<version>.jar files, place it into your <eclipse>/dropins/plugins directory, and restart Eclipse.
+
+### Install from update site ###
+The most convenient, but least up-to-date way to install eclipse-jpf is to use the Eclipse/Help/Install New Software... dialog, by entering the the eclipse-jpf update site URL:
+
+     http://babelfish.arc.nasa.gov/trac/jpf/raw-attachment/wiki/projects/eclipse-jpf/update
diff --git a/doc/install/eclipse-plugin/update.md b/doc/install/eclipse-plugin/update.md
new file mode 100644 (file)
index 0000000..18ccf75
--- /dev/null
@@ -0,0 +1 @@
+This is the target location for the eclipse-jpf update site.
\ No newline at end of file
diff --git a/doc/install/eclipse-plugin/update/features.md b/doc/install/eclipse-plugin/update/features.md
new file mode 100644 (file)
index 0000000..f22f5b8
--- /dev/null
@@ -0,0 +1 @@
+Nothing to see here, just a place holder to upload the eclipse-jpf feature.
\ No newline at end of file
diff --git a/doc/install/eclipse-plugin/update/plugins.md b/doc/install/eclipse-plugin/update/plugins.md
new file mode 100644 (file)
index 0000000..180eefa
--- /dev/null
@@ -0,0 +1 @@
+Nothing to see here, just a place holder to upload the eclipse-jpf plugin.
\ No newline at end of file
diff --git a/doc/install/index.md b/doc/install/index.md
new file mode 100644 (file)
index 0000000..b7f40b8
--- /dev/null
@@ -0,0 +1,19 @@
+# How to Obtain and Install JPF #
+
+The JPF core and most of its extensions are pure Java applications, so they are not many platform requirements other than sufficient memory and a reasonably fast machine. Use of IDEs is optional, but most JPF modules include out-of-the-box configuration files for both Eclipse and Netbeans.
+
+You can obtain JPF sources from the [Mercurial](http://mercurial.selenic.com/wiki/) repositories, but it is not recommended to clone this directory itself (you most likely would get old sub-repository revisions). You need at least the core of JPF, [jpf-core](../jpf-core/index) which can be built with [Ant](http://ant.apache.org) from the command line, or directly opened as a [NetBeans](http://www.netbeans.org) or [Eclipse](http://www.eclipse.org) project.
+
+The JPF core project already come with its configuration file, but you have to create a per-site [site.properties](site-properties) file.
+
+If you use the JPF shells (graphical JPF front-ends), you might also want to install the corresponding NetBeans or Eclipse adapter plugins, although shells are standalone Java (swing) applications that can also be used without an IDE.
+
+Here are the details:
+
+  - [System requirements](requirements)
+  - [Downloading binary snapshots](snapshot)
+  - [Downloading sources from the Mercurial repositories](repositories)
+  - [Creating a site properties file](site-properties)
+  - [Building, testing, and running](build)
+  - [Installing the Eclipse plugin](eclipse-plugin)
+  - [Installing the NetBeans plugin](netbeans-plugin)
\ No newline at end of file
diff --git a/doc/install/netbeans-jpf.md b/doc/install/netbeans-jpf.md
new file mode 100644 (file)
index 0000000..539e0df
--- /dev/null
@@ -0,0 +1,10 @@
+# netbeans-jpf #
+
+This is a plugin to launch JPF on selected application properties (*.jpf) files from within NetBeans. No worries, this is a minimal plugin that mainly starts an external process (JPF), so it doesn't muck with your NetBeans views.
+
+The main functions are
+
+ - start the JPF shell in an external process
+ - wait for editor positioning requests from the JPF shell, and show the corresponding source lines
+
+Why is this named differently than all the other projects? Because it is a different kind of project: this is *not* a normal JPF project, i.e. it is not a freeform NetBeans project with the usual JPF directory structure and artifacts. It is a NetBeans module, and it's main artifact is a *.nbm file in the build directory.
\ No newline at end of file
diff --git a/doc/install/netbeans-plugin.md b/doc/install/netbeans-plugin.md
new file mode 100644 (file)
index 0000000..c5360a4
--- /dev/null
@@ -0,0 +1,11 @@
+# Installing the NetBeans JPF plugin #
+ 1. Download and install [jpf-core](../jpf-core/index), e.g. from the [Mercurial repository](../install/repositories)
+ 2. -------------- take a break ---------------
+ 3. Download the `gov-nasa-jpf-netbeans-runjpf.nbm` file from [here](http://babelfish.arc.nasa.gov/trac/jpf/attachment/wiki/install/netbeans-plugin/gov-nasa-jpf-netbeans-runjpf.nbm).
+ 4. From within Netbeans go to **Tools**->**Plugins** (Alt+T followed by Alt+g)
+ 5. Select the **Downloaded** tab
+ 6. Click on **Add Plugins...** (Alt+A)
+ 7. Select the `gov-nasa-jpf-netbeans-runjpf.nbm` file that was downloaded in step 3
+ 8. Select **install**
+ 9. Agree to the License agreement
+ 10. Restart Netbeans
\ No newline at end of file
diff --git a/doc/install/repo_shell.md b/doc/install/repo_shell.md
new file mode 100644 (file)
index 0000000..e5609b1
--- /dev/null
@@ -0,0 +1,74 @@
+# Checkout and Build from Shell Prompt (Unix) #\r
+\r
+While there is a CVS repository on the JPF Sourceforge site, it is not in use anymore. The current version is kept in the Subversion repository.\r
+\r
+<note> We will shift to a distributed version control system (Mercurial or Git) soon</note>\r
+\r
+\r
+# SVN #\r
+\r
+This is not a general introduction of [nor do we cover details of [[https://sourceforge.net/svn/?group_id=136825|Subversion on Sourceforge]((SVN),)(http://subversion.tigris.org|Subversion]]). To obtain the current JPF version, execute the following command from a shell prompt:\r
+\r
+~~~~~~~~ {.bash}\r
+>svn checkout https://javapathfinder.svn.sourceforge.net/svnroot/javapathfinder/trunk\r
+~~~~~~~~\r
+\r
+To update later-on, enter from within one of the javapathfinder directories\r
+\r
+\r
+~~~~~~~~ {.bash}\r
+>svn update\r
+~~~~~~~~\r
+\r
+To commit (in case you are a project member and have a sourceforge account), use\r
+\r
+\r
+~~~~~~~~ {.bash}\r
+>svn commit -m "commit message" \r
+~~~~~~~~\r
+\r
+In order to build and test JPF from the commandline, you need Ant and JUnit. If you do not want to use the scripts and versions that are provided with JPF, make sure you have set up your *CLASSPATH* to contain both tools. As of Ant 1.6.5 and JUnit 4.1, this involves the following environment settings:\r
+\r
+\r
+~~~~~~~~ {.bash}\r
+>export PATH=$PATH:<your-ant-dir>/bin\r
+>export CLASSPATH=<your-ant-dir>/lib/ant.jar:<your-junit-dir>/junit-4.1.jar\r
+~~~~~~~~\r
+\r
+~~~~~~~~\r
+<note tip> for your convenience, we have added all required external libraries \r
+and scripts to the *build-tools* directory, so you do not have to install \r
+any of the external components.</note>\r
+~~~~~~~~\r
+Now you can proceed as described in section Building JPF from a Command Line. For the impatient reader, this is mainly one command\r
+\r
+\r
+~~~~~~~~ {.bash}\r
+>cd javapathfinder-trunk\r
+>build-tools/bin/ant run-tests\r
+\r
+Buildfile: build.xml\r
+\r
+init:\r
+    [mkdir] Created dir: /users/pcmehlitz/projects/javapathfinder-trunk/build\r
+ ...\r
+compile-jpf:\r
+    [javac] Compiling 543 source files to /users/pcmehlitz/projects/javapathfinder-trunk/build/jpf\r
+ ...\r
+run-tests:\r
+     [echo] --- running Junit tests from build/test..\r
+    [junit] Running TestJavaLangObjectJPF\r
+    [junit] Tests run: 1, Failures: 0, Errors: 0, Time elapsed: 0.876 sec\r
+ ...\r
+BUILD SUCCESSFUL\r
+Total time: 2 minutes 25 seconds\r
+~~~~~~~~\r
+\r
+or (especially for non-Unix folk)\r
+\r
+\r
+~~~~~~~~ {.bash}\r
+>java RunAnt run-tests\r
+~~~~~~~~\r
+\r
+which should compile the whole system and runs the regression test suite with the provided Ant and JUnit versions.
\ No newline at end of file
diff --git a/doc/install/repositories.md b/doc/install/repositories.md
new file mode 100644 (file)
index 0000000..7d43566
--- /dev/null
@@ -0,0 +1,68 @@
+# JPF Source Repository Access #
+
+JPF sources are kept as [Mercurial](http://www.selenic.com/mercurial) repositories within the http://babelfish.arc.nasa.gov/hg/jpf directory. You need to clone the subrepositories (e.g. http://babelfish.arc.nasa.gov/hg/jpf/jpf-core) that you are interested in, **not** the root directory ../hg/jpf itself (which most likely will give you old subrepo revisions).
+
+We provide anonymous, public read access. If you want to push your changes back to the repository, and you are not a NASA Ames employee, you need to [obtain a JPF contributor account](wiki:about/account).
+
+Mercurial is a [Distributed Version Control System](http://betterexplained.com/articles/intro-to-distributed-version-control-illustrated/) (DVCS), like Git. If you are not familiar with this, it means "all repositories are created equal", and you have to read up a bit. The foremost authority is ["Mercurial: The Definite Guide"](http://hgbook.red-bean.com/).
+
+For the inpatient, we also provide a short [Mercurial primer](../devel/mercurial).
+
+## Command Line Access ##
+
+To check out the jpf-core use the mercurial command `clone`:
+
+~~~~~~~~ {.bash}
+> cd ~/projects
+
+> hg clone http://babelfish.arc.nasa.gov/hg/jpf/jpf-core
+destination directory: jpf-core
+requesting all changes
+...
+added 71 changesets with 2045 changes to 1694 files
+updating working directory
+683 files updated, 0 files merged, 0 files removed, 0 files unresolved
+~~~~~~~~
+
+The same process can be repeating by substituting `jpf-core` with the [project](../projects/index) you are interested in. You can install the projects wherever you want, but you have to remember where you installed them for the subsequent [site.properties](../install/site-properties) configuration.
+
+To update your local repository, change to its directory and do `pull` (don't forget the `-u` option, or your working directories will not get updated) 
+
+~~~~~~~~ {.bash}
+> cd ~/projects/jpf-core
+> hg pull -u
+~~~~~~~~
+
+If you want - and are allowed - to push back your changes, you use **`https://`**`babelfish.arc.nasa.gov/hg/jpf/<project>` as the URL, which will require entering your user-name and password. Before pushing the changes you have to commit the changes from your working directory to your local repository. 
+
+~~~~~~~~ {.bash}
+> cd ~/projects/jpf-core
+> hg commit -m "this commits to the local repository"
+~~~~~~~~
+
+The changes now can be pushed to the central repository using the following command
+
+~~~~~~~~ {.bash}
+> hg push https://babelfish.arc.nasa.gov/hg/jpf/jpf-core
+~~~~~~~~
+
+## Mercurial Support within NetBeans ##
+
+There is no need to install any plugins, NetBeans is distributed with Mercurial support.
+
+
+## Mercurial Plugin for Eclipse ##
+
+To work within Eclipse
+
+* Download and install the [MercurialEclipse](http://javaforge.com/project/HGE) plugin, which at the time of this writing is available from the update site: http://cbes.javaforge.com/update (the usual Eclipse spiel: **Help** -> **Install New Software...** -> **add site**, enter the update URL above) 
+
+* In the eclipse menu: **File** -> **Import** -> **Mercurial** -> **Clone repository using Mercurial** -> **Next**
+
+* In the repository location, URL, specify http://babelfish.arc.nasa.gov/hg/jpf/jpf-core
+
+* Check the box for 'Search for .project files in clone and use them to create projects'
+
+* Click on **Finish** 
+
+The steps listed above will clone the repository in your workspace. Right clicking on the project will show a 'Team' option that allows to perform all the version control operations. 
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
new file mode 100644 (file)
index 0000000..ccec451
--- /dev/null
@@ -0,0 +1,79 @@
+# System Requirements #
+
+## Java ##
+Most of the JPF components, including the [jpf-core](../jpf-core/index), are pure Java applications. The minimal version is Java SE 8 (if you have to use JDK 1.7 or JDK 1.6 you have to check out the 'java-7' and 'java-1.6' branches of our repository, respectively), we generally advise to use the latest stable Java version that is available for your platform. You can find out about your java by running the following statement from the command line.
+
+~~~~~~~~ {.bash}
+> java -version
+java version "1.8.0_20"
+...
+~~~~~~~~
+
+JPF is a resource hungry application. We recommend at least 2Gb of memory, and generally use a `-Xmx1024m` setting when launching Java. The disk footprint for most JPF projects is fairly small, the jpf-core takes about 40M when fully expanded with sources, compiled classes and jar files. The binary distribution (jar files) takes less than 1.5M.
+
+Some JPF projects do require 3rd party native executables (like DLLs) that are platform specific. Please refer to the specific project pages for details.
+
+### Java specifics for Windows ###
+Make sure you have the JDK installed, otherwise there is no javac compiler available.
+
+In order to build JPF from a Windows Command Prompt (executing `ant.bat` from inside the respective JPF project directory), you have to set the `JAVA_HOME` environment variable. 
+
+### Java specifics for OS X ###
+On Mac OS X 10.10, Java 1.7 is default, but `/Applications/Utilities/Java Preferences.app` can change the setting. In some cases, it may be necessary to manually change the symlink that determines which version is default:
+
+~~~~~~~~ {.bash}
+sudo rm /System/Library/Frameworks/JavaVM.framework/Versions/Current
+sudo ln -s 1.8 /System/Library/Frameworks/JavaVM.framework/Versions/Current
+~~~~~~~~
+
+## Mercurial (Version Control System) ##
+If you want to download the JPF source repositories, you need to install the [Mercurial](http://mercurial.selenic.com/wiki/) distributed version control system on your machine, which requires [Python](http://www.python.org). If you are using a Windows machine, you can install [TortoiseHg](http://tortoisehg.bitbucket.org/), which provides a Windows Explorer extension and includes Python.
+
+On some Mac OS X systems, it may be necessary to set the `LC_ALL` and `LANG` environment variables for Mercurial to work correctly.
+
+in `~/.bashrc`:
+
+~~~~~~~~ {.bash}
+export LC_ALL=en_US.UTF-8
+export LANG=en_US.UTF-8 
+~~~~~~~~
+
+in `~/.cshrc`:
+
+~~~~~~~~ {.bash}
+setenv LC_ALL en_US.UTF-8
+setenv LANG en_US.UTF-8 
+~~~~~~~~
+
+
+If you already have Eclipse installed, and want to download the source repositories from within the IDE, you need the [MercurialEclipse](http://javaforge.com/project/HGE) plugin, which you can install from this update-site: http://cbes.javaforge.com/update
+
+Note that NetBeans comes with Mercurial support by default.
+
+
+## Apache Ant ##
+
+Although you can also build from Eclipse, we use [Apache Ant](http://ant.apache.org) as our primary build system. **Ant is no longer included in the jpf-core distribution** so you have to install it separately. Currently (as of Ant 1.9.3), this involves
+
+ * getting Ant binaries e.g. from http://www.apache.org/dist/ant/binaries/
+ * setting the `ANT_HOME` environment variable to the directory where you unpacked the binaries
+ * adding `ANT_HOME/bin` to your `PATH` environment variable
+
+
+## JUnit ##
+
+Our Ant script (build.xml) includes a `test` target which uses [JUnit](http://junit.org) to run regression tests. **JUnit is no longer included in the jpf-core distribution**. For JUnit-4.11 installation involves the following steps
+
+ * get junit-<version>.jar and hamcrest-core-<version>.jar, e.g. from the links on https://github.com/junit-team/junit/wiki/Download-and-Install
+ * add both jars to your `CLASSPATH` environment variable 
+
+
+## JPF IDE plugins ##
+
+JPF components come with project configurations for both [NetBeans](http://www.netbeans.org) and [Eclipse](http://www.eclipse.org), so you might want to use your favorite IDE. Since the JPF build process is [Ant](http://ant.apache.org)-based, NetBeans is generally a better fit because it is Ant-based and can make direct use of your JPF site configuration.
+
+If you want to install the [Eclipse plugin](./eclipse-jpf), you need an Eclipse version >= 3.5 (Galileo) **running under JavaSE-1.8**. Please see the [Installing the Eclipse JPF plugin](./eclipse-plugin) page for details.
+
+If you want to go with Eclipse and have to rebuild the JPF [Eclipse plugin](./eclipse-jpf), make sure you install the Eclipse Plugin Development Environment (PDE) from the respective Eclipse server.
+
+If you want to use the [NetBeans plugin](./netbeans-jpf), the minimal NetBeans version is 6.5.
\ No newline at end of file
diff --git a/doc/install/site-properties.md b/doc/install/site-properties.md
new file mode 100644 (file)
index 0000000..691fa4d
--- /dev/null
@@ -0,0 +1,32 @@
+# Creating  a site.properties file #
+
+The site.properties file tells JPF at startup time where to look for installed projects, so that it can add classpaths accordingly without you having to type off your fingers. It is a normal [Java properties](http://en.wikipedia.org/wiki/.properties) file, which supports a few additional things like key expansion.
+
+While you can tell JPF at startup time where to look for `site.properties`, we recommend using the default location, which is **`<user.home>/.jpf/site.properties`**. If you don't know what value the standard Java system property `user.home` has on your machine, please run the attached Java program. On Unix systems, this is your home directory.
+
+Assuming that you installed your JPF projects under `<user.home>/projects/jpf`, a typical `site.properties` looks like this:
+
+~~~~~~~~ {.bash}
+# JPF site configuration
+
+jpf-core = ${user.home}/projects/jpf/jpf-core
+
+# numeric extension
+jpf-numeric = ${user.home}/projects/jpf/jpf-numeric
+
+# annotation-based program properties extension
+jpf-aprop = ${user.home}/projects/jpf/jpf-aprop
+
+extensions=${jpf-core},${jpf-aprop}
+
+#... and all your other installed projects
+~~~~~~~~ 
+
+If you are a Windows user, and you want to enter absolute pathnames, don't use unquoted backslashes '\' since the `java.util.Properties` parser would interpret these as special chars (like `"\n"`). You can use ordinary slashes '/' instead. To avoid drive letters, use system properties like `${user.home}`.
+
+
+A sample site.properties file is attached to this page. Note that the "`${..}`" terms are automatically expanded by JPF, i.e. you do not have to enter explicit paths.
+
+Each installed project is defined by a "`<project-name> = <project-directory>`" key/value pair. The project name is usually the same as the repository name.
+
+Note that we don't require anymore that all projects are in the extensions list, **but** jpf-core (or wherever your JPF core classes are) now needs to be in there. In fact, you probably want to have only `jpf-core` in `extensions`, and use the `@using <project-name>` for the other ones from either your project properties (jpf.properties, for project dependencies) or - usually - from your application properties (*.jpf) files. See [JPF configuration](../user/config) for details.
\ No newline at end of file
diff --git a/doc/install/snapshot.md b/doc/install/snapshot.md
new file mode 100644 (file)
index 0000000..d7839c1
--- /dev/null
@@ -0,0 +1,14 @@
+# Downloading Binary Snapshots #
+
+
+Available binary snapshots are attached as *.zip archives to the [jpf-core](../jpf-core/index) page. Just click the on the download link and tell your browser where to store them on disk, which you need to remember for your subsequent [site.properties](../install/site-properties) configuration. We recommend putting all JPF modules under a single parent directory that holds the site.properties file:
+
+~~~~~~~~ {.bash}
+jpf/
+     site.properties
+     jpf-core/
+     jpf-symbc/
+     …
+~~~~~~~~
+
+Many JPF modules are still fast moving, so we recommend using the source repositories to stay up-to-date. Our policy is to only push changes to this server which pass all regression tests
\ No newline at end of file
diff --git a/doc/intro/classification.md b/doc/intro/classification.md
new file mode 100644 (file)
index 0000000..667e755
--- /dev/null
@@ -0,0 +1,25 @@
+# JPF Key Features #
+
+By now, it should be clear that JPF is not just a model checker: it is a JVM that can be used for model checking. However, if you are familiar with formal methods, you might want to know what kind of model checking methods and features are supported.
+
+Some of the basic model checking traits are:
+
+* **Explicit State** model checking is JPF's standard mode of operation. It means JPF keeps track of concrete values of local variables, stackframes, heap objects and thread states. Other than the intentionally different scheduling behavior, JPF should produce the same results like a normal JVM. Of course it is slower (it is a JVM running on top of a JVM, doing a lot of additional work).
+
+* **Symbolic Execution** means that JPF can perform program execution with symbolic values obtained from how a certain variable was used along the current path of execution (e.g. `x>0 && x<43`). Moreover, JPF can even mix concrete and symbolic execution, or switch between them. Please see the Symbolic Pathfinder project documentation for details.
+
+* **State Matching** is a key mechanism to avoid unnecessary work. The execution state of a program mainly consists of heap and thread-stack snapshots. While JPF executes, it checks every new state if it already has seen an equal one, in which case there is no use to continue along the current execution path, and JPF can backtrack to the nearest non-explored non-deterministic choice. What variables contribute to state matching, and how state matching is performed can be controlled by the user.
+
+* **Backtracking** means that JPF can restore previous execution states, to see if there are unexplored choices left. For instance, if JPF reaches a program end state, it can walk backwards to find different possible scheduling sequences that have not been executed yet. While this theoretically can be achieved by re-executing the program from the beginning, backtracking is a much more efficient mechanism if state storage is optimized.
+
+* **Partial Order Reduction** is what JPF employs to minimize context switches between threads that do not result in interesting new program states. This is done on-the-fly, i.e. without prior analysis or annotation of the program, by examining which instructions can have inter-thread effects. While this is fast, it cannot handle the “diamond case”, since it cannot look ahead of the current execution. 
+
+JPF's flexibility is achieved by an architecture that provides a large number of extension points, of which the most important ones are:
+
+ * search strategies - to control in which order the state space is searched
+ * listeners - to monitor and interact with JPF execution (e.g. to check for new properties)
+ * native peers - to model libraries and execute code at the host VM level
+ * bytecode factories - to provide different execution models (like symbolic execution)
+ * publishers - to generate specific reports
+
+In general, flexibility through configuration is JPF's answer to the scalability problem of software model checking.
\ No newline at end of file
diff --git a/doc/intro/index.md b/doc/intro/index.md
new file mode 100644 (file)
index 0000000..8fcec1f
--- /dev/null
@@ -0,0 +1,17 @@
+# Introduction #
+JPF started as a software model checker, but nowadays, JPF is a runtime system with many different execution modes and extensions which go beyond model checking. All the various modes of JPF, while serving different purposes, used to verify Java programs by
+
+ * detecting and explaining defects
+ * collecting "deep" runtime information like coverage metrics
+ * deducing interesting test vectors and creating corresponding test drivers
+ * and many more...   
+
+Although JPF is mostly associated with software model checking, it can be applied in variety of ways.  People often confuse this with testing, and indeed JPF's notion of model checking can be close to systematic testing. Below we use a simple example that illustrates the differences.
+
+Here is the outline of this section:
+
+  * [What is JPF?](what_is_jpf)
+  * [Testing vs. Model Checking](testing_vs_model_checking)
+    * [Random value example](random_example)
+    * [Data race example](race_example)
+  * [JPF key features](classification)
diff --git a/doc/intro/race_example.md b/doc/intro/race_example.md
new file mode 100644 (file)
index 0000000..bb7074b
--- /dev/null
@@ -0,0 +1,105 @@
+# Example: Data Race #
+
+That's nice, but of course we also could have provoked the error in our random value example by using explicit loops instead of the `Random.nextInt()` calls, i.e. by explicitly enumerating all possible `a` and `b` values in our program. This would be typically done in a program that is a dedicated test driver, in a process which is called *systematic testing*. However, the program we want to verify might not be a test driver, and we might not even have the sources so that we could modify it accordingly.
+
+But the real show stopper for systematic testing lies within the instructions representing choices: at the application level, we might neither be aware of that there are choices, what the choice values are, nor be able to explicitly pick them.
+
+To demonstrate this point, let us look at a little concurrency example using two threads of execution. Quite obviously, the program produces different results depending on if line (2) or (4) gets executed first. But assuming we can't control what happens in (1) and (2), this time we cannot explicitly enumerate the choices - they are made by the system scheduler, i.e. outside of our application.
+
+~~~~~~~~ {.java}
+public class Racer implements Runnable {
+     int d = 42;
+     public void run () {
+          doSomething(1000);                   // (1)
+          d = 0;                               // (2)
+     }
+     public static void main (String[] args){
+          Racer racer = new Racer();
+          Thread t = new Thread(racer);
+          t.start();
+          doSomething(1000);                   // (3)
+          int c = 420 / racer.d;               // (4)
+          System.out.println(c);
+     }
+     static void doSomething (int n) {
+          // not very interesting..
+          try { Thread.sleep(n); } catch (InterruptedException ix) {}
+     }
+}
+~~~~~~~~
+
+Chances are, we don't encounter this defect at all during normal testing:
+
+~~~~~~~~ {.bash}
+> java Racer
+10
+> 
+~~~~~~~~
+
+Not so with JPF. Being a real virtual machine, there is nothing we can't control. And being a different kind of a Java virtual machine, JPF recognizes that 'racer' is an object that is shared between two threads, and hence executes all possible statement sequences / scheduling combinations in which this object can be accessed. This time, we give the complete output, which also shows the trace (the execution history) that lead to the defect found by JPF:
+
+~~~~~~~~ {.bash}
+> bin/jpf Racer
+JavaPathfinder v4.1 - (C) 1999-2007 RIACS/NASA Ames Research Center
+====================================================== system under test
+application: /Users/pcmehlitz/tmp/Racer.java
+
+====================================================== search started: 5/24/07 12:32 AM
+10
+10
+
+====================================================== error #1
+gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty
+java.lang.ArithmeticException: division by zero
+        at Racer.main(Racer.java:20)
+
+====================================================== trace #1
+------------------------------------------------------ transition #0 thread: 0
+gov.nasa.jpf.jvm.choice.ThreadChoiceFromSet {>main}
+      [insn w/o sources](282)
+  Racer.java:15                  : Racer racer = new Racer();
+  Racer.java:1                   : public class Racer implements Runnable {
+      [insn w/o sources](1)
+  Racer.java:3                   : int d = 42;
+  Racer.java:15                  : Racer racer = new Racer();
+  Racer.java:16                  : Thread t = new Thread(racer);
+      [insn w/o sources](51)
+  Racer.java:16                  : Thread t = new Thread(racer);
+  Racer.java:17                  : t.start();
+------------------------------------------------------ transition #1 thread: 0
+gov.nasa.jpf.jvm.choice.ThreadChoiceFromSet {>main,Thread-0}
+  Racer.java:17                  : t.start();
+  Racer.java:19                  : doSomething(1000);                   // (3)
+  Racer.java:6                   : try { Thread.sleep(n); } catch (InterruptedException ix) {}
+      [insn w/o sources](2)
+  Racer.java:6                   : try { Thread.sleep(n); } catch (InterruptedException ix) {}
+  Racer.java:7                   : }
+  Racer.java:20                  : int c = 420 / racer.d;               // (4)
+------------------------------------------------------ transition #2 thread: 1
+gov.nasa.jpf.jvm.choice.ThreadChoiceFromSet {main,>Thread-0}
+  Racer.java:10                  : doSomething(1000);                   // (1)
+  Racer.java:6                   : try { Thread.sleep(n); } catch (InterruptedException ix) {}
+      [insn w/o sources](2)
+  Racer.java:6                   : try { Thread.sleep(n); } catch (InterruptedException ix) {}
+  Racer.java:7                   : }
+  Racer.java:11                  : d = 0;                               // (2)
+------------------------------------------------------ transition #3 thread: 1
+gov.nasa.jpf.jvm.choice.ThreadChoiceFromSet {main,>Thread-0}
+  Racer.java:11                  : d = 0;                               // (2)
+  Racer.java:12                  : }
+------------------------------------------------------ transition #4 thread: 0
+gov.nasa.jpf.jvm.choice.ThreadChoiceFromSet {>main}
+  Racer.java:20                  : int c = 420 / racer.d;               // (4)
+
+====================================================== search finished: 5/24/07 12:32 AM
+>
+~~~~~~~~
+
+Looking at the output created by our test program, we see the result `"10"` printed twice, but that doesn't confuse us anymore. From our first example, we know this simply means that JPF first tried two scheduling sequences that normally terminated the program without provoking the defect, before finally picking the one that causes the error.
+
+It still might be a bit confusing that the printed trace contains some source lines twice. Ignoring the details of its choice generation mechanism, this is caused by JPF executing bytecode instructions, not source lines, and a single source line can easily get translated into a number of bytecode instructions. This would go away if we configure JPF so that it reports the executed bytecode, but at the cost of much larger trace that is harder to read. What is more interesting is that JPF tells us about the thread choices it made in each transition (the lines starting with `gov.nasa.jpf.jvm.ThreadChoice..`).
diff --git a/doc/intro/random_example.md b/doc/intro/random_example.md
new file mode 100644 (file)
index 0000000..3499e9f
--- /dev/null
@@ -0,0 +1,95 @@
+# Example: `java.util.Random` #
+
+We start with a simple example that uses java.util.Random. Consider the following program that obtains two random values in (2) and (3), and then performs some computation (4) with them.
+
+~~~~~~~~ {.java}
+import java.util.Random;
+
+public class Rand {
+     public static void main (String[] args) {
+          Random random = new Random(42);      // (1)
+          
+          int a = random.nextInt(2);           // (2)
+          System.out.println("a=" + a);
+          
+          //... lots of code here
+          
+          int b = random.nextInt(3);           // (3)
+          System.out.println("  b=" + b);
+         
+          int c = a/(b+a -2);                  // (4)
+          System.out.println("    c=" + c);         
+     }
+}
+~~~~~~~~
+
+
+## Testing ##
+
+Executing this program with a normal Java VM yields something like the following output. If we don't provide an explicit seed when creating the Random object in (1), the result is going to differ between runs, but every run will choose just a single `a` and `b` value (i.e. print just a single `"a=.."` and `"b=.."` line.
+
+~~~~~~~~ {.java}
+> java Rand
+a=1
+  b=0
+    c=-1
+> 
+~~~~~~~~
+
+Let's look at a graphical representation of all the ways our program could be executed, and how it actually was executed in our test run. The nodes of the graph represent *program states*, and the edges *transitions* the execution could take from a certain state.
+
+![Figure 1: Random example](../graphics/sw-model-checking.svg){align=center width=700}
+
+## Model Checking ##
+
+Enter JPF - not much different results if we start JPF as a plain 'java' replacement. The only difference is that it (a) takes longer to complete, and (b) tells us something about a "search", which hints on that something more than in our test run is going on
+
+~~~~~~~~ {.bash}
+> bin/jpf Rand
+JavaPathfinder v4.1 - (C) 1999-2007 RIACS/NASA Ames Research Center
+====================================================== system under test
+application: /Users/pcmehlitz/tmp/Rand.java
+
+====================================================== search started: 5/23/07 11:48 PM
+a=1
+  b=0
+    c=-1
+
+====================================================== results
+no errors detected
+
+====================================================== search finished: 5/23/07 11:48 PM
+>
+~~~~~~~~
+
+What is this "search" supposed to mean? Looking at source line (4) we realize that there is a potential problem: for certain `a` and `b` values, this expression can cause a division by zero `ArithmeticException`. Depending on the random seed used in (1), it's quite possible we would never encounter this case if we run (i.e. test) the program with a normal JVM.
+
+Re-enter JPF - but this time we tell it to not only consider single values for `a` and `b`, but look at all possible choices:
+
+~~~~~~~~ {.bash}
+> bin/jpf +cg.enumerate_random=true Rand
+JavaPathfinder v4.1 - (C) 1999-2007 RIACS/NASA Ames Research Center
+====================================================== system under test
+application: /Users/pcmehlitz/tmp/Rand.java
+
+====================================================== search started: 5/23/07 11:49 PM
+a=0
+  b=0
+    c=0
+  b=1
+    c=0
+  b=2
+
+====================================================== error #1
+gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty
+java.lang.ArithmeticException: division by zero
+        at Rand.main(Rand.java:15)
+....
+>
+~~~~~~~~
+
+What has happened? By specifying "+vm.enumerate_random=true" we told JPF to consider all possible values for expressions (2) and (3). JPF starts with `"a=0"`, then picks `"b=0"`, which yields a valid `"c=0"` value. But instead of terminating like a normal VM, JPF recognized that there are more choices left, so it backtracks to (3) and picks `"b=1"`. Again, no problem here with computing `"c=0"`. Back to (3), JPF now tries `"b=2"`, which of course spells disaster for our little program when executing (4), as can be seen by the following error report.
+
+Here is a graphical representation of this search process. It should be noted that JPF per default only runs up to the point where it finds an error or there are no more choices left to explore. But if we would somehow fix the `"a=0,b=2"` case, JPF would still find the `"a=1,b=1"` case in the next run, since it systematically tries all choices. No matter what error it finds, JPF also keeps the complete trace (execution path) how it got to this error (denoted by the red arrows), which means we don't have to debug the program to find out.
+
+![Figure 2: Random example](../graphics/sw-model-checking-2.svg){align=center width=450}
diff --git a/doc/intro/testing_vs_model_checking.md b/doc/intro/testing_vs_model_checking.md
new file mode 100644 (file)
index 0000000..56b41d5
--- /dev/null
@@ -0,0 +1,28 @@
+# Testing vs. Model Checking #
+
+So what JPF does is test our program for defects? No, it usually does more, at least when used as a model checker. How does testing differ from model checking?
+
+Software testing is an empirical set of techniques where you execute your program with a number of inputs in order to find out if it behaves correctly. This comes with two parts that involve the right choices: test input and test oracle.
+
+![Figure 1: Testing](../graphics/states-testing.svg){align=center}
+
+Testing techniques differ on how we choose the input (random, "interesting" problem domain values like corner cases etc.), and on how much knowledge about the SUT and its execution environment we assume (black/grey/white box), which especially affects how we can define and check correct behavior. This involves a lot of educated guesses, or as Edsger Dijkstra has put it: "program testing can at best show the presence of errors but never their absence". We usually compensate this by performing "enough" tests - which would be the next guess. Testing complex systems can easily turn into finding a needle in a haystack. If you are a good tester, you make the right guesses and hit the defect that is inevitably there. If not, don't worry - your users will find it later.
+
+![Figure 2: Model checking](../graphics/states-mc.svg){align=center}
+
+[Model Checking](http://en.wikipedia.org/wiki/Model_checking) as a [Formal Method](http://en.wikipedia.org/wiki/Formal_methods) does not depend on guesses. At least as the theory goes, if there is a violation of a given specification, model checking will find it. Model checking is supposed to be a rigorous method that exhaustively explores all possible SUT behaviors. 
+
+To illustrate this, look at the [Random value example](random_example), which shows how testing differs from model checking if we have a program that uses a sequence of random values: the test always processes just one set of values at a time, and we have little control over which ones. Model checking doesn't stop until it has checked all data combinations or has found an error.
+
+With the random example, we can at least see the choices in our program. Consider a concurrent program, such as [Data race example](race_example) - do you know where the operating system switches between threads? All we know is that different scheduling sequences can lead to different program behavior (e.g. if there are data races), but there is little we can do in our tests to force scheduling variation. There are program/test spec combinations which are "untestable". Being a virtual machine, our software model checker doesn't suffer the same fate - it has complete control over all threads of our program, and can execute all scheduling combinations.
+
+![Figure 3: Threads interleaving](../graphics/interleavings.svg){align=center width=550}
+
+That is the theory. In reality "all possible" can be a pretty large number - too large for existing computing resources or our patience. Just assume the number of different scheduling sequences of a program consisting of N threads P,,1,, .. P,,N,, that each have n,,i,, atomic instruction sequences.
+
+For 2 threads with 2 atomic sections each this gives us 6 different scheduling combinations. For 8 sections the result is 12870, 16 sections yield 601080390 - that is why it is called *state explosion*. Software model checking has a scalability problem, even more so than model checking of hardware, since programs usually have a lot more states.
+
+There are several things we can do. First, we can optimize the model checker, which is simply an engineering feat. Next, we can find program states which are equivalent with respect to the properties we are checking, which can be done with various degrees of abstractions and value representations. Last, we can try to get to the defect first, before we run out of time or memory. But this is where the boundary between testing and model checking becomes blurred, as it involves things like heuristics about interesting input values or system behaviors, and these heuristics tend to drop reachable program states.
+
+JPF does all of the above to curb the state space explosion, and most things are configured instead of hardwired, so you don't depend on built-in heuristics. But no matter how much we shrink the state space, JPF can still observe a lot more about the program execution than normal tests (i.e. we can be more precise with our oracles), and it still knows about the execution history in case we find a defect, which is always just the first part - we also need to understand it so that it can be fixed eventually. 
+
diff --git a/doc/intro/what_is_jpf.md b/doc/intro/what_is_jpf.md
new file mode 100644 (file)
index 0000000..e8a5ce2
--- /dev/null
@@ -0,0 +1,44 @@
+# What is JPF? #
+
+That depends on how you configure it. 
+First and foremost, there is no single, monolithic JPF - it is a runtime configured combination of different components. The project *jpf-core* is the essential component that constitutes the core structure of JPF. 
+Here we explain what *jpf-core* is.
+<!--- JPF is designed in a way which can be easily extended.
+We therefore restrict ourselves here to what the *jpf core* is, but keep in mind it is only primus inter pares among JPF components. --->
+
+## The Core : a VM that supports Model Checking ##
+
+![Figure 1: High level view of JPF](../graphics/jpf-basic.svg){align=center width=400}
+
+The JPF core is a Virtual Machine (VM) for Java™ bytecode. That means that JPF is a program which executes the system under test (SUT). The VM of JPF is able to handle bytecode instructions that are created by a standard Java compiler. While execution semantics of Java bytecodes are clearly defined in [Sun's Java virtual machine specifications](http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html/), we have little hardwired semantics in JPF - the VM instruction set is represented by a set of classes that can be replaced. 
+JPF is a special VM. JPF itself is written in Java, so it is not as fast as your normal Java. That implies  that JPF is VM which is running on top of another VM. 
+
+As JPF executes the SUT, it checks for certain properties which are given to JPF as input. Some of the properties that can be checked by JPF are unhandled exceptions, deadlocks, and user-defined assertions which are used to test properties of the code's behavior.
+JPF gets back to you with a report that says if the properties hold and/or which verification artifacts have been created by JPF for further analysis (like test cases). 
+
+JPF (theoretically) explores *all* potential executions in a systematic way. In contrast, an ordinary JVM executes the code in only one possible way.
+Basically, JPF can identify points, which represent *execution choices*, in your program from which the execution could proceed differently. Then it systematically explores all of them.
+Typical execution choices are different scheduling sequences or random values, but JPF allows you to introduce your own type of choices such as user input or state machine events.
+
+The number of paths can grow out of hand, and it usually will. This is known as the most challenging problem in software model checking referred to as *state space explosion problem*. 
+JPF uses different ways to mitigate this problem. The first line of defense employed by JPF is *state matching*: each time JPF reaches a choice point, it checks if it has already seen a similar program state, in which case it can safely abandon this path, *backtrack* to a previous choice point that has still unexplored choices, and proceed from there. That's right, JPF can restore program states, which is like telling a debugger "go back 100 instructions".
+
+So what are these features used for? Normally to find defects in the SUT, but what kind of defects? By now you know the answer: it depends on how you configure JPF. By default, the core checks for properties that can be identified by the VM without you having to specify them: deadlocks and unhandled exceptions (which also covers Java `assert` expressions). We call these *non-functional* properties, and no application should violate them. But JPF doesn't stop there - you can define your own properties, which is mostly done with so called *listeners*, little "plugins" that let you closely monitor all actions taken by JPF, like executing single instructions, creating objects, reaching a new program state and many more. An example of a property implemented in the form of a listener is a race detector, which identifies unsynchronized access to shared variables in concurrent programs (the JPF core comes with two of them).
+
+One additional feature that comes in handy in case JPF finds a defect is the complete execution history that leads to the defect, including every executed bytecode instruction. We call this a program *trace*, and it is invaluable to find out what really caused the defect. Think of a deadlock - usually there is not much you can directly deduce from a snapshot of call stacks.
+
+All this explains why JPF is called "a debugger toolbox on steroids": first it automatically executes your program in all possible ways to find defects you don't even know about yet, then it explains you what caused these defects.
+
+## Caveat : not a lightweight tool ##
+
+Of course that is the ideal world. In reality, this can require quite a lot of configuration and even some programming. JPF is not a "black box" tool like a compiler, and the learning curve can be steep. What makes this worse is that JPF cannot execute *native code* which is implemented in a language other than Java but invoked from Java applications. 
+Not because it doesn't know how to do that, but because it often doesn't make sense: native code like system calls to write to a file cannot easily be reverted - JPF would loose its capability to match or backtrack program states. But of course there is a remedy, and it is configurable: *native peers* and *model classes*. Native peers are classes that hold methods that are executed in lieu of real native methods. This code is executed by the real Java VM, not JPF, hence it can also speed up things. Model classes are simple replacements of standard classes, like `java.lang.Thread` that provide alternatives for native methods which are fully observable and backtrackable.
+
+If you are familiar with Java implementations, you know about the humongous proportions of the included libraries, and hence it is obvious that we cannot handle all of them, at least not yet. There is no theoretical limit, and implementing missing library methods is usually pretty easy, but you should be prepared to encounter `UnsatisfiedLinkErrors` and such if you let JPF loose on large production systems. Notorious white spots are `java.io` and `java.net`, but there are people working on it. Who knows, maybe you are interested to join the effort - JPF is open sourced and there is nothing you can't see. We now have more than two dozen major collaborators in industry, government and academia around the world.
+
+So what makes it worthwhile to invest in JPF? After all, it is a heavyweight tool, not a quick and simple bug finder. First, if you are looking for a research platform to try out your new software verification ideas, chances are you can get along with JPF in a fraction of time compared to native production VMs, which are typically optimized towards speed and care little about extensibility or observability.
+
+The second answer is that - as of this writing - there are bugs *only* JPF can find (before the fact, that is), and there are more and more Java applications that cannot afford to learn about these bugs after the fact. JPF is a tool for mission critical applications, where failure is not an option. No surprise it was started by NASA.
+
+
+
diff --git a/doc/jpf-core/AssertionProperty.md b/doc/jpf-core/AssertionProperty.md
new file mode 100644 (file)
index 0000000..21c49e8
--- /dev/null
@@ -0,0 +1,88 @@
+# AssertionProperty #
+
+This listener adds some special capabilities for `java.lang.AssertionError` processing:
+
+ * report `AssertionErrors` that are otherwise carelessly caught in `... catch (Throwable t) {..}` clauses
+ * ignore AssertionErrors if `ap.go_on` is set
+
+`AssertionErrors` differ a bit from other exceptions - they should never be handled, and they usually represent functional properties (as opposed to non-functionals like `NullPointerExceptions` and `ArithmeticExceptions`). Consequently, it can be regarded as an error if an `AssertionError` is swallowed by a 'catch-all', and it is at least an option to ignore an unhandled `AssertionError`, e.g. to see what non-functional defects - if any - it might cause downstream.
+
+### Example 1: catch-all ###
+
+~~~~~~~~ {.java}
+public class CatchAll {
+  public static void main(String[] args){
+    try {
+      int d = 42;
+      //.. some stuff
+      assert d > 50 : "d below threshold";
+      int x = d - 50;
+      //.. some more stuff
+    } catch (Throwable t) { // bad
+      // ..I'm careless
+    }
+  }
+}
+~~~~~~~~
+
+Checked with:
+
+~~~~~~~~ {.bash}
+>jpf +listener=.listener.AssertionProperty CatchAll
+~~~~~~~~
+
+Produces:
+
+~~~~~~~~
+JavaPathfinder v5.0 - (C) 1999-2007 RIACS/NASA Ames Research Center
+====================================================== system under test
+application: CatchAll.java
+
+====================================================== search started: 10/13/09 1:33 PM
+
+====================================================== error #1
+gov.nasa.jpf.listener.AssertionProperty
+d below threshold
+...
+~~~~~~~~
+
+
+### Example 2: go-on ###
+
+~~~~~~~~ {.java}
+public class GoOn {
+  public static void main (String[] args){
+    int d = 42;
+    // lots of computations...
+    assert d > 50 : "d below threshold";
+    int x = Math.max(0, d - 50);
+    // lots of more computations...
+    int y = 42000 / x;
+  }
+}
+~~~~~~~~
+
+Checked with 
+
+~~~~~~~~ {.bash}
+>jpf +listener=.listener.AssertionProperty +ap.go_on GoOn
+~~~~~~~~
+
+Produces:
+
+~~~~~~~~ {.bash}
+JavaPathfinder v5.0 - (C) 1999-2007 RIACS/NASA Ames Research Center
+
+====================================================== system under test
+application: GoOn.java
+
+====================================================== search started: 10/13/09 1:59 PM
+WARNING - AssertionError: d below threshold
+       at GoOn.main(GoOn.java:5)
+
+====================================================== error #1
+gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty
+java.lang.ArithmeticException: division by zero
+       at GoOn.main(GoOn.java:8)
+...
+~~~~~~~~
diff --git a/doc/jpf-core/ErrorTraceGenerator.md b/doc/jpf-core/ErrorTraceGenerator.md
new file mode 100644 (file)
index 0000000..0edf13c
--- /dev/null
@@ -0,0 +1,32 @@
+## Error Trace Generator ##\r
+\r
+This is a listener to output a lightweight error trace. It prints the instructions at POR boundaries or points where there are multiple choices. An example is shown below.  \r
+\r
+~~~~~~~~\r
+\r
+====================### Lightweight Error Trace ###=======================\r
+\r
+\r
+Length of Error Trace: 35\r
+--------------------------------------------------- Thread1\r
+ Event.wait_for_event(oldclassic.java:79)\r
+      wait();\r
+--------------------------------------------------- Thread2\r
+ SecondTask.run(oldclassic.java:129)\r
+      if (count == event2.count) { // <race> ditto\r
+--------------------------------------------------- Thread2\r
+ SecondTask.run(oldclassic.java:127)\r
+      event1.signal_event();       // updates event1.count\r
+--------------------------------------------------- Thread2\r
+ SecondTask.run(oldclassic.java:133)\r
+      count = event2.count;        // <race> ditto\r
+--------------------------------------------------- Thread1\r
+ FirstTask.run(oldclassic.java:103)\r
+        event1.wait_for_event();\r
+--------------------------------------------------- Thread1\r
+\r
+~~~~~~~~\r
+\r
+Configuration: **+listener=gov.nasa.jpf.listener.ErrorTraceGenerator**\r
+\r
+Note the Error trace generator does not have the same memory bottlenecks as **report.console.property_violation=trace** that stores every bytecode instruction executed along the path from the start of the program to the error state. The error trace generator dynamically recreates the counterexample by tracing back to the start from the error state. The head of the error trace (list shown in the example) represents the last instruction in the error trace while the tail represents the first instruction.  
\ No newline at end of file
diff --git a/doc/jpf-core/ExceptionInjector.md b/doc/jpf-core/ExceptionInjector.md
new file mode 100644 (file)
index 0000000..4ab7766
--- /dev/null
@@ -0,0 +1,105 @@
+# ExceptionInjector #
+
+The ExceptionInjector is a listener that can throw user configured exceptions at arbitrary program locations. The main purpose is to ease the testing of exception handler code that would otherwise hard to reach, e.g. because it is not clear if/how an exception could be thrown in 3rd party code.
+
+## Properties ##
+
+~~~~~~~~ {.bash}
+**ei.exception** = <exception-spec>;... \
+
+  <exception-spec> := <type>'@'<location> \
+
+  <type> := <exception-classname>[[string-literal]('(') ')'] \
+
+  <location> := <classname>':'line | <classname>'.'<method-spec>[ [[BR](':'line])]
+~~~~~~~~
+
+Relative line numbers count from the first executable statement in the method body. They are mostly used to have tests that are more robust against changes in the target source file.
+
+If a method is given without a line offset, the exception is thrown before executing INVOKE instructions that call the specified method.
+
+If a line is specified (either absolute or method-body relative), the exception is thrown before executing the associated bytecode at this line.
+
+If more than one exception specification is given, these need to be separated by semicolons ';' (commas cannot be used because they can appear within argument type lists of the target method).
+
+Method argument types have to be specified the same way as they are reported by 'javap', i.e. with fully qualified type names in dot notation (e.g. "`foo(java.lang.String,int[])`"). Return types can be omitted.  
+
+`ei.throw_first` [`boolean`] - if true, throw exception on first bytecode instruction associated with the given (absolute or relative) source line. If false, throw on last associated bytecode instruction
+
+## Examples ##
+
+### (1) throw exception on absolute line 42 in xyz.MyClass: ###
+
+The application property file
+
+~~~~~~~~ {.bash}
+target = yxz.MyClass
+ei.exception = ArithmeticException@xyz.MyClass:42
+~~~~~~~~
+on file
+~~~~~~~~ {.java}
+1:  package x.y.z;
+    ..
+    public class MyClass {
+      ..
+      try {
+        ..
+42:     int z = x / y;
+        ..
+      } catch (ArithmeticException ax){
+        // the handler code to test
+      }
+     ..
+~~~~~~~~
+
+will throw an ArithmeticException on line 42 regardless of the 'x' and 'y' values. 
+
+### (2) throw a `Zapp("gotcha")` exception on (relative) line 2 in method body `xyz.MyClass.foo()` ###
+
+The application property file
+
+~~~~~~~~ {.bash}
+target = yxz.MyClass
+ei.exception = Zapp("gotcha")@xyz.MyClass.foo(int[]):2
+~~~~~~~~
+on file
+~~~~~~~~ {.java}
+    package x.y.z;
+    ..
+    public class MyClass {
+      ..
+      void foo (int[] whatever) {
+        // some comment (doesn't count for line offsets)
++0:     int n = ..       // first statement line in foo()
++1:     // some more comment (does count for line offsets)
++2:     doSomething(n);  // can throw a Zapp exception 
+     ..
+~~~~~~~~
+will throw a Zapp("gotcha") exception on relative line 2 of method body xyz.MyClass.foo(). Note that the line offset counts from the first executable statement line within foo().
+
+
+### (3) throw an `IOException` when calling `File.createTempFile()` ###
+The application property file
+
+~~~~~~~~ {.bash}
+target = yxz.MyClass
+ei.exception = java.io.IOException@java.io.File.createTempFile(java.lang.String,java.lang.String)
+~~~~~~~~
+
+will throw an exception on the first call of File.createTempFile(), regardless of where this occurs and what the parameters to the call are
+
+~~~~~~~~ {.java}
+1:  package x.y.z;
+    ..
+    public class MyClass {
+      ..
+      try {
+        ..
+        File tmp = File.createTempFile(prefix,suffix);
+        ..
+      } catch (IOException iox) {
+        // the handler code to test
+      }
+     ..
+~~~~~~~~    
diff --git a/doc/jpf-core/IdleFilter.md b/doc/jpf-core/IdleFilter.md
new file mode 100644 (file)
index 0000000..03c6e98
--- /dev/null
@@ -0,0 +1,99 @@
+# IdleFilter #
+
+The `gov.nasa.jpf.listener.IdleFilter` is a listener that can be used to close state spaces with loops. Consider a simple "busy waiting" loop
+
+~~~~~~~~ {.java}
+for (long l=0; l<10000000; l++);
+~~~~~~~~
+
+While not a good thing to do in general, it is benign if executed in a normal VM. For JPF, it causes trouble because it adds a lot of useless steps to the stored path, and slows down execution considerably.
+
+In addition, people who expect JPF to match states can get surprised by programs like
+
+~~~~~~~~ {.java}
+while (true){
+  // no transition break in here
+}
+~~~~~~~~
+not being state matched, and hence not terminating (it wouldn't terminate in a normal VM either). 
+
+`IdleFilter` is a little tool to deal with such (bad) loops. It counts the number of back-jumps it encounters within the same thread and stackframe, and if this number exceeds a configured threshold it takes one of the following actions:
+
+ * warn - just prints out a warning that we have a suspicious loop
+ * break - breaks the transition at the back-jump `goto` instruction, to allow state matching
+ * prune - sets the transition ignored, i.e. prunes the search tree
+ * jump - skips the back-jump. This is the most dangerous action since you better make sure the loop does not contain side-effects your program depends on.
+
+
+### Properties ###
+Consequently, there are two options:
+
+ * `idle.max_backjumps = \<number\>` : max number of back-jumps that triggers the configured action (default 500)
+ * `idle.action = warn|break|prune|jump` : action to take when number of back-jumps exceeds threshold
+
+### Examples ###
+
+**(1)** The test program
+
+~~~~~~~~ {.java}
+...
+public void testBreak () {
+  int y = 4;
+  int x = 0;
+
+  while (x != y) { // JPF should state match on the backjump
+    x = x + 1;
+    if (x > 3) {
+      x = 0;
+    }
+  }
+}
+~~~~~~~~
+
+would never terminate under JPF or a host VM. Running it with
+
+~~~~~~~~ {.bash}
+> bin/jpf +listener=.listener.IdleFilter +idle.action=break ...
+~~~~~~~~
+
+does terminate due to state matching and produces the following report
+
+~~~~~~~~ {.bash}
+...
+====================================================== search started: 4/8/10 4:14 PM
+[WARNING] IdleFilter breaks transition on suspicious loop in thread: main
+        at gov.nasa.jpf.test.mc.basic.IdleLoopTest.testBreak(gov/nasa/jpf/test/mc/basic/IdleLoopTest.java:42)
+...
+====================================================== results
+no errors detected
+~~~~~~~~
+
+-----
+**(2)** The following program would execute a long time under JPF
+
+~~~~~~~~ {.java}
+...
+public void testJump () {
+  for (int i=0; i<1000000; i++){
+    //...
+  }
+
+  System.out.println("Ok, jumped past loop");
+}
+~~~~~~~~
+
+If we run it with
+
+~~~~~~~~ {.bash}
+> bin/jpf +listener=.listener.IdleFilter +idle.action=jump ...
+~~~~~~~~
+
+JPF comes back quickly with the result
+
+~~~~~~~~ {.bash}
+====================================================== search started: 4/8/10 4:20 PM
+[WARNING] IdleFilter jumped past loop in: main
+        at gov.nasa.jpf.test.mc.basic.IdleLoopTest.testJump(gov/nasa/jpf/test/mc/basic/IdleLoopTest.java:74)
+Ok, jumped past loop
+...
+~~~~~~~~
diff --git a/doc/jpf-core/index.md b/doc/jpf-core/index.md
new file mode 100644 (file)
index 0000000..28fda0e
--- /dev/null
@@ -0,0 +1,43 @@
+# jpf-core #
+This is the basis for all JPF projects, i.e. you always need to install it. jpf-core contains the basic VM and model checking infrastructure, and can be used to check for concurrency defects like deadlocks, and unhandled exceptions like `NullPointerExceptions` and `AssertionErrors`.
+
+
+## Repository ##
+The Mercurial repository is on http://babelfish.arc.nasa.gov/hg/jpf/jpf-core
+
+## Properties ##
+jpf-core supports two rather generic properties, which are configured by default:
+
+ * `gov.nasa.jpf.jvm.NoUncaughtExceptionsProperty`
+ * `gov.nasa.jpf.jvm.NotDeadlockedProperty`
+
+There is no need to parameterize any of them. `NoUncaughtExceptionsProperty` covers all `Throwable` objects that are not handled within the application, i.e. would terminate the process.
+
+Some of the listeners (like `PreciseRaceDetector`) are `ListenerAdapter` instances, i.e. work as more specific Property implementations.
+
+## Listeners ##
+jpf-core includes a variety of [listeners](../devel/listener) that fall into three major categories:
+
+ * program properties
+ * execution monitoring
+ * execution control
+
+Some of the main listeners are
+
+ * [AssertionProperty](./AssertionProperty)
+ * [IdleFilter](./IdleFilter)
+ * [ExceptionInjector](./ExceptionInjector)
+
+## Properties ##
+jpf-core uses many JPF properties, you can find most of them in the `defaults.properties` file. The following ones are of interest for users
+
+ * `listener` - a comma separated list of class names representing listeners that should be automatically instantiated and registered during JPF startup
+ * `listener.autoload` - a comma separated list of annotation types. If JPF encounters such an annotation in one of the analyzed classes at runtime, it automatically loads and registered the associated listener
+ * `listener.<annotation-type>` - class name of the listener associated with `<annotation-type>`
+ * `vm.insn_factory.class` - class name of a [`BytecodeInstructionFactory`](../devel/bytecode_factory), e.g. to switch to the symbolic execution mode or to use specific bytecode implementations for checking numeric properties 
+ * `vm.halt_on_throw (true|false)` - tells JPF if it should try to find a handler if it encounters an exception in the analyzed program (useful to avoid masking exceptions within handlers)
+ * [`cg.randomize_choices`](../user/config/random) `(random|path|def)` - tells JPF if it should randomize the order of choices for each [`ChoiceGenerator`](../devel/choicegenerator), to avoid degenerated searches (e.g. always indexing with the main thread in scheduling choices).
+ * `report.console.property_violation` - comma separated list of topics that should be printed by JPF if it detects an error. Possible values include 
+    - `error` error description
+    - `snapshot` thread stacks 
+    - `trace` instruction/statement trace (can be long and memory-expensive)
\ No newline at end of file
diff --git a/doc/papers/chicago-author-date.csl b/doc/papers/chicago-author-date.csl
new file mode 100644 (file)
index 0000000..595235f
--- /dev/null
@@ -0,0 +1,549 @@
+<?xml version="1.0" encoding="utf-8"?>
+<style xmlns="http://purl.org/net/xbiblio/csl" class="in-text" version="1.0" demote-non-dropping-particle="never" page-range-format="chicago">
+  <info>
+    <title>Chicago Manual of Style 16th edition (author-date)</title>
+    <id>http://www.zotero.org/styles/chicago-author-date</id>
+    <link href="http://www.zotero.org/styles/chicago-author-date" rel="self"/>
+    <link href="http://www.chicagomanualofstyle.org/tools_citationguide.html" rel="documentation"/>
+    <author>
+      <name>Julian Onions</name>
+      <email>julian.onions@gmail.com</email>
+    </author>
+    <contributor>
+      <name>Sebastian Karcher</name>
+    </contributor>
+    <contributor>
+      <name>Richard Karnesky</name>
+      <email>karnesky+zotero@gmail.com</email>
+      <uri>http://arc.nucapt.northwestern.edu/Richard_Karnesky</uri>
+    </contributor>
+    <contributor>
+      <name>Andrew Dunning</name>
+      <email>andrew.dunning@utoronto.ca</email>
+    </contributor>
+    <category citation-format="author-date"/>
+    <category field="generic-base"/>
+    <summary>The author-date variant of the Chicago style</summary>
+    <updated>2014-05-23T03:53:32+00:00</updated>
+    <rights license="http://creativecommons.org/licenses/by-sa/3.0/">This work is licensed under a Creative Commons Attribution-ShareAlike 3.0 License</rights>
+  </info>
+  <locale xml:lang="en">
+    <terms>
+      <term name="editor" form="verb-short">ed.</term>
+      <term name="container-author" form="verb">by</term>
+      <term name="translator" form="verb-short">trans.</term>
+      <term name="editortranslator" form="verb">
+        <single>edited and translated by</single>
+        <multiple>edited and translated by</multiple>
+      </term>
+      <term name="translator" form="short">trans.</term>
+    </terms>
+  </locale>
+  <macro name="secondary-contributors">
+    <choose>
+      <if type="chapter paper-conference" match="none">
+        <names variable="editor translator" delimiter=". ">
+          <label form="verb" text-case="capitalize-first" suffix=" "/>
+          <name and="text" delimiter=", "/>
+        </names>
+      </if>
+    </choose>
+  </macro>
+  <macro name="container-contributors">
+    <choose>
+      <if type="chapter paper-conference" match="any">
+        <group prefix=", " delimiter=", ">
+          <names variable="container-author" delimiter=", ">
+            <label form="verb" suffix=" "/>
+            <name and="text" delimiter=", "/>
+          </names>
+          <names variable="editor translator" delimiter=", ">
+            <label form="verb" suffix=" "/>
+            <name and="text" delimiter=", "/>
+          </names>
+        </group>
+      </if>
+    </choose>
+  </macro>
+  <macro name="editor">
+    <names variable="editor">
+      <name name-as-sort-order="first" and="text" sort-separator=", " delimiter=", " delimiter-precedes-last="always"/>
+      <label form="short" prefix=", "/>
+    </names>
+  </macro>
+  <macro name="translator">
+    <names variable="translator">
+      <name name-as-sort-order="first" and="text" sort-separator=", " delimiter=", " delimiter-precedes-last="always"/>
+      <label form="short" prefix=", "/>
+    </names>
+  </macro>
+  <macro name="recipient">
+    <choose>
+      <if type="personal_communication">
+        <choose>
+          <if variable="genre">
+            <text variable="genre" text-case="capitalize-first"/>
+          </if>
+          <else>
+            <text term="letter" text-case="capitalize-first"/>
+          </else>
+        </choose>
+      </if>
+    </choose>
+    <names variable="recipient" delimiter=", ">
+      <label form="verb" prefix=" " text-case="lowercase" suffix=" "/>
+      <name and="text" delimiter=", "/>
+    </names>
+  </macro>
+  <macro name="substitute-title">
+    <choose>
+      <if type="article-journal article-magazine article-newspaper review review-book" match="any">
+        <text macro="container-title"/>
+      </if>
+    </choose>
+  </macro>
+  <macro name="contributors">
+    <group delimiter=". ">
+      <names variable="author">
+        <name and="text" name-as-sort-order="first" sort-separator=", " delimiter=", " delimiter-precedes-last="always"/>
+        <label form="short" prefix=", "/>
+        <substitute>
+          <names variable="editor"/>
+          <names variable="translator"/>
+          <text macro="substitute-title"/>
+          <text macro="title"/>
+        </substitute>
+      </names>
+      <text macro="recipient"/>
+    </group>
+  </macro>
+  <macro name="contributors-short">
+    <names variable="author">
+      <name form="short" and="text" delimiter=", " initialize-with=". "/>
+      <substitute>
+        <names variable="editor"/>
+        <names variable="translator"/>
+        <text macro="substitute-title"/>
+        <text macro="title"/>
+      </substitute>
+    </names>
+  </macro>
+  <macro name="interviewer">
+    <names variable="interviewer" delimiter=", ">
+      <label form="verb" prefix=" " text-case="capitalize-first" suffix=" "/>
+      <name and="text" delimiter=", "/>
+    </names>
+  </macro>
+  <macro name="archive">
+    <group delimiter=". ">
+      <text variable="archive_location" text-case="capitalize-first"/>
+      <text variable="archive"/>
+      <text variable="archive-place"/>
+    </group>
+  </macro>
+  <macro name="access">
+    <group delimiter=". ">
+      <choose>
+        <if type="graphic report" match="any">
+          <text macro="archive"/>
+        </if>
+        <else-if type="article-journal bill book chapter legal_case legislation motion_picture paper-conference" match="none">
+          <text macro="archive"/>
+        </else-if>
+      </choose>
+      <choose>
+        <if variable="issued" match="none">
+          <group delimiter=" ">
+            <text term="accessed" text-case="capitalize-first"/>
+            <date variable="accessed" delimiter=" ">
+              <date-part name="month"/>
+              <date-part name="day"/>
+            </date>
+          </group>
+        </if>
+      </choose>
+      <choose>
+        <if type="legal_case" match="none">
+          <choose>
+            <if variable="DOI">
+              <text variable="DOI" prefix="doi:"/>
+            </if>
+            <else>
+              <text variable="URL"/>
+            </else>
+          </choose>
+        </if>
+      </choose>
+    </group>
+  </macro>
+  <macro name="title">
+    <choose>
+      <if variable="title" match="none">
+        <choose>
+          <if type="personal_communication" match="none">
+            <text variable="genre" text-case="capitalize-first"/>
+          </if>
+        </choose>
+      </if>
+      <else-if type="bill book graphic legislation motion_picture report song" match="any">
+        <text variable="title" text-case="title" font-style="italic"/>
+        <group prefix=" (" suffix=")" delimiter=" ">
+          <text term="version"/>
+          <text variable="version"/>
+        </group>
+      </else-if>
+      <else-if variable="reviewed-author">
+        <group delimiter=", ">
+          <text variable="title" font-style="italic" prefix="Review of "/>
+          <names variable="reviewed-author">
+            <label form="verb-short" text-case="lowercase" suffix=" "/>
+            <name and="text" delimiter=", "/>
+          </names>
+        </group>
+      </else-if>
+      <else-if type="legal_case interview" match="any">
+        <text variable="title"/>
+      </else-if>
+      <else>
+        <text variable="title" text-case="title" quotes="true"/>
+      </else>
+    </choose>
+  </macro>
+  <macro name="edition">
+    <choose>
+      <if type="bill book graphic legal_case legislation motion_picture report song" match="any">
+        <choose>
+          <if is-numeric="edition">
+            <group delimiter=" " prefix=". ">
+              <number variable="edition" form="ordinal"/>
+              <text term="edition" form="short" strip-periods="true"/>
+            </group>
+          </if>
+          <else>
+            <text variable="edition" prefix=". "/>
+          </else>
+        </choose>
+      </if>
+      <else-if type="chapter  paper-conference" match="any">
+        <choose>
+          <if is-numeric="edition">
+            <group delimiter=" " prefix=", ">
+              <number variable="edition" form="ordinal"/>
+              <text term="edition" form="short"/>
+            </group>
+          </if>
+          <else>
+            <text variable="edition" prefix=", "/>
+          </else>
+        </choose>
+      </else-if>
+    </choose>
+  </macro>
+  <macro name="locators">
+    <choose>
+      <if type="article-journal">
+        <choose>
+          <if variable="volume">
+            <text variable="volume" prefix=" "/>
+            <group prefix=" (" suffix=")">
+              <choose>
+                <if variable="issue">
+                  <text variable="issue"/>
+                </if>
+                <else>
+                  <date variable="issued">
+                    <date-part name="month"/>
+                  </date>
+                </else>
+              </choose>
+            </group>
+          </if>
+          <else-if variable="issue">
+            <group delimiter=" " prefix=", ">
+              <text term="issue" form="short"/>
+              <text variable="issue"/>
+              <date variable="issued" prefix="(" suffix=")">
+                <date-part name="month"/>
+              </date>
+            </group>
+          </else-if>
+          <else>
+            <date variable="issued" prefix=", ">
+              <date-part name="month"/>
+            </date>
+          </else>
+        </choose>
+      </if>
+      <else-if type="legal_case">
+        <text variable="volume" prefix=", "/>
+        <text variable="container-title" prefix=" "/>
+        <text variable="page" prefix=" "/>
+      </else-if>
+      <else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
+        <group prefix=". " delimiter=". ">
+          <group>
+            <text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
+            <number variable="volume" form="numeric"/>
+          </group>
+          <group>
+            <number variable="number-of-volumes" form="numeric"/>
+            <text term="volume" form="short" prefix=" " plural="true"/>
+          </group>
+        </group>
+      </else-if>
+      <else-if type="chapter paper-conference" match="any">
+        <choose>
+          <if variable="page" match="none">
+            <group prefix=". ">
+              <text term="volume" form="short" text-case="capitalize-first" suffix=" "/>
+              <number variable="volume" form="numeric"/>
+            </group>
+          </if>
+        </choose>
+      </else-if>
+    </choose>
+  </macro>
+  <macro name="locators-chapter">
+    <choose>
+      <if type="chapter paper-conference" match="any">
+        <choose>
+          <if variable="page">
+            <group prefix=", ">
+              <text variable="volume" suffix=":"/>
+              <text variable="page"/>
+            </group>
+          </if>
+        </choose>
+      </if>
+    </choose>
+  </macro>
+  <macro name="locators-article">
+    <choose>
+      <if type="article-newspaper">
+        <group prefix=", " delimiter=", ">
+          <group>
+            <text variable="edition" suffix=" "/>
+            <text term="edition" prefix=" "/>
+          </group>
+          <group>
+            <text term="section" form="short" suffix=" "/>
+            <text variable="section"/>
+          </group>
+        </group>
+      </if>
+      <else-if type="article-journal">
+        <choose>
+          <if variable="volume issue" match="any">
+            <text variable="page" prefix=": "/>
+          </if>
+          <else>
+            <text variable="page" prefix=", "/>
+          </else>
+        </choose>
+      </else-if>
+    </choose>
+  </macro>
+  <macro name="point-locators">
+    <choose>
+      <if variable="locator">
+        <choose>
+          <if locator="page" match="none">
+            <choose>
+              <if type="bill book graphic legal_case legislation motion_picture report song" match="any">
+                <choose>
+                  <if variable="volume">
+                    <group>
+                      <text term="volume" form="short" suffix=" "/>
+                      <number variable="volume" form="numeric"/>
+                      <label variable="locator" form="short" prefix=", " suffix=" "/>
+                    </group>
+                  </if>
+                  <else>
+                    <label variable="locator" form="short" suffix=" "/>
+                  </else>
+                </choose>
+              </if>
+              <else>
+                <label variable="locator" form="short" suffix=" "/>
+              </else>
+            </choose>
+          </if>
+          <else-if type="bill book graphic legal_case legislation motion_picture report song" match="any">
+            <number variable="volume" form="numeric" suffix=":"/>
+          </else-if>
+        </choose>
+        <text variable="locator"/>
+      </if>
+    </choose>
+  </macro>
+  <macro name="container-prefix">
+    <text term="in" text-case="capitalize-first"/>
+  </macro>
+  <macro name="container-title">
+    <choose>
+      <if type="chapter paper-conference" match="any">
+        <text macro="container-prefix" suffix=" "/>
+      </if>
+    </choose>
+    <choose>
+      <if type="legal_case" match="none">
+        <text variable="container-title" text-case="title" font-style="italic"/>
+      </if>
+    </choose>
+  </macro>
+  <macro name="publisher">
+    <group delimiter=": ">
+      <text variable="publisher-place"/>
+      <text variable="publisher"/>
+    </group>
+  </macro>
+  <macro name="date">
+    <choose>
+      <if variable="issued">
+        <date variable="issued">
+          <date-part name="year"/>
+        </date>
+      </if>
+      <else-if variable="accessed">
+        <date variable="accessed">
+          <date-part name="year"/>
+        </date>
+      </else-if>
+    </choose>
+  </macro>
+  <macro name="day-month">
+    <date variable="issued">
+      <date-part name="month"/>
+      <date-part name="day" prefix=" "/>
+    </date>
+  </macro>
+  <macro name="collection-title">
+    <choose>
+      <if match="none" type="article-journal">
+        <choose>
+          <if match="none" is-numeric="collection-number">
+            <group delimiter=", ">
+              <text variable="collection-title" text-case="title"/>
+              <text variable="collection-number"/>
+            </group>
+          </if>
+          <else>
+            <group delimiter=" ">
+              <text variable="collection-title" text-case="title"/>
+              <text variable="collection-number"/>
+            </group>
+          </else>
+        </choose>
+      </if>
+    </choose>
+  </macro>
+  <macro name="collection-title-journal">
+    <choose>
+      <if type="article-journal">
+        <group delimiter=" ">
+          <text variable="collection-title"/>
+          <text variable="collection-number"/>
+        </group>
+      </if>
+    </choose>
+  </macro>
+  <macro name="event">
+    <group>
+      <text term="presented at" suffix=" "/>
+      <text variable="event"/>
+    </group>
+  </macro>
+  <macro name="description">
+    <choose>
+      <if type="interview">
+        <group delimiter=". ">
+          <text macro="interviewer"/>
+          <text variable="medium" text-case="capitalize-first"/>
+        </group>
+      </if>
+      <else>
+        <text variable="medium" text-case="capitalize-first" prefix=". "/>
+      </else>
+    </choose>
+    <choose>
+      <if variable="title" match="none"/>
+      <else-if type="thesis personal_communication speech" match="any"/>
+      <else>
+        <group delimiter=" " prefix=". ">
+          <text variable="genre" text-case="capitalize-first"/>
+          <choose>
+            <if type="report">
+              <text variable="number"/>
+            </if>
+          </choose>
+        </group>
+      </else>
+    </choose>
+  </macro>
+  <macro name="issue">
+    <choose>
+      <if type="legal_case">
+        <text variable="authority" prefix=". "/>
+      </if>
+      <else-if type="speech">
+        <group prefix=". " delimiter=", ">
+          <group delimiter=" ">
+            <text variable="genre" text-case="capitalize-first"/>
+            <text macro="event"/>
+          </group>
+          <text variable="event-place"/>
+          <text macro="day-month"/>
+        </group>
+      </else-if>
+      <else-if type="article-newspaper article-magazine personal_communication" match="any">
+        <text macro="day-month" prefix=", "/>
+      </else-if>
+      <else>
+        <group prefix=". " delimiter=", ">
+          <choose>
+            <if type="thesis">
+              <text variable="genre" text-case="capitalize-first"/>
+            </if>
+          </choose>
+          <text macro="publisher"/>
+        </group>
+      </else>
+    </choose>
+  </macro>
+  <citation et-al-min="4" et-al-use-first="1" disambiguate-add-year-suffix="true" disambiguate-add-names="true" disambiguate-add-givenname="true" givenname-disambiguation-rule="primary-name">
+    <layout prefix="(" suffix=")" delimiter="; ">
+      <group delimiter=", ">
+        <group delimiter=" ">
+          <text macro="contributors-short"/>
+          <text macro="date"/>
+        </group>
+        <text macro="point-locators"/>
+      </group>
+    </layout>
+  </citation>
+  <bibliography hanging-indent="true" et-al-min="11" et-al-use-first="7" subsequent-author-substitute="&#8212;&#8212;&#8212;" entry-spacing="0">
+    <sort>
+      <key macro="contributors"/>
+      <key variable="issued"/>
+    </sort>
+    <layout suffix=".">
+      <group delimiter=". ">
+        <text macro="contributors"/>
+        <text macro="date"/>
+        <text macro="title"/>
+      </group>
+      <text macro="description"/>
+      <text macro="secondary-contributors" prefix=". "/>
+      <text macro="container-title" prefix=". "/>
+      <text macro="container-contributors"/>
+      <text macro="edition"/>
+      <text macro="locators-chapter"/>
+      <text macro="collection-title-journal" prefix=", " suffix=", "/>
+      <text macro="locators"/>
+      <text macro="collection-title" prefix=". "/>
+      <text macro="issue"/>
+      <text macro="locators-article"/>
+      <text macro="access" prefix=". "/>
+    </layout>
+  </bibliography>
+</style>
diff --git a/doc/papers/index.md b/doc/papers/index.md
new file mode 100644 (file)
index 0000000..87fb93a
--- /dev/null
@@ -0,0 +1,133 @@
+# Related Publications #
+
+JPF has been both a research target and a system in use for a number of years. A broad collection of papers and reports is available, including the following (incomplete) list
+
+> **Notes:** some of the older papers now have mostly historical relevance. JPF has undergone a lot of changes since 2000. If you need more recent information, especially about the design and usage of current 
+JPF versions, please consult the documentation.
+
+
+---
+bibliography: ./references.bib
+csl: ./chicago-author-date.csl
+...
+
+## Core Papers ##
+
+@visser:2003
+
+@lerda:2001
+
+## JPF Infrastructure and Features ##
+
+@shafiei:breugel:2014
+
+@nshafiei:2012
+
+@kulikov:2010
+
+## Testing and Symbolic Execution ##
+
+@indradeep:2013
+
+@person:2008
+
+@pasareanu:2008
+
+@visser:2004
+
+@pasareanu:2004
+
+@artho:2003
+
+@khurshid:2003
+
+## Heuristic Search ##
+
+@groce:2004
+
+@groce:2002
+
+@groce:visser:2002
+
+## Verification of Networked Software ##
+
+@shafiei:2014
+
+@artho:2011
+
+@artho:2009
+
+@artho:2008
+
+## Explaining Counter Examples ##
+
+@grove:2003
+
+##  Applying Java PathFinder ##
+
+@shafiei:2013
+
+@stergiopoulos:2012
+
+@mehlitz:2008
+
+@penix:2005
+
+@giannakopoulou:2004
+
+@brat:2004
+
+@bordini:2003
+
+
+## Misc ##
+
+@havelund:2007
+
+@mansouri:2007
+
+@havelund:2002
+
+@pasareanu:dwyer:2003
+
+@brat:2001
+
+@visser:park:2000
+
+@havelund:2000
+
+@havelund:1999
+
+@havelund:skakkebaek:1999
+
+@havelund:Pressburger:1998
+
+## Papers from Darko Marinov's group ##
+
+@gligoric:2010
+
+@gligoric:jagannath:2010
+
+@lauterburg:2010
+
+@sobeih:2010
+
+@lauterburg:2009
+
+@gligoric:2009
+
+@damorim:2008
+
+@lauterburg:2008
+
+@gvero:2008
+
+@damorim:2007
+
+@zhou:2007
+
+@damorim:2006
+
+@damorim:pacheco:2006
+
+## References ##
\ No newline at end of file
diff --git a/doc/papers/references.bib b/doc/papers/references.bib
new file mode 100644 (file)
index 0000000..698195f
--- /dev/null
@@ -0,0 +1,636 @@
+@article{visser:2003,
+ author = {Visser, Willem and Havelund, Klaus and Brat, Guillaume and Park, Seungjoon and Lerda, Flavio},
+ title = "{M}odel {C}hecking {P}rograms",
+ journal = {Automated Software Engineering},
+ volume = {10},
+ number = {2},
+ month = {April},
+ year = {2003}, 
+ pages = {203--232},
+ publisher = {Kluwer Academic Publishers}, 
+ url = "http://dl.acm.org/citation.cfm?id=641186"
+ }
+
+@inproceedings{lerda:2001,
+ author = {Lerda, Flavio and Visser, Willem},
+ title = {Addressing Dynamic Issues of Program Model Checking},
+ booktitle = {Proceedings of the 8th International SPIN Workshop on Model Checking of Software},
+ year = {2001},
+ pages = {80--102},
+ publisher = {Springer},
+ url = "http://dl.acm.org/citation.cfm?id=380921.380931",
+} 
+
+@inproceedings{khurshid:2003,
+    author = {Sarfraz Khurshid and Corina S. P\v{a}s\v{a}reanu and Willem Visser},
+    title = {Generalized Symbolic Execution for Model Checking and Testing},
+    booktitle = {Proceedings of the Ninth International Conference on Tools and Algorithms for the Construction and Analysis of Systems},
+    year = {2003},
+    pages = {553--568},
+    publisher = {Springer},
+    url = "http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.8.8862"
+}
+
+@article{visser:2004,
+ author = {Visser, Willem and P\v{a}s\v{a}reanu, Corina S. and Khurshid, Sarfraz},
+ title = {Test Input Generation with Java PathFinder},
+ journal = {Proceedings of International Symposium on Software Testing and Analysis},
+ volume = {29},
+ number = {4},
+ month = jul,
+ year = {2004},
+ pages = {97--107},
+ publisher = {ACM},
+ url = "http://dl.acm.org/citation.cfm?id=1007526",
+} 
+
+@article{pasareanu:2004,
+year={2004},
+volume={2989},
+series={Lecture Notes in Computer Science},
+title={Verification of Java Programs Using Symbolic Execution and Invariant Generation},
+url={http://link.springer.com/chapter/10.1007%2F978-3-540-24732-6_13#},
+publisher={Springer},
+author={Păsăreanu, Corina S. and Visser, Willem},
+pages={164-181},
+}
+
+@inproceedings{person:2008,
+ author = {Person, Suzette and Dwyer, Matthew B. and Elbaum, Sebastian and P\v{a}s\v{a}reanu, Corina S.},
+ title = {Differential Symbolic Execution},
+ booktitle = {Proceedings of the 16th {ACM} {SIGSOFT} International Symposium on Foundations of Software Engineering},
+ year = {2008},
+ pages = {226--237},
+ url = {http://doi.acm.org/10.1145/1453101.1453131},
+ publisher = {ACM},
+ }
+@inproceedings{pasareanu:2008,
+  author    = {Corina S. P\v{a}s\v{a}reanu and
+               Peter C. Mehlitz and
+               David H. Bushnell and
+               Karen Gundy{-}Burlet and
+               Michael R. Lowry and
+               Suzette Person and
+               Mark Pape},
+  title     = {Combining unit-level symbolic execution and system-level concrete
+               execution for testing {NASA} software},
+  booktitle = {Proceedings of the {ACM} {SIGSOFT} International Symposium on Software
+               Testing and Analysis},
+  pages     = {15--26},
+  year      = {2008},
+  url       = {http://doi.acm.org/10.1145/1390630.1390635},
+}
+
+@inproceedings{pasareanu:2007,
+  author    = {Corina S. P\v{a}s\v{a}reanu and
+               Willem Visser},
+  title     = {Symbolic Execution and Model Checking for Testing},
+  booktitle = {Hardware and Software: Verification and Testing, Third International
+               Haifa Verification Conference},
+  pages     = {17--18},
+  year      = {2007},
+  url       = {http://dx.doi.org/10.1007/978-3-540-77966-7_5},
+}
+
+@inproceedings{artho:2003,
+  author    = {Cyrille Artho and
+               Doron Drusinsky and
+               Allen Goldberg and
+               Klaus Havelund and
+               Michael R. Lowry and
+               Corina S. P\v{a}s\v{a}reanu and
+               Grigore Rosu and
+               Willem Visser},
+  title     = {Experiments with Test Case Generation and Runtime Analysis},
+  booktitle = {Abstract State Machines, Advances in Theory and Practice},
+  pages     = {87--107},
+  year      = {2003},
+  url       = {http://dx.doi.org/10.1007/3-540-36498-6_5},
+}
+
+@article{groce:2004,
+  author    = {Alex Groce and
+               Willem Visser},
+  title     = {Heuristics for model checking Java programs},
+  journal   = {International Journal on Software Tools for Technology Transfer},
+  volume    = {6},
+  number    = {4},
+  pages     = {260--276},
+  year      = {2004},
+  url       = {http://dx.doi.org/10.1007/s10009-003-0130-9},
+}
+
+@inproceedings{groce:2002,
+  author    = {Alex Groce and
+               Willem Visser},
+  title     = {Model checking Java programs using structural heuristics},
+  booktitle = {Proceedings of International Symposium on Software Testing and Analysis},
+  pages     = {12--21},
+  year      = {2002},
+  url       = {http://doi.acm.org/10.1145/566172.566175},
+}
+
+@inproceedings{groce:visser:2002,
+  author    = {Alex Groce and
+               Willem Visser},
+  title     = {Heuristic Model Checking for Java Programs},
+  booktitle = {Proceedings of the 9th International SPIN Workshop on Model Checking of Software},
+  pages     = {242--245},
+  year      = {2002},
+  url       = {http://dx.doi.org/10.1007/3-540-46017-9_21},
+}
+
+@inproceedings{artho:2011,
+ author = {Leungwattanakit, Watcharin and Artho, Cyrille and Hagiya, Masami and Tanabe, Yoshinori and Yamamoto, Mitsuharu},
+ title = {Model checking distributed systems by combining caching and process checkpointing},
+ booktitle = {Proceedings of the 26th IEEE/ACM International Conference on International Conference on Automated Software Engineering},
+ year = {2011},
+ pages = {103--112},
+ publisher = {IEEE},
+month={November},
+ address = {Lawrence, KS, USA},
+ url  = {​http://staff.aist.go.jp/c.artho/papers/checkpointing.pdf},
+} 
+
+@inproceedings{artho:2009,
+ author = {Artho, Cyrille and Leungwattanakit, Watcharin and Hagiya, Masami and Tanabe, Yoshinori and Yamamoto, Mitsuharu},
+ title = {Cache-Based model Checking of Networked Applications: From Linear to Branching Time},
+ booktitle = {Proceedings of the 27th International Conference on Automated Software Engineering},
+ year = {2009},
+month = {November},
+ pages = {447--458},
+ publisher = {IEEE},
+ address = {Auckland, New Zealand},
+ url = {​http://staff.aist.go.jp/c.artho/papers/ase-2009.pdf},
+}
+
+@inproceedings{artho:2008,
+author={Artho, Cyrille and Leungwattanakit, Watcharin and Hagiya, Masami and Tanabe, Yoshinori},
+title={Efficient Model Checking of Networked Applications},
+year={2008},
+volume={11},
+series={Lecture Notes in Business Information Processing},
+booktitle = {Proceedings of the 46th International Conference on Objects, Components, Models and Patterns},
+publisher={Springer},
+pages={22--40},
+address={Zurich, Switzerland},
+month={June/July},
+url = {http://staff.aist.go.jp/c.artho/papers/tools-2008.pdf}
+}
+
+@inproceedings{grove:2003,
+  author    = {Alex Groce and
+               Willem Visser},
+  title     = {What Went Wrong: Explaining Counterexamples},
+  booktitle = {Proceedings of the 10th International SPIN Workshop on Model Checking of Software},
+  pages     = {121--135},
+  year      = {2003},
+  url       = {http://dx.doi.org/10.1007/3-540-44829-2_8},
+  publisher = {Springer},
+  address = {Portland},
+}
+
+@article{penix:2005,
+  author    = {John Penix and
+               Willem Visser and
+               Seungjoon Park and
+               Corina S. P\v{a}s\v{a}reanu and
+               Eric Engstrom and
+               Aaron Larson and
+               Nicholas Weininger},
+  title     = {Verifying Time Partitioning in the {DEOS} Scheduling Kernel},
+  journal   = {Formal Methods in System Design},
+  volume    = {26},
+  number    = {2},
+  pages     = {103--135},
+  year      = {2005},
+  url       = {http://dx.doi.org/10.1007/s10703-005-1490-4},
+}
+
+@article{brat:2004,
+  author    = {Guillaume P. Brat and
+               Doron Drusinsky and
+               Dimitra Giannakopoulou and
+               Allen Goldberg and
+               Klaus Havelund and
+               Michael R. Lowry and
+               Corina S. Pasareanu and
+               Arnaud Venet and
+               Willem Visser and
+               Richard Washington},
+  title     = {Experimental Evaluation of Verification and Validation Tools on Martian
+               Rover Software},
+  journal   = {Formal Methods in System Design},
+  volume    = {25},
+  number    = {2-3},
+  pages     = {167--198},
+  year      = {2004},
+  url       = {http://dx.doi.org/10.1023/B:FORM.0000040027.28662.a4},
+}
+
+@inproceedings{bordini:2003,
+  author    = {Rafael H. Bordini and
+               Michael Fisher and
+               Carmen Pardavila and
+               Willem Visser and
+               Michael Wooldridge},
+  title     = {Model Checking Multi-Agent Programs with {CASP}},
+  booktitle = {Proceedings of Computer Aided Verification},
+  pages     = {110--113},
+  year      = {2003},
+  url       = {http://dx.doi.org/10.1007/978-3-540-45069-6_10},
+}
+
+@inproceedings{giannakopoulou:2004,
+  author    = {Dimitra Giannakopoulou and
+               Corina S. Pasareanu and
+               Jamieson M. Cobleigh},
+  title     = {Assume-Guarantee Verification of Source Code with Design-Level Assumptions},
+  booktitle = {26th International Conference on Software Engineering {(ICSE} 2004),
+               23-28 May 2004, Edinburgh, United Kingdom},
+  pages     = {211--220},
+  year      = {2004},
+  url       = {http://doi.ieeecomputersociety.org/10.1109/ICSE.2004.1317443},
+}
+
+@inproceedings{mehlitz:2008,
+  author    = {Peter Mehlitz},
+  title     = {Trust Your Model - Verifying Aerospace System Models with Java Pathfinder},
+  booktitle = {Aerospace Conference, 2008 IEEE},
+  pages     = {1--11},
+  year      = {2008},
+  url       = {http://ti.arc.nasa.gov/m/pub-archive/1402/1402%20(Mehlitz).pdf},
+}
+
+@inproceedings{stergiopoulos:2012,
+ author = {Stergiopoulos, George and Tsoumas, Bill and Gritzalis, Dimitris},
+ title = {Hunting Application-level Logical Errors},
+ booktitle = {Proceedings of the 4th International Conference on Engineering Secure Software and Systems},
+ year = {2012},
+ address = {Eindhoven, The Netherlands},
+ pages = {135--142},
+ url = {http://dx.doi.org/10.1007/978-3-642-28166-2_13},
+ publisher = {Springer},
+}
+
+@article{mansouri:2007,
+ author = {Masoud Mansouri-Samani and John Penix and Lawrence Markosian},
+ title = {Assume-Guarantee Verification of Source Code with Design-Level Assumptions},
+ journal = {Software Assurance Research Program (SARP), NASA IV&V publication},
+ year = {2007},
+ pages = {203--232},
+ publisher = {Kluwer Academic Publishers}, 
+ url = {http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20080015887.pdf},    
+ }
+ @article{havelund:2002,
+  author    = {Klaus Havelund and Willem Visser},
+  title     = {Program Model Checking as a New Trend},
+  journal = {International Journal on Software Tools for Technology Transfer (STTT)},
+  volume = {4},
+  number = {1},
+  month = {October},
+  year = {2002},
+  url = {http://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/20030003734.pdf},
+}
+
+@article{pasareanu:dwyer:2003,
+  author  = "C. P\v{a}s\v{a}reanu and M. Dwyer and W. Visser.",
+  title   = "{Finding Feasible Abstract Counter-Examples}",
+  journal = "International Journal on Software Tools for Technology Transfer (STTT)",
+  year    = "2003",
+  volume  = "5",
+  number  = "1",
+  month   = "November",
+  url = "http://ti.arc.nasa.gov/m/profile/pcorina/papers/tacas_sttt01.ps"
+}
+
+@inproceedings{brat:2001,
+ author = {Guillaume Brat and Willem Visser},
+ title = {Combining Static Analysis and Model Checking for Software Analysis},
+ booktitle = {Proceedings of the 15th IEEE International Conference on Automated Software Engineering},
+ year = {2001},
+ address = {San Diego, USA},
+ month = {November},
+ url = {http://dl.acm.org/citation.cfm?id=872568},
+}
+
+@inproceedings{visser:park:2000,
+    author = {Willem Visser and Seungjoon Park and John Penix},
+    title = {Using Predicate Abstraction to Reduce Object-Oriented Programs for Model Checking},
+    booktitle = {Proceedings of the 3rd ACM SIGSOFT Workshop on Formal Methods in Software Practice},
+    year = {2000},
+    pages = {3--12},
+    month = {August},
+    url = "http://ti.arc.nasa.gov/m/tech/rse/publications/papers/SIGSOFT00/fmsp00.pdf",
+}
+
+@article{havelund:2000,
+  author    = {Klaus Havelund and
+               Thomas Pressburger},
+  title     = {Model Checking {Java} Programs using {Java} PathFinder},
+  journal   = {International Journal on Software Tools for Technology Transfer (STTT)},
+  volume    = {2},
+  number    = {4},
+  pages     = {366--381},
+  year      = {2000},
+  url       = {http://www.havelund.com/Publications/jpf-sttt.ps.Z},
+}
+
+@inproceedings{havelund:1999,
+  author    = {Klaus Havelund},
+  title     = {Java PathFinder, {A} Translator from Java to Promela},
+  booktitle = {Theoretical and Practical Aspects of {SPIN} Model Checking},
+  month     = {July},
+  year      = {1999}, 
+  url       = {http://www.havelund.com/Publications/jpf1-spin-99.pdf},
+}
+
+@inproceedings{havelund:skakkebaek:1999,
+  author    = {Klaus Havelund and
+               Jens U. Skakkeb{\ae}k},
+  title     = {Applying Model Checking in Java Verification},
+  booktitle = {Theoretical and Practical Aspects of {SPIN} Model Checking, 5th and
+               6th International {SPIN} Workshops},
+  pages     = {216--231},
+  year      = {1999},
+  url       = {http://www.havelund.com/Publications/jpf-fm99.ps.Z},
+}
+
+@article{havelund:2007,
+  author    = {Klaus Havelund},
+  title     = {Java PathFinder User Guide},
+  booktitle = {Unpublished Report},
+  year = {1999},
+  url = {​http://www.havelund.com/Publications/jpf-manual.ps.Z},
+}
+
+@article{havelund:Pressburger:1998,
+  author    = {Klaus Havelund and Thomas Pressburger},
+  title     = {Java PathFinder, A Translator from Java to Promela},
+  booktitle = {Unpublished Report},
+  year = {1998},
+  url = {​​http://www.havelund.com/Publications/jpf1-report-99.pdf},
+}
+
+@inproceedings{gligoric:2010,
+  author    = {Milos Gligoric and
+               Tihomir Gvero and
+               Vilas Jagannath and
+               Sarfraz Khurshid and
+               Viktor Kuncak and
+               Darko Marinov},
+  title     = {Test generation through programming in {UDITA}},
+  booktitle = {Proceedings of the 32nd {ACM/IEEE} International Conference on Software
+               Engineering},
+  address   = {Cape Town, South Africa},
+  month     = {May},
+  pages     = {225--234},
+  year      = {2010},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/GligoricETAL10UDITA.pdf},
+}
+
+@inproceedings{gligoric:jagannath:2010,
+  author    = {Milos Gligoric and
+               Vilas Jagannath and
+               Darko Marinov},
+  title     = {MuTMuT: Efficient Exploration for Mutation Testing of Multithreaded
+               Code},
+  booktitle = {Third International Conference on Software Testing, Verification and
+               Validation},
+  pages     = {55--64},
+  month     = {April},
+  address   = {Paris, France},
+  year      = {2010},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/GligoricETAL10MuTMuT.pdf},
+}
+
+@inproceedings{lauterburg:2010,
+  author    = {Steven Lauterburg and
+               Rajesh K. Karmani and
+               Darko Marinov and
+               Gul Agha},
+  title     = {Evaluating Ordering Heuristics for Dynamic Partial-Order Reduction
+               Techniques},
+  booktitle = {Fundamental Approaches to Software Engineering},
+  address   = {Paphos, Cyprus},
+  month     = {March},
+  pages     = {308--322},
+  year      = {2010},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/LauterburgETAL10DPORHeuristics.pdf},
+}
+
+@inproceedings{lauterburg:2009,
+  author    = {Steven Lauterburg and
+               Mirco Dotta and
+               Darko Marinov and
+               Gul A. Agha},
+  title     = {A Framework for State-Space Exploration of Java-Based Actor Programs},
+  booktitle = {24th {IEEE/ACM} International Conference on Automated Software Engineering},
+  month     = {November},
+  address   = {Auckland, New Zealand},
+  pages     = {468--479},
+  year      = {2009},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/LauterburgETAL09Basset.pdf},
+}
+
+@article{sobeih:2010,
+  author    = {Ahmed Sobeih and
+               Marcelo d'Amorim and
+               Mahesh Viswanathan and
+               Darko Marinov and
+               Jennifer C. Hou},
+  title     = {Assertion Checking in J-Sim Simulation Models of Network Protocols},
+  journal   = {Simulation: Transactions of The Society for Modeling and Simulation International},
+  volume    = {86},
+  number    = {11},
+  pages     = {651--673},
+  year      = {2010},
+  url       = {http://dx.doi.org/10.1177/0037549709349326},
+}
+
+@inproceedings{gligoric:2009,
+  author    = {Milos Gligoric and
+               Tihomir Gvero and
+               Steven Lauterburg and
+               Darko Marinov and
+               Sarfraz Khurshid},
+  title     = {Optimizing Generation of Object Graphs in Java PathFinder},
+  booktitle = {Second International Conference on Software Testing Verification and Validation},
+  month     = {April},
+  address   = {Denver, Colorado, {USA}},
+  pages     = {51--60},
+  year      = {2009},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/GligoricETAL09OptimizingGeneration.pdf},
+}
+
+@article{damorim:2008,
+  author    = {Marcelo d'Amorim and
+               Steven Lauterburg and
+               Darko Marinov},
+  title     = {Delta Execution for Efficient State-Space Exploration of Object-Oriented Programs},
+  journal   = {{IEEE} Transactions on Software Engineering},
+  volume    = {34},
+  number    = {5},
+  pages     = {597--613},
+  year      = {2008},
+  month     = {September/October},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/dAmorimETAL08DeltaExecution.pdf},
+}
+
+@inproceedings{lauterburg:2008,
+  author    = {Steven Lauterburg and
+               Ahmed Sobeih and
+               Darko Marinov and
+               Mahesh Viswanathan},
+  title     = {Incremental state-space exploration for programs with dynamically
+               allocated data},
+  booktitle = {International Conference on Software Engineering},
+  address   = {Leipzig, Germany},
+  month     = {May},
+  pages     = {291--300},
+  year      = {2008},
+  url       = {​http://mir.cs.illinois.edu/~marinov/publications/LauterburgETAL08IncrementalChecking.pdf},
+}
+
+@inproceedings{gvero:2008,
+  author    = {Tihomir Gvero and
+               Milos Gligoric and
+               Steven Lauterburg and
+               Marcelo d'Amorim and
+               Darko Marinov and
+               Sarfraz Khurshid},
+  title     = {State extensions for java pathfinder},
+  booktitle = {International Conference on Software Engineering, Demo Papers},
+  address   = {Leipzig, Germany},
+  month     = {May},
+  pages     = {863--866},
+  year      = {2008},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/GligoricETAL09OptimizingGeneration.pdf},
+}
+
+@inproceedings{damorim:2007,
+  author    = {Marcelo d'Amorim and
+               Steven Lauterburg and
+               Darko Marinov},
+  title     = {Delta execution for efficient state-space exploration of object-oriented
+               programs},
+  booktitle = {Proceedings of the {ACM/SIGSOFT} International Symposium on Software
+               Testing and Analysis},
+  address   = {London, UK},
+  month     = {July},
+  pages     = {50--60},
+  year      = {2007},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/dAmorimETAL07DeltaExecution.pdf},
+}
+
+@inproceedings{zhou:2007,
+ author = {Zhou, Yuanyuan and Marinov, Darko and Sanders, William and Zilles, Craig and d'Amorim, Marcelo and Lauterburg, Steven and Lefever, Ryan M. and Tucek, Joe},
+ title = {Delta Execution for Software Reliability},
+ booktitle = {Proceedings of the 3rd Workshop on on Hot Topics in System Dependability},
+ series = {HotDep'07},
+ year = {2007},
+ address = {Edinburgh, UK},
+ url = {http://mir.cs.illinois.edu/~marinov/publications/ZhouETAL07DeltaExecution.pdf},
+ publisher = {USENIX Association},
+} 
+
+@inproceedings{damorim:2006,
+  author    = {Marcelo d'Amorim and
+               Ahmed Sobeih and
+               Darko Marinov},
+  title     = {Optimized Execution of Deterministic Blocks in Java PathFinder},
+  booktitle = {Proceedings of Formal Methods and Software Engineering, 8th International Conference
+               on Formal Engineering Methods},
+  address   = {Macao, China}, 
+  month     =  {November},
+  pages     = {549--567},
+  year      = {2006},
+  url       = {http://mir.cs.illinois.edu/~marinov/publications/dAmorimETAL06OptimizedDeterministicBlocks.pdf},
+}
+
+@inproceedings{damorim:pacheco:2006,
+  author    = {Marcelo d'Amorim and
+               Carlos Pacheco and
+               Tao Xie and
+               Darko Marinov and
+               Michael D. Ernst},
+  title     = {An Empirical Comparison of Automated Generation and Classification
+               Techniques for Object-Oriented Unit Testing},
+  booktitle = {Proceedings of 21st {IEEE/ACM} International Conference on Automated Software Engineering},
+  address   = {Tokyo, Japan},
+  month     = {September},
+  pages     = {59--68},
+  year      = {2006},
+  url       = {​http://mir.cs.illinois.edu/~marinov/publications/dAmorimETAL06Symclat.pdf},
+}
+
+@unpublished{kulikov:2010,
+  author = {Sergey Kulikov and Nastaran Shafiei and Franck van Breugel and Willem Visser},
+  title = {{Detecting Data Races with Java PathFinder}},
+  year = "2010",
+  url = {http://www.cse.yorku.ca/~franck/research/drafts/race.pdf}
+}
+
+@inproceedings{indradeep:2013,
+  author = {Ghosh, Indradeep and Shafiei, Nastaran and Li, Guodong and Chiang, Wei-Fan},
+  title = {{JST: an automatic test generation tool for industrial Java applications with strings}},
+  booktitle = {Proceedings of the 35th International Conference on Software Engineering},
+  year = {2013},
+  month = {May},
+  address = {San Francisco, CA, USA},
+  pages = {992--1001},
+  publisher = {IEEE},
+  url = {http://dl.acm.org/citation.cfm?id=2486925}
+} 
+
+@article{nshafiei:2012,
+  author = {Shafiei, Nastaran and Mehlitz, Peter C.},
+  journal = {ACM SIGSOFT Software Engineering Notes},
+  number = 6,
+  pages = {1-5},
+  title = {{Modeling class loaders in Java PathFinder version 7}},
+  volume = 37,
+  year = 2012,
+  month = {November},
+  url = {http://dl.acm.org/citation.cfm?id=2382800}
+}
+
+@article{shafiei:2014,
+  author = {Shafiei, Nastaran and Mehlitz, Peter C.},
+  journal = {ACM SIGSOFT Software Engineering Notes},
+  number = 1,
+  pages = {1-5},
+  title = {{Extending JPF to verify distributed systems}},
+  volume = 39,
+  year = 2014,
+  month = {January},
+  url = {http://dl.acm.org/citation.cfm?id=2560577},
+}
+
+@inproceedings{shafiei:2013,
+  author    = {Nastaran Shafiei and
+               Franck van Breugel},
+  title     = {Towards model checking of computer games with Java PathFinder},
+  booktitle = {Proceedings of the 3rd International Workshop on Games and Software
+               Engineering},
+  address   = {San Francisco, CA, USA},
+  month     = {May},
+  pages     = {15--21},
+  year      = {2013},
+  url       = {http://dl.acm.org/citation.cfm?id=2662596},
+}
+
+
+@inproceedings{shafiei:breugel:2014,
+  author    = {Nastaran Shafiei and
+               Franck van Breugel},
+  title     = {Automatic handling of native methods in Java PathFinder},
+  booktitle = {Proceedings of the International SPIN Workshop on Model Checking of Software},
+  address   = {San Jose, CA, USA},
+  month     = {July},
+  pages     = {97--100},
+  year      = {2014},
+  url       = {http://dl.acm.org/citation.cfm?id=2632363},
+}
\ No newline at end of file
diff --git a/doc/user/api.md b/doc/user/api.md
new file mode 100644 (file)
index 0000000..fdfd9a6
--- /dev/null
@@ -0,0 +1,125 @@
+# The Verify API #
+Although the primary purpose of JPF is to verify applications that are not JPF dependent, it can also be used to check programs that are explicitly written to execute under JPF. There are two ways to do this
+
+ * use of jpf annotations such as `@gov.nasa.jpf.annotation.JPFConfig` and `@gov.nasa.jpf.annotation.FilterField`
+ * use of the various `gov.nasa.jpf.vm.Verify` APIs
+
+The first method provides only directives to JPF (by means of Java annotations), and does not modify or add any specific code to your system under test. The second one is imperative and should only be used by JPF specific test drivers.
+
+## JPF Annotations ##
+The JPF core currently supports two annotation types: `@JPFConfig` and `@FilterField`
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.annotation.JPFConfig
+...
+@JPFConfig({"listener+=,gov.nasa.jpf.aprop.listener.SharedChecker", ..})
+public class MyClass {
+  ..
+}
+~~~~~~~~
+
+The `@JPFConfig` annotation allows to set JPF properties (<key>=<value> pairs) for classes and methods of your application, which is especially useful to add specific listeners. You have to be aware of the property effects though, since not all behavior can be changed at runtime, and you usually cannot reverse features with this method
+
+The `@FilterField` annotation is used to mark certain fields in your application as not relevant for state matching. This is useful if you have to add debugging information like counters that would otherwise increase the state space (i.e. would prevent state matching).
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.annotation.FilterField;
+...
+public class MyClass {
+  ..
+  @FilterField int counter;
+  ..
+}
+~~~~~~~~
+
+This does not change execution of your program in any way, and also does not affect backtracking within JPF (i.e. values are properly restored). It only tells JPF to ignore marked fields when hashing/matching program states.
+
+There is a [jpf-aprop](wiki:projects/jpf-aprop) JPF module that is dedicated to Java annotation based program properties such as `@Nonnull`, `@NonShared`, `@Immutable`, `@Requires` and many others.
+
+## The Verify API ##
+
+Sometimes it is useful to create JPF specific test drivers, like you create JUnit test cases. In this code, you can make explicit use of JPF APIs, since it is not supposed to run outside JPF. There are several categories of APIs which are encapsulated in the `gov.nasa.jpf.vm.Verify` class.
+
+### Data ChoiceGenerators ###
+Data ChoiceGenerators (CGs) are suitable for writing test drivers that are JPF aware. The idea is to obtain non-deterministic input values from JPF in a way that it can systematically analyze all relevant choices. In its most simple form, this can be used like
+
+~~~~~~~~ {.java}
+// test driver code
+import gov.nasa.jpf.vm.Verify;
+..
+boolean cond = Verify.getBoolean();
+// following code is executed for both cond=true AND cond=false
+..
+~~~~~~~~
+
+The `Verify` class contains methods for creating a number of different choice generators for builtin Java types, like
+
+~~~~~~~~ {.java}
+..
+int i = Verify.getInt(min,max); // evaluates for all values between 'min' and 'max' (inclusive)
+..
+double d = Verify.getDoubleFromSet(-42.0, 0.0, 42.0 ..); // evaluates all given values
+..
+~~~~~~~~ 
+
+
+CGs don't stop at value sets that can be completely enumerated based on their type (like boolean, and - at least theoretically - integers). JPF also supports configured heuristics based choice generators, where the values sets are application dependent and can be specified in property files.
+
+![Figure: Data ChoiceGenerators](../graphics/choicegen-example.svg){align=center width=750}
+
+Choice generator implementation is described in a [separate section](wiki:devel/choicegenerator) of this documentation.
+
+### Search Pruning ###
+
+Explicitly restricting the search is useful for highly application specific properties, where it is obvious that certain values are not of interest with respect to the property.
+
+~~~~~~~~ {.java}
+// ..compute some data..
+Verify.ignoreIf(data > someValue); // prune search if true
+// ..do some stuff with data..
+~~~~~~~~
+If the provided expression evaluates to true, JPF does not continue to execute the current path, and backtracks to the previous non-deterministic choice point.
+
+### State Annotation ###
+Based on certain value combinations, an application might give JPF hints about the relevance of a program state that can be subsequently used by Search and/or Heuristic implementations.
+
+~~~~~~~~ {.java}
+// ..compute some data
+Verify.interesting( data < someValue );
+// ..do some stuff with data
+~~~~~~~~
+
+This does not stop execution by JPF, but stores an 'interesting' attribute for the current state. Its more general version is used to attach arbitrary strings to states:
+
+~~~~~~~~ {.java}
+// ..compute some data
+if (data < someValue) {
+  Verify.setAnnotation("critical data value");
+  // ..do some stuff with dat
+~~~~~~~~
+
+Again, this category is about to become less important since Search- and VMListeners are superior mechanisms to store not just strings but arbitrary objects as state annotations.
+
+### Verification Output ###
+
+This is the most simple category, which is used to differentiate between normal program output (that is executed and analyzed by JPF), and output that is strictly verification relevant, i.e. should not appear when executing a program outside JPF. Not very surprising, it contains a number of print(..) methods.
+
+### Atomicity Control ###
+
+this category can be used to control the number of thread interleavings JPF has to analyze. While this is problematic in terms of missing potential defects, it is was often the only way to constrain the state space so that JPF could verify a given application.
+
+~~~~~~~~ {.java}
+Verify.beginAtomic();
+...
+// all code in here is executed by JPF in one transition
+...
+Verify.endAtomic();
+~~~~~~~~
+
+Direct atomicity control was mainly used before the automatic, on-the-fly partial order reduction (POR) was implemented, and only remains relevant for applications that are (still) problematic with respect to POR. JPF's on-the-fly POR implementation is described in a [separate section](../devel/partial_order_reduction) of this documentation.
+
+### Other Usages ###
+
+Some `Verify` methods support collecting information during JPF execution, which is persistent and can be later-on queried by JPF embedding code (programs that execute JPF). This uses an MJI trick where the [native peer](../devel/mji) class (`JPF_gov_nasa_jpf_vm_Verify`) is used to set some data during JPF execution, which can be later-on retrieved by model class code (through `gov.nasa.jpf.vm.Verify`) that is executed outside of JPF. This is currently used to implement counters, which in turn are used to verify JPF itself.
+
+It should be noted that while most of the Verify APIs have alternative implementations that enable execution outside of JPF, applications using them at least don't build outside the JPF environment. Their use therefore is only recommended for JPF specific test drivers.
\ No newline at end of file
diff --git a/doc/user/application_types.md b/doc/user/application_types.md
new file mode 100644 (file)
index 0000000..352e9c9
--- /dev/null
@@ -0,0 +1,36 @@
+# JPF Application Goals and Types #
+
+By now, you already know that you run JPF on compiled Java programs, just like a normal VM. But what are the goals to apply JPF, and - based on these goals - what are the different JPF application types?
+
+## Why to Use JPF? ##
+Before we classify the different types of JPF applications, it is worth to spend a few thoughts on why we actually want to apply it. A word of caution: if you have a strictly sequential program with only a few well defined input values, you are probably better off writing a few tests - using JPF won't tell you much. There are two major reasons to run JPF:
+
+### Explore Execution Alternatives ###
+After all, JPF started as a software model checker, so its original domain is to explore execution choices, of which we have four different types:
+
+  - scheduling sequences - concurrent applications are still the major domain for JPF application because (a) defects like deadlocks and data races are subtle and usually spurious, and (b) the scheduler can usually not be controlled from a testing environment, i.e. this is hard to impossible to test. JPF on the other hand not only "owns" the scheduler (it is a virtual machine) and explores all interesting scheduling combinations, it even lets you define your own scheduling policies
+  - input data variation - JPF allows you to explore input value sets that can be defined by heuristics (e.g. a value below, at, and above a certain threshold). This is especially useful when writing test drivers
+  - environment events - program types like Swing or web applications usually react to external events like user input. Such events can be simulated by JPF as choice sets
+  - control flow choices - JPF can not only check how your program reacts to a concrete input, it can also turn around and systematically explore the program control structure (branch instructions) to compute input data values that would execute a certain part of your code.
+
+### Execution Inspection ###
+
+Even if your program does not have a lot of execution alternatives, you can make use of JPF's inspection capabilities. Being an extensible virtual machine, it is relatively easy to implement coverage analyzers, or non-invasive tests for conditions that would otherwise go unnoticed because they are hard or tedious to implement (like overflow in numerical instructions).
+
+## JPF Application Types ##
+There are three basic JPF application types, each of them having different strengths and weaknesses: JPF- aware, unaware, and "enabled" programs.
+
+![Figure: JPF application types](../graphics/app-types.svg){align=center width=800}
+
+### JPF Unaware Programs ###
+This is the usual case - you run JPF on an application that is not aware of verification in general, or JPF in particular. It just runs on any VM that is Java compatible. The typical reason to check such an application with JPF is to look for violations of so called non-functional properties that are hard to test for, such as deadlocks or race conditions. JPF is especially good at finding and explaining concurrency related defects, but you have to know the costs: JPF is much slower than a production VM (for a reason - it does a lot more than a normal byte code interpreter), and it might not support all the Java libraries that are used by the system under test.
+
+### JPF Dependent Programs ###
+On the other side of the spectrum we have applications that are models - their only purpose in life is to be verified by JPF (e.g. to check a certain algorithm), so Java just happens to be the implementation language because that is what JPF understands. Typically, these applications are based on a domain specific framework (such as the [statechart extension](../projects/jpf-statechart) that has been written so that JPF can verify the model. As a consequence, the model applications themselves are usually small, scale well, and do not require additional property specification. The downside is that it is quite expensive to develop the underlying domain frameworks.
+
+### JPF Enabled Programs ###
+The third category probably has the best return on investment - programs that can run on any VM, but contain Java annotations that represent properties which cannot easily be expressed with standard Java language. 
+
+For example, assume you have a class which instances are not thread safe, and hence are not supposed to be used as shared objects. You can just run JPF and see if it finds a defect (like a data race) which is caused by illegally using such an object in a multi-threaded context. But even if JPF out-of-the-box can handle the size of the state space and finds the defect, you probably still have to spend a significant effort to analyze a long trace to find the original cause (which might not even be visible in the program). It is not only more easy on the tool (means faster), but also better for understanding if you simply mark the non-threadsafe class with a `@NonShared` annotation. Now JPF only has to execute up to the point where the offending object reference escapes the creating thread, and can report an error that immediately shows you where and how to fix it. 
+
+There is an abundance of potential property-related annotations (such as `@NonNull` for a static analyzer and JPF), including support for more sophisticated, embedded languages to express pre- and post-conditions.
diff --git a/doc/user/components.md b/doc/user/components.md
new file mode 100644 (file)
index 0000000..55a01d2
--- /dev/null
@@ -0,0 +1,57 @@
+# JPF components #
+Java is more than a programming language. It is a set of layers that starts with a platform specific VM implementation (which is referred to as the *host VM*) that sits on top of native libraries written for and using your operating system. To this stack, we add JPF - a Java application that runs on top of the "host VM", but is itself a VM which then executes your System Under Test (SUT).
+
+![Figure 1: Different layers involved when using JPF](../graphics/jpf-layers.svg){align=center width=700}
+
+With all this recursion, it is easy to get confused about what Java code gets processed at which level. To make things worse,  most of the standard library class files (java.* classes) and some of the annotations get processed by both VMs, but of course in different instantiations. Purpose of this page is to shed some light on what code is associated with the different layers and parts of the system.
+
+In order to untangle, we have to step back and look at what components are involved when applying JPF to the SUT. We categorize the components with respect to two aspects:
+
+ 1. processing VM (host VM, JPF)
+ 1. associated distribution entity (host Java installation, JPF core, JPF extensions, SUT)
+
+Remember the [What is JPF](../intro/what_is_jpf) diagram from the introduction? This is a more detailed version of it. 
+
+![Figure 2: JPF infrastructure](../graphics/jpf-intro-new.svg){align=center width=800}
+
+Let us walk this diagram left-to-right. We start with the compiled Java application we want to verify. This is of course not part of the JPF distribution, but will be executed by JPF, so the corresponding class files do not have to be visible to the host VM (that runs JPF). However, the application code might use classes and interfaces that reside in the JPF distribution. Together, the application and such modeling libraries and/or annotations form the System under Test (SUT)
+
+Next comes the JPF core itself. By now we know this is mainly a VM that is written in Java itself, so you run it on whatever Java system is installed on your machine. This means all classes that constitute JPF need to be visible to the host JVM, e.g. by setting the global `CLASSPATH` environment variable (which is not the recommended way to do this). 
+
+Now comes the important part: **JPF is not a monolithic system**. It uses various configured components to do things such as
+
+ * property implementation
+ * library modeling
+ * report generation
+
+and much more. Even if you don't specify anything when you run it, JPF will use a default configuration that pulls in a lot of components such as [listeners](../devel/listener), which do not have to be part of the [JPF core](../jpf-core/index), but can reside in their own JPF extension modules.
+
+If you do specify your own [configuration](../user/config), you can tell JPF to use components that live outside of the [JPF core](../jpf-core/index) distribution, either in one of the installed JPF extension modules, or in your own project. Either way, all these [listeners](../devel/listener), [native peers](../devel/mji), [publishers](../devel/report) and other components are at runtime part of the whole  JPF system, so they need to be visible to the host VM.
+
+The last part is the most tricky one - your application is most probably using standard Java libraries (e.g. `System.out.println()`), as do the classes that constitute JPF. Some of the standard library classes have to be replaced by JPF specific versions when JPF executes the SUT (e.g. classes such as `java.lang.Class`that have native methods), and JPF has a special [MJI](../devel/mji) mechanism to do that. However, most standard library classes have pure Java implementations, and we take these straight from the host-JVM installation. This means, standard libraries end up in JPF's classpath *and* the host JVM CLASSPATH: and their code is executed by JPF *and* the host VM, but the corresponding instances are different (for instance, a `java.lang.String` object has very different representations in the host VM and JPF).
+
+
+JPF keeps associated class paths separate. There are three different path settings in the JPF configuration:
+
+ 1. `native_classpath` - this includes everything that has to be visible to the host VM, which includes mostly the classes of JPF itself, listeners, native peers and bytecode instruction sets. The `native_classpath` is dynamically constructed from your site.properties and jpf.properties [configuration files](../user/config) during JPF startup, and processed by the JPF classloader
+ 2. `classpath` - this includes the classes that are executed by JPF, which include your SUT, and all libraries that are used by it (*-classes.jar of installed JPF components). Note that some of the library classes (like `java.lang.Thread`) are replaced by JPF specific versions. Again, this is constructed by JPF during startup from all your configured jpf.properties files, but processed by JPFs internal classfile lookup mechanism.
+ 3. `sourcepath`  - depending on the reports that have to be generated by JPF, it might have to find SUT and library sources for the executed classfiles (bytecodes). This is how you tell JPF where these sources reside.
+
+You can set these path lists explicitly from your application property file or the command line, but it is better to let JPF collect them when processing the jpf.properties files of your installed JPF components, which all include  lines like
+
+~~~~~~~~ {/bash}
+<jpf-module>.native_classpath = ..
+<jpf-module>.classpath = ..
+<jpf-module>.sourcepath = ..
+~~~~~~~~
+
+A word of caution: if - during JPF startup - you encounter a message like
+
+~~~~~~~~ {.bash}
+...
+[SEVERE} error during VM runtime initialization: wrong model classes (check 'classpath')
+~~~~~~~~
+
+it means the `classpath` setting is wrong and JPF loads some of the standard library classes that have to be replaced with its own versions  - they need to be *modeled* (like `java.lang.Thread`, `java.lang.Class` and others - all part of jpf-classes.jar within the jpf-core module).
+
+This can also happen later-on with less essential library classes that are not part of jpf-core, but should be loaded from one of the installed extensions. In this case, this usually shows up as an `UnsatisfiedLinkError`
diff --git a/doc/user/config.md b/doc/user/config.md
new file mode 100644 (file)
index 0000000..a57d07b
--- /dev/null
@@ -0,0 +1,153 @@
+# Configuring JPF #
+
+JPF configuration can be intimidating. It is worth to think about why we need such a heavy mechanism before we dive into its details. Little in JPF is hardwired. Since JPF is such an open system that can be parameterized and extended in a variety of ways, there is a strong need for a general, uniform configuration mechanism. The challenge for this mechanism is that many of the parts which are subject to parameterization are configured themselves (i.e. options for optional JPF components like listeners). This effectively prohibits the use of a configuration object that contains concrete fields to hold configuration data, since this class would be a central "design bottleneck" for a potentially open number of JPF components like Searches, Instruction sets and Listeners.
+
+The goal is to have a configuration object that
+
+  * is based on string values
+  * can be extended at will
+  * is passed down in a hierarchical initialization process so that every component extracts only its own parameters
+
+We achieve this by means of a central dictionary object (`gov.nasa.jpf.Config`) which is initialized through a hierarchical set of Java property files that target three different initialization layers:
+
+  1. site: optionally installed JPF components
+  2. project: settings for each installed JPF module and (optionally) systems under test
+  3. application: the class and program properties JPF should check (this is part of your system under test)
+
+Initialization happens in a prioritized order, which means you can override anything from later configuration stages, all the way up to command line parameters. Actually, this can be even overridden by using the `Verify` API from system under test code, but this is a developer topic. Here is the blueprint, which we will examine in order of execution:
+
+![Figure: Configuring JPF](../graphics/properties.svg){align=center width=800}
+
+
+## Property Types ##
+
+Property specifications are processed in a hierarchical order: site properties, project properties, application properties and command line properties. Later stages can override previous stages. Each property is a `<key>=<value>` pair, but we do support some special notations (see below) for key/value expansion, value extension, and pseudo properties that act as directives.
+
+### Site Properties ###
+The site.properties file is machine specific and not part of any JPF project, which means you have to create a [ site.properties](wiki:install/site-properties) file as part of the install process. A sample site.properties might look like:
+
+~~~~~~~~
+jpf-core = ${user.home}/projects/jpf/jpf-core
+jpf-shell = ${user.home}/projects/jpf/jpf-shell
+jpf-awt = ${user.home}/projects/jpf/jpf-awt
+...
+extensions=${jpf-core},${jpf-shell}
+~~~~~~~~
+
+Each module is listed as a `<name>=<directory>` pair, and optionally added to the comma separated list of `extensions`. The order in which modules are added to `extensions` does matter, since it will determine the order in which each of these components is initialized, which basically maps to an ordered list of classpath entries (both for the host VM and JPF itself - paths are kept separate).
+
+Note that we do **not** require all modules being added to `extensions`, **but** jpf-core needs to be in there. Dependencies on modules not listed in `extensions` can be specified later-on with the `@using` directive. It is a good idea to keep the `extensions` list small to avoid conflicts, and to improve class load times (shorter classpaths). 
+
+Note also that the `extensions` entries are of the form `${<key>}`, which tells JPF to replace these expressions with the value that is associated to <key>. 
+
+Site properties have to be stored in the directory from where you start JPF, one of its parent directories, or a global `${user.home}/.jpf/site.properties`. We recommend a to keep it in a common root directory that contains all your JPF projects and modules.
+
+
+### Project Properties ###
+Each JPF module contains a jpf.properties file in its root directory, no matter if this is the jpf-core or an extension. System under test projects can contain a jpf.properties file to factor out common settings (such as class paths) for all JPF applications within this project.
+
+A project property file defines the JPF specific paths that need to be set for the module or system under test to work properly
+
+ 1. `<module-name>.`**`native_classpath`**: the host VM classpath (i.e. the classes that constitute JPF itself)
+ 1. `<module-name>.`**`classpath`**: the classpath JPF uses to execute the system under test
+ 1. `<module-name>.`**`test_classpath`**: host VM and JPF classpath for regression tests
+ 1. `<module-name>.`**`sourcepath`**: the path entries JPF uses to locate sources in case it needs to create program traces
+
+Additionally, `jpf.properties` should contain default values for all module or project specific settings (e.g. report options).
+
+An example project properties file might look like:
+
+~~~~~~~~ {.bash}
+jpf-aprop = ${config_path}
+
+#--- path specifications
+jpf-aprop.native_classpath = build/jpf-aprop.jar;lib/antlr-runtime-3.1.3.jar
+jpf-aprop.classpath = build/examples
+jpf-aprop.test_classpath = build/tests
+jpf-aprop.sourcepath = src/examples
+
+#--- other project specific settings
+listener.autoload=${listener.autoload},javax.annotation.Nonnull,...
+listener.javax.annotation.Nonnull=gov.nasa.jpf.aprop.listener.NonnullChecker
+...
+~~~~~~~~
+
+
+
+The first entry (`<module-name>=${config_path}`) in a jpf.properties should always define the module name. JPF automatically expands `${config_path}` with the pathname of the directory in which this jpf.properties file resides.
+
+jpf.properties are executed in order of definition within site.properties, with one caveat: if you start JPF from within a directory  that contains a jpf.properties file, this one will always take precedence, i.e. will be loaded last (overriding previous settings except of the command line). This way, we ensure that JPF developers can enforce priority of the component they are working on.
+
+Both site.properties and jpf.properties can define or override any key/value pairs they want, but keep in mind that you might end up with different system behavior depending on where you start JPF - avoid configuration force fights by keeping jpf.properties settings disjunct wherever you can.
+
+Please note that site and project properties have to be consistent, i.e. the module names (e.g. jpf-awt) in site.properties and jpf.properties need to be the same. This is also true for the build.xml Ant project names.
+
+It is perfectly fine to have a jpf.properties in a SUT that only uses JPF for verification. You need at least to set up the `classpath` so that JPF knows where to find the SUT classes.
+
+
+### Application Properties ###
+In order to run JPF, you need to tell it what main class it should start to execute. This is the minimal purpose of the *.jpf application properties files, which are part of your systems under test (SUT). Besides the `target` setting that defines the main class of your SUT, you can also define a list of `target_args` and any number of JPF properties that define how you want your application to be checked (listeners, search policy, bytecode factories etc.). A typical example looks like
+
+~~~~~~~~ {.bash}
+#--- dependencies on other JPF modules
+@using = jpf-awt
+@using = jpf-shell
+
+#--- what JPF should run
+target = RobotManager
+
+#--- other stuff that defines how to run JPF
+listener+=,.listener.OverlappingMethodAnalyzer
+
+shell=.shell.basicshell.BasicShell
+awt.script=${config_path}/RobotManager-thread.es
+cg.enumerate_random=true
+...
+~~~~~~~~
+
+The `@using = <module-name>` directive tells JPF to load the `jpf.properties` of the specified projects (defined in `site.properties`). This is the way to ensure proper path initialization of projects that are not listed in `extensions`. 
+
+### Command Line Properties ###
+Last not least, you can override or extend any of the previous settings by providing "**+**<key>=<value>" pairs as command line options. This is convenient for experiments if you have to determine the right settings values empirically
+
+
+## Special Property Syntax ##
+JPF supports a number of special notations that are valid Java properties syntax, but are only processed by JPF (and - to a certain extend - by Ant):
+
+ * **`key=...${x}..`** - replaces `"${x}"` with whatever is currently stored under the key "`x`". This also works recursively, as in "`classpath = mypath;${classpath}`". Note that while Apache Ant does also support value expansion, but not recursively. In addition, JPF also supports expansion in the key part (i.e. left of the "`=`")
+
+ * **`key+=val`** - appends val to whatever is currently stored under "`key`". Note that there can be no blank between key and "`+=`", which would not be parsed by Java. This expansion only works in JPF 
+
+ * **`+key=val`** - in a properties file adds "`val`" in front of what is currently stored under "`key`". Note that if you want to use this from the command line, you have to use two "`++`", since command line options start with "+"
+
+ * **`${config_path}`** - is automatically set to the directory pathname of the currently parsed property file. This can be useful to specify relative pathnames (e.g. input scripts for the jpf-awt extension)
+
+ * **`${config}`** - is set to the file pathname of the currently parsed file
+
+ * **`@requires=<key>`** - can be used to short-circuit loading of a properties file. This is a simple mechanism to control loading of jpf.properties file sections that are incompatible with other modules, and compares to `#ifdef` preprocessor directives. The respective keys are usually set from application properties (*.jpf) files. 
+Note this does not throw an exception if the required key is not defined, it just bails out of loading the properties file that contains the @requires directive
+
+ * **`@include=<property-file>`** - recursively loads the properties file with the specified pathname. You can use the automatically defined `${config_path}` property to specify path names that are relative to where the file containing the `@include` directive is, or use `${<module-name>}` for paths that are relative to the specified module.
+
+ * **`@using=<module-name>`** - is similar to `@include`, but loads the jpf.properties of the specified module name (which has to be defined in site.properties). Note that there is no `${..}` around the module name - otherwise it would be replaced. The `@using` directive is the preferred way to specify dependencies on modules that are not in the `extensions` list (i.e. automatically initialized).
+
+ * **`@include_if = ?<key>?<properties-file>`** - is a conditional `@include` that only loads the properties file if the specified `<key>` is defined
+
+ * **`@include_unless = ?<key>?<properties-file>`** - likewise loads the file only if `<key>` is not defined. Both of these directives are used rarely.   
+
+ * Omitting the "`=..`" part in command line settings defaults to a "`true`" value for the corresponding key
+
+ * Setting an empty value (`key=`) removes the key from the config dictionary
+
+## Debugging ##
+Depending on the number of installed and loaded projects, you can easily end up with hundreds of settings. There are two command line options you can use if you assume there is a configuration error:
+
+ * **`-show`** - prints all Config entries after the initialization is complete
+
+ * **`-log`** - lists the order in which properties files got loaded by JPF
+
+## Details on various options ##
+
+* [Randomization](config/random)
+
+* Error Reporting
diff --git a/doc/user/config/random.md b/doc/user/config/random.md
new file mode 100644 (file)
index 0000000..4fae737
--- /dev/null
@@ -0,0 +1,15 @@
+## Randomization Options in JPF ##
+
+The randomization options in JPF allow the user to experiment in randomizing the order of choices explored. 
+
+`cg.randomize_choices` can have three possible values: random, path, def.
+
+ * `random`: It explores random choices during program execution with **varying results among different trials**. The default seed used to generate different results is the **system time in milliseconds**
+
+ * `path`: It explores random choices during program execution with **reproducible results among different trials**. The default seed used to generate reproducible results is **42**. The value of the seed can be changed by setting the seed config option.
+
+ * `def`: No randomization, choices are explored using the default search order imposed by the model checker. 
+
+`cg.seed (_INT_)`: The user can specify a particular seed for the random number generator in order to obtain reproducible results in the presence of randomization. Note that this is effective only when the `path` option of `randomize_choices` is selected. 
+
+
diff --git a/doc/user/index.md b/doc/user/index.md
new file mode 100644 (file)
index 0000000..6626007
--- /dev/null
@@ -0,0 +1,19 @@
+# How to Use JPF #
+
+This section is where the real fun starts. Here you learn about
+
+  - [Different applications of JPF](application_types)
+  - [JPF's runtime components](components)
+  - [Starting JPF](run)
+  - [Configuring JPF](config)
+  - [Understanding JPF output](output)
+  - [Using JPF's Verify API in the system under test](api)
+
+All this assumes you are more interested in running JPF than in developing with/for it, so we will leave most of the JPF internals for the [developer section](../devel/index) of this wiki. 
+
+We do have to bother you with some basic concepts though. Keep in mind that JPF is usually not a black-box tool (such as a compiler). Most probably you have to configure it according to your needs because
+
+  * you have specific verification goals (properties)
+  * your application has a huge state space that is challenging for a model checker
+
+On the other hand, JPF is also not a "works-or-fails" tool. Depending on how much time you want to invest, you can adapt it to almost all application types and verification goals. And since JPF is open sourced, chances are somebody has already done that
diff --git a/doc/user/output.md b/doc/user/output.md
new file mode 100644 (file)
index 0000000..349242d
--- /dev/null
@@ -0,0 +1,98 @@
+# Understanding JPF Output #
+
+There are three different ways a JPF run can produce output, each of them with a different purpose, but all controlled by the [general JPF configuration mechanism](config):
+
+  1. System under test output - what is the SUT doing?\
+
+  2. JPF logging - what is JPF doing?\
+
+  3. JPF reporting system - what is the result of the JPF run?\
+
+
+
+## System Under Test Output ##
+
+This is the most simple form of output, which usually just consists of `System.out.println(..)` calls embedded in the application code. There is only one caveat - since this is executed by JPF as part of the application, the same print statement might be executed several times:
+
+~~~~~~~~ {.java}
+public class MyApplication ..{
+    ...
+    boolean cond = Verify.getBoolean();
+    System.out.println("and the cond is: " + cond);
+    ...
+}
+~~~~~~~~
+
+will produce
+
+~~~~~~~~
+...
+and the cond is: true
+...
+and the cond is: false
+...
+~~~~~~~~
+
+The second execution of the print statement is of course preceded by a backtrack operation of JPF (the `Verify.getBoolean()` statement has two choices `{true,false}`), but the backtracking might not be visible, e.g. when running without the `ExecTracker` or `ChoiceTracker` listeners.
+
+Since it can be sometimes confusing to see the same output twice without knowing if there is an iteration in the application, or JPF did backtrack between executions, there are two configuration options to control the output behavior:
+
+`vm.tree_output = {true|false}` - means output is shown on the console each time a print statement is executed. This corresponds to the above example, and is the default behavior.
+
+`vm.path_output = {true|false}` - will not immediately print the output on the console, but store in the path for subsequent processing once JPF terminates (if the *output* topic is specified - see below). This should produce the same output as running the test application on a normal JVM.
+
+## Logging ##
+
+This is a more interesting form of JPF output, primarily intended to show what JPF does internally. For this purpose, it has to support various levels of details, ranging from severe errors to fine grained logging of JPF operations.
+
+JPF's logging mechanism does not reinvent the wheel, it piggybacks on the standard java.util.logging infrastructure. While this means it would be possible to use customized LogHandlers and Formatters (e.g. to log in XML format), there are specialized JPF incarnations of these classes, mainly to enable logging configuration via the standard JPF configuration mechanism rather than system properties.
+
+Using the JPF Logging involves two aspects: (1) controlling log output destination, and (2) setting log levels. Both are done with JPF property files.
+
+To set the default log level, use the `log.level` property (the supported levels being *severe,warning,info,fine,finer,finest*)
+
+If you want to log to a different console that possibly even runs on a remote machine, use the gov.nasa.jpf.tools.LogConsole on the machine that should display the log messages:
+
+~~~~~~~~ {.bash}
+$ java gov.nasa.jpf.tools.LogConsole <port>
+~~~~~~~~
+
+Then start JPF on the test machine, specifying where the log output should go:
+
+~~~~~~~~ {.bash}
+$ jpf +log.output=<host>:<port> ... MyTestApp
+~~~~~~~~
+
+The default host is "localhost", default port is 20000. If these are suitable settings, you can start the `LogConsole` without parameters, and just specify `+log.output=socket` when running JPF.
+
+
+If you develop your own JPF classes, please also check the [JPF logging API](../devel/logging) page.
+
+## Reports ##
+
+The JPF reporting system is used to show the outcome of a JPF run, to report property violations, print traces, show statistics and much more. This is in a way the most important part of the JPF user interface, and might involve various different output formats (text, XML, API calls) and targets (console, IDE). Depending on application and project, users might also need control over what items are displayed in which order. It is also obvious this needs to be an extensible mechanism, to adapt to new tools and properties. The JPF report system provides all this, again controlled by JPF's general configuration mechanism.
+
+The basic concept is that reporting depends on a predefined set of output phases, each of them with a configured, ordered list of topics. The output phases supported by the current system are:
+
+  * start - processed when JPF starts
+  * transition - processed after each transition
+  * property_violation - processed when JPF finds a property violation
+  * finished - processed when JPF terminates
+
+There is no standard transition topic yet (but it could be implemented in `PublisherExtensions`). The standard `property_violation` topics include:
+
+  * error - shows the type and details of the property violation found
+  * trace - shows the program trace leading to this property violation
+  * snapshot - lists each threads status at the time of the violation
+  * output - shows the program output for the trace (see above)
+  * statistics - shows property statistics information
+
+Last not least, the finished list of topics that usually summarizes the JPF run:
+
+  * result - reports if property violations were found, and shows a short list of them
+  * statistics - shows overall statistics information
+
+The system consists of three major components: (1) the Reporter, (2) any number of format specific Publisher objects, and (3) any number of tool-, property- and Publisher-specific PublisherExtension objects. Here is the blueprint:
+
+
+Again, there is separate [report system API](../devel/report) documentation if you are interested in JPF development.
diff --git a/doc/user/run.md b/doc/user/run.md
new file mode 100644 (file)
index 0000000..ce3b515
--- /dev/null
@@ -0,0 +1,258 @@
+# Running JPF #
+There are five general ways to run JPF, depending on your execution environment (command prompt or IDE) and desired level of configuration support. This page has to cover quite some ground, so bear with us
+
+ 1. [from a command prompt (operating system shell)](#command-line)
+ 2. [from an IDE (NetBeans, Eclipse) without using JPF plugins](#running-jpf-from-within-ide-without-plugins)
+ 3. [from an IDE with JPF plugins installed](#running-jpf-from-within-ide-with-plugins)
+ 4. [from within a JUnit test class](#launching-jpf-from-junit-tests)
+ 5. [single tests from command line](#explicitly-running-tests-from-the-command-line)
+ 6. [explicitly from an arbitrary Java program](#explicitly-launching-jpf-from-a-java-program)
+
+## Command Line ##
+
+There are several ways to run JPF from the command line, using varying degrees of its runtime infrastructure. The most simple way is to use the provided `bin/jpf` script of the jpf-core distribution. Go to the directory where your system under test (SUT) classes reside, and do a
+
+~~~~~~~~ {.bash}
+> <jpf-core-dir>/bin/jpf +classpath=. <application-main-class>
+~~~~~~~~  
+
+or preferably
+
+~~~~~~~~ {.bash}
+> <jpf-core-dir>/bin/jpf <application-property-file>.jpf
+~~~~~~~~  
+
+(see target specification below). If you want to avoid platform specific scripts, you only have to slightly expand this to
+
+~~~~~~~~ {.bash}
+> java -jar <jpf-core-dir>/build/RunJPF.jar +classpath=. <application-main-class>
+~~~~~~~~
+
+This makes use of the small RunJPF.jar startup jar that is part of the jpf-core distribution, which only includes the classes that are required to start the JPF bootstrapping process (esp. the JPF classloader). These classes automatically process the various [JPF configuration files](config). If your SUT is not trivial, it is also recommended to add a "-Xmx1024m" host VM option, to avoid running out of memory.
+
+Last (and probably most rarely), you can directly start JPF and give it an explicit classpath. This amounts to something like
+
+~~~~~~~~ {.bash}
+> java -classpath <jpf-core-dir>/build/jpf.jar gov.nasa.jpf.JPF +classpath=. <application-main-class>
+~~~~~~~~
+
+Of course, this gets quickly more complicated if you use JPF extensions, which require to add to both the host VM and the JPF classpath, which is completely automated if you use the RunJPF.jar method. Explicitly setting paths is only for rare occasions if you develop JPF components yourself.
+
+There are three different argument groups that are processed by JPF:
+
+#### (1) JPF command line options ####
+
+These options should come first (after RunJPF.jar), and all start with a hyphen ("-").  The set of currently supported options is:
+
+ * -help : show usage information and exit
+ * -log  : print the configuration steps
+ * -show : print the configuration dictionary after configuration is complete
+
+The last two options are mostly used to debug if the JPF configuration does not work as expected. Usually you start with `-show`, and if you don't see the values you expect, continue with `-log` to find out how the values got set.
+
+
+#### (2) JPF properties ####
+
+This is the second group of options, which all start with a plus ("+") marker, and consist of "`+<key>=<value>`" pairs like
+
+~~~~~~~~ {.bash}
+.. +cg.enumerate_random=true
+~~~~~~~~
+
+All properties from the various JPF properties [configuration files](config) can be overridden from the command-line, which means there is no limit regarding number and values of options. If you want to extend an existing value, you can use any of the following notations
+
+ * `+<key>+=<value>` - which appends <value>
+ * `++<key>=<value>` - which prepends <value>
+ * `+<key>=..${<key>}..` - which gives explicit control over extension positions
+
+Normal JPF properties `${<key>}` expansion is supported.
+
+If the `=<value>` part is omitted, a default value of `true` is assumed. If you want to set a value to null (i.e. remove a key), just skip the `<value>` part, as in `+<key>=`
+
+#### (3) target specification ####
+
+There are two ways to specify what application JPF should analyze
+ * explicit classname and arguments
+
+~~~~~~~~ {.bash}
+> jpf ...  x.y.MyApplication arg1 arg2 ..
+~~~~~~~~
+
+ * application property file (*.jpf)
+
+~~~~~~~~ {.bash}
+> jpf ... MyApplication.jpf
+~~~~~~~~
+
+We recommend using the second way, since it enables you to store all required settings in a text file that can be kept together with the SUT sources, and also allows you to start JPF from within !NetBeans or Eclipse just by selecting the *.jpf file (this is mainly what the IDE plugins are for). Please note that application property files require a "`target`" entry, as in
+
+~~~~~~~~ {.bash}
+# JPF application property file to verify x.y.MyApplication
+target = x.y.MyApplication
+target.args = arg1,arg2
+# Note that target_args in JPF 6 changed to target.args in JPF 7.
+...
+~~~~~~~~
+
+## Running JPF from within IDE without plugins ##
+
+You can start JPF from within !NetBeans or Eclipse without having the IDE specific JPF plugins installed. In this case, JPF uses the standard IDE consoles to report verification results. For details, please refer to the following pages:
+
+ * [Running JPF from within NetBeans without plugin](run_nb)
+ * [Running JPF from Eclipse without plugin](run_eclipse)
+
+Note that this is **not** the recommended way to run JPF from within an IDE, unless you want to debug JPF or your classes.
+
+## Running JPF from within IDE with plugins ##
+
+You can simplify launching JPF from within !NetBeans or Eclipse by using the respective plugins that are available from this server. In this case, you just have to create/select an application property (*.jpf) file within your test project, and use the IDE context menu to start a graphical JPF user interface. These so called "JPF shells" are separate applications (that can be configured through normal JPF properties), i.e. appear in a separate window, but can still communicate with the IDE, e.g. to position editor windows. You can find more details on
+
+ * [Running JPF from within NetBeans with netbeans-jpf plugin](run_nb_plugin)
+ * [Running JPF from Eclipse with eclipse-jpf plugin](run_eclipse_plugin)
+
+This is becoming the primary method of running JPF. The benefits are twofold: (1) this is executed outside of the IDE process, i.e. it doesn't crash the IDE if JPF runs out of memory, and (2) it makes use of all your standard JPF configuration (site.properties and jpf.properties), in the same way like running JPF from a command-line. 
+
+## Launching JPF from JUnit tests ##
+
+JPF comes with [JUnit](http://www.junit.org) based testing infrastructure that is used for its own regression test suite. This mechanism can also be used to create your own test drivers that are executed by JUnit, e.g. through an [Ant](http://ant.apache.org) build script. The source structure of your tests is quite simple
+
+~~~~~~~~ {.java}
+import gov.nasa.jpf.util.test.JPFTestSuite;
+import org.junit.Test;
+
+public class MyTest extends TestJPF {
+
+  @Test
+  public void testSomeFunction() {
+    if (verifyNoPropertyViolation(jpfOptions)) {  // specifies the test goal, "jpfOptions" are optional 
+       someFuntction(); ..                        // this section is verified by JPF
+    } 
+  }
+
+  //.. more @Test methods
+~~~~~~~~
+
+From a JUnit perspective, this is a completely normal test class. You can therefore execute such a test with the standard `<junit>` [Ant](http://ant.apache.org) task, like
+
+~~~~~~~~ {.xml}
+    <property file="${user.home}/.jpf/site.properties"/>
+    <property file="${jpf-core}/jpf.properties"/>
+    ...
+    <junit printsummary="on" showoutput="off" haltonfailure="yes"
+           fork="yes" forkmode="perTest" maxmemory="1024m">
+      ...
+      <classpath>
+        ...
+        <pathelement location="${jpf-core}/build/jpf.jar"/>
+      </classpath>
+
+      <batchtest todir="build/tests">
+        <fileset dir="build/tests">
+          ...
+          <include name="**/*Test.class"/>
+        </fileset>
+      </batchtest>
+    </junit>
+    ...
+~~~~~~~~
+
+Only jpf.jar needs to be in the host VM classpath when compiling and running the test, since `gov.nasa.jpf.util.test.TestJPF` will use the normal JPF configuration (site.properties and configured jpf.properties) to set up the required `native_classpath`, `classpath`, 'test_classpath` and `sourcepath` settings at runtime. Please refer to the [JPF configuration](config) page for details. 
+
+If you don't have control over the build.xml because of the IDE specific project type (e.g. if your SUT is configured as a NetBeans "Class Library Project"), you have to add jpf.jar as an external jar to your IDE project configuration.
+
+In addition to adding jpf.jar to your build.xml or your IDE project configuration, you might want to add a jpf.properties file to the root directory of your project, to set up things like where JPF finds classes and sources it should analyze (i.e. settings that should be common for all your tests). A generic example could be 
+
+~~~~~~~~ {.bash}
+  # example of JPF project properties file to set project specific paths
+
+  # no native classpath required if this is not a JPF project itself
+  myproject.native_classpath=...
+
+  # where does JPF find the classfiles to execute
+  myproject.classpath=build/classes
+
+  # where do test classes reside
+  myproject.test_classpath=build/test/classes
+
+  # other project common JPF settings like autoloaders etc.
+  listener.autoload+=,javax.annotation.Nonnull
+  listener.javax.annotation.Nonnull=.aprop.listener.NonnullChecker
+  ...
+~~~~~~~~
+
+You can find project examples here
+
+ * [standard NetBeans project](../projects/standardnbproject) ("Java Class Library" or "Java Application")
+ * Freeform NetBeans project (with user supplied build.xml)
+ * standard Eclipse project (with user supplied build.xml)
+
+Please refer to the [Verify API](api) and the [JPF tests](../devel/jpf_tests) pages for details about JPF APIs (like `verifyNoPropertyViolation(..)` or `Verify.getInt(min,max)`) you can use within your test classes.
+
+Since JPF projects use the same infrastructure for their regression tests, you can find a wealth of examples under the `src/tests` directories of your installed JPF projects. 
+
+## Explicitly Running Tests from the command line ##
+
+You can also run your `TestJPF` derived test drivers by using the `bin/test` script (which in turn just a short for "`java -jar tools/RunTest.jar`", i.e. is platform independent):
+
+~~~~~~~~ {.bash}
+bin/test <test-class> [<test-method>]
+~~~~~~~~ 
+
+Note that each `verify..(jpfArgs)` uses its own `Config` instance in this case. If you want to specify temporary JPF options from the command-line when running `RunTest`, prefix them with `test` like in the following example
+
+~~~~~~~~ {.bash}
+bin/test +test.listener=.listener.ExecTracker gov.nasa.jpf.test.mc.basic.AttrsTest
+~~~~~~~~
+
+## Explicitly Launching JPF from a Java Program ##
+Since JPF is a pure Java application, you can also run it from your own application. The corresponding pattern looks like this:
+
+~~~~~~~~ {.java}
+public class MyJPFLauncher {
+  ...
+  public static void main(String[] args){
+    ..
+    try {
+
+      // this initializes the JPF configuration from default.properties, site.properties
+      // configured extensions (jpf.properties), current directory (jpf.properies) and
+      // command line args ("+<key>=<value>" options and *.jpf)
+      Config conf = JPF.createConfig(args);
+
+      // ... modify config according to your needs
+      conf.setProperty("my.property", "whatever");
+
+      // ... explicitly create listeners (could be reused over multiple JPF runs)
+      MyListener myListener = ... 
+
+      JPF jpf = new JPF(conf);
+
+      // ... set your listeners
+      jpf.addListener(myListener);
+
+      jpf.run();
+      if (jpf.foundErrors()){
+        // ... process property violations discovered by JPF
+      }
+    } catch (JPFConfigException cx){
+      // ... handle configuration exception
+      // ...  can happen before running JPF and indicates inconsistent configuration data
+    } catch (JPFException jx){
+      // ... handle exception while executing JPF, can be further differentiated into
+      // ...  JPFListenerException - occurred from within configured listener
+      // ...  JPFNativePeerException - occurred from within MJI method/native peer
+      // ...  all others indicate JPF internal errors
+    }
+  ...
+~~~~~~~~ 
+
+Please refer to the [Embedding JPF](../devel/embedded) developers documentation for details. If you start JPF through your own launcher application, you have to take care of setting up the required `CLASSPATH` entries so that it finds your (and JPFs) classes, or you can use the generic `gov.nasa.jpf.Main` to load and start your launcher class, which makes use of all the path settings you have in your [site.properties](../install/site-properties) and the directories holding project properties (jpf.properties) referenced therein (details on [how to configure JPF](../user/config). This brings us back to the command line at the top of this page, only that you specify which class should be loaded through `Main`:
+
+~~~~~~~~ {.bash}
+> java -jar .../RunJPF.jar -a MyJPFLauncher ...
+~~~~~~~~ 
+
+(note that `gov.nasa.jpf.Main` is the `Main-Class` entry of the executable RunJPF.jar, which also holds the `JPFClassLoader`). 
+
+Just for the sake of completeness, there is another way to start JPF explicitly through a `gov.nasa.jpf.JPFShell` implementation, which is using the normal `JPF.main()` to load your shell, which in turn instantiates and runs a `JPF` object. This is specified in your application property (*.jpf) file with the `shell=<your-shell-class>` option. Use this if your way to start JPF is optional, i.e. JPF could also be run normally with your *.jpf.
\ No newline at end of file
diff --git a/doc/user/run_eclipse.md b/doc/user/run_eclipse.md
new file mode 100644 (file)
index 0000000..72a8e3a
--- /dev/null
@@ -0,0 +1,20 @@
+## Running JPF within eclipse ##
+
+To run JPF in eclipse after building 
+
+* JPF click "Run"-->"Run" or click "Play"-->"Run As Dialog".
+
+* Select Java Application and click "New".
+
+* Click on the "Main" tab. Ensure the project selected is jpf-core and the in the Main Class pick `gov.nasa.jpf.JPF`.
+
+* Click on the "Arguments" tab. Specify the `<application-main-class>` in the Program Arguments. Any additional configuration properties can be specified. For example, 
+
+~~~~~~~~ {.bash}
++cg.randomize_choices=true
+oldclassic
+~~~~~~~~
+
+* Click on "Run" to verify the application in JPF. The output of the program and the results will be displayed on the eclipse console window. 
+
+Alternatively you pick the run-jpf-core launch configuration and add the `<application-main-class>` of the system under test. The eclipse IDE detects the various launch configurations. When you click on the Run button they appear on the left column, pick "run-jpf-core". It is, however, recommended you run the application with the eclipse plugin for better performance and ease of usability.  
\ No newline at end of file
diff --git a/doc/user/run_eclipse_plugin.md b/doc/user/run_eclipse_plugin.md
new file mode 100644 (file)
index 0000000..a2f40e8
--- /dev/null
@@ -0,0 +1,12 @@
+# Running JPF from within Eclipse using eclipse-jpf #
+
+eclipse-jpf can be easily configured to run JPF from within Eclipse
+
+ 1. Install eclipse-jpf (see [Installing Eclipse JPF plugin](../install/eclipse-plugin))
+ 2. Make sure that the correct site.properties file is being used for JPF (The default is usually correct)
+   To see which site.properties file is being used:
+   3. From the Eclipse top menu go to **Window**->**Preferences**
+   4. Select **JPF Preferences**
+   5. Make sure that "Path to site.properties" is defined properly.
+ 6. From either the **Package Explorer** view (On the left of the main Eclipse screen by default) right-click on the *.jpf with the JPF configuration wanted and select **Verify...**
+ 7. Make sure that the **Console view** (Usually at the bottom of the main Eclipse screen) is opened to view the results from JPF
\ No newline at end of file
diff --git a/doc/user/run_nb.md b/doc/user/run_nb.md
new file mode 100644 (file)
index 0000000..b6e60d9
--- /dev/null
@@ -0,0 +1,92 @@
+# Running JPF from within NetBeans without JPF plugin #
+
+Please Note that the following description assumes NetBeans "freeform projects". In general, it is much better to use the JPF plugins, which not only make the configuration steps described below obsolete, but also work with other NetBeans project types.
+
+Since the NetBeans build process is Ant based, and Ant can read a subset of JPF configuration files, integration is fairly straight forward. Every JPF project comes with its own build.xml, and a `.../nbproject` directory that holds a NetBeans specific project.xml and ide-file-target.xml file, which can serve as templates for your own projects.
+
+Once everything is configured correctly, you can run JPF by selecting application property (*.jpf) files in the "project view", and do a "Run"->"Run File" from the NetBeans main menu (without the plugin, there will be no "run" item in the project view context menu, but hotkeys should still work).
+
+To make this work, you have to configure two NetBeans specific files:
+
+### nbproject/project.xml ###
+
+If you want to use JPF specific classes (like annotations), you have to make sure the NetBeans source checker will see them. You can achieve this by re-using the JPF property files to set the required classpaths, like
+
+~~~~~~~~ {.xml}
+...
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
+            <name>...</name>
+            <properties>
+                <property-file>${user.home}/.jpf/site.properties</property-file>
+                <property-file>${jpf-core}/jpf.properties</property-file>
+                ...                
+            </properties>
+         ...
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/2">
+            <compilation-unit>
+                <package-root>...</package-root>
+                <classpath mode="compile">...;${jpf-core.native_classpath}</classpath>
+            </compilation-unit>
+          ...
+~~~~~~~~
+
+This works for two reasons:
+
+ * Ant supports `${key}` expansion from properties (as long as they are not self-recursive)
+ * Ant properties are single-assignment, i.e. subsequent definitions of the same property name will be ignored
+
+The next step is required no matter if you use JPF types or not - you have to define file actions, which will link to the ide-file-targets.xml
+
+~~~~~~~~ {.xml}
+         ...
+            <ide-actions>
+                ...
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-jpf</target>
+                    <context>
+                        <property>jpf.config</property>
+                        <folder>...</folder>
+                        <pattern>\.jpf$</pattern>
+                        <format>absolute-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+           ...
+~~~~~~~~
+
+It seems this has to be defined for each source folder, which can require a number of redundant XML elements
+
+### nbproject/ide-file-targets.xml ###
+
+Within this file, you have to provide the scripts to run the actions defined in the project.xml. This can also make use of the JPF configuration files:
+
+~~~~~~~~ {.xml}
+<project basedir=".." name="....">
+
+    <property file="${user.home}/.jpf/site.properties"/>
+    <property file="${jpf-core}/jpf.properties"/>
+
+    <path id="base.path">
+        ...
+        <pathelement path="${jpf-core.native_classpath}"/>
+    </path>
+   ...
+    <target name="run-selected-jpf">
+        <fail unless="jpf.config">Must set property 'jpf.config'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <java classname="gov.nasa.jpf.JPF" failonerror="true" fork="true">
+            <arg value="-c"/>
+            <arg value="${jpf.config}"/>
+            <classpath>
+                ...
+                <path refid="base.path"/>
+            </classpath>
+        </java>
+    </target>
+   ...
+~~~~~~~~
+
diff --git a/doc/user/run_nb_plugin.md b/doc/user/run_nb_plugin.md
new file mode 100644 (file)
index 0000000..134634e
--- /dev/null
@@ -0,0 +1,12 @@
+# Running JPF from within NetBeans with netbeans-jpf plugin #
+netbeans-jpf can be easily configured to run JPF at the click of a mouse.
+
+ 1. Install netbeans-jpf (see: [Installing the NetBeans JPF plugin](../install/netbeans-plugin))
+ 2. Make sure the correct `site.properties` file is being used for JPF (The default is usually correct)\
+     To see which `site.properties` file is being used:
+   3. From the NetBeans top menu go to "Tools"->"Options" (Alt+T followed by Alt+O)
+   4. Select "Miscellaneous" from the top of the Options Window
+   5. Select the "Java Pathfinder" tab
+   6. Make sure that "Path to `site.properties`" is defined properly, uncheck "Use default `site.properties` location" to change the path
+ 7. From either the "Projects" or "Files" view (on the left of the main NetBeans screen by default,) select the JPF properties file (i.e., a file with a `.jpf` extension) you would like to run. Right click on this file and select the "Verify..." menu item to run JPF.
+ 8. To view the results, make sure that the "Output" View is open (On the bottom of the main NetBeans screen by default)
\ No newline at end of file
diff --git a/eclipse/AntBuilder.launch b/eclipse/AntBuilder.launch
new file mode 100644 (file)
index 0000000..f66e685
--- /dev/null
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>\r
+<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">\r
+<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_CLEAN_TARGETS" value="clean,"/>\r
+<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>\r
+<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="true"/>\r
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"/>\r
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"/>\r
+<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>\r
+<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>\r
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>\r
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="org.eclipse.ant.internal.ui.antsupport.InternalAntRunner"/>\r
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jpf-core"/>\r
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="${build_project}/build.xml"/>\r
+<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,auto,clean"/>\r
+<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>\r
+<stringAttribute key="process_factory_id" value="org.eclipse.ant.ui.remoteAntProcessFactory"/>\r
+</launchConfiguration>\r
diff --git a/eclipse/run-JPF.launch b/eclipse/run-JPF.launch
new file mode 100644 (file)
index 0000000..e599ce9
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/jpf-core/src/main/gov/nasa/jpf/tool/RunJPF.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;javaProject name=&amp;quot;jpf-core&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.javaProject&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;packageFragmentRoot handle=&amp;quot;=jpf-core/lib\/junit-4.10.jar&amp;quot;/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.jdt.launching.sourceContainer.packageFragmentRoot&quot;/&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;jpf-core&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;jpf-core&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${resource_loc}/build/main&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${resource_loc}/build/peers&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${resource_loc}/lib/*&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${resource_loc}/build/tests&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${resource_loc}/build/examples&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="gov.nasa.jpf.tool.RunJPF"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="&quot;${resource_loc}&quot;"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jpf-core"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx1024m -ea"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc}"/>
+</launchConfiguration>
diff --git a/eclipse/test-JPF.launch b/eclipse/test-JPF.launch
new file mode 100644 (file)
index 0000000..e7e33e9
--- /dev/null
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/jpf-core/src/main/gov/nasa/jpf/tool/RunTest.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
+<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;sourceLookupDirector&gt;&#10;&lt;sourceContainers duplicates=&quot;true&quot;&gt;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#10;&amp;lt;default/&amp;gt;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#10;&lt;/sourceContainers&gt;&#10;&lt;/sourceLookupDirector&gt;&#10;"/>
+<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
+<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
+</listAttribute>
+<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER&quot; javaProject=&quot;jpf-core&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;jpf-core&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${project_loc}/build/main&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${project_loc}/peers&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${project_loc}/lib/*&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${project_loc}/build/tests&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.variableClasspathEntry&quot;&gt;&#10;&lt;memento path=&quot;3&quot; variableString=&quot;${project_loc}/build/examples&quot;/&gt;&#10;&lt;/runtimeClasspathEntry&gt;&#10;"/>
+</listAttribute>
+<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="gov.nasa.jpf.tool.RunTest"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="${java_type_name} ${string_prompt}"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jpf-core"/>
+<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -ea"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc}"/>
+</launchConfiguration>
diff --git a/eclipse/update-JPF-siteproperties.launch b/eclipse/update-JPF-siteproperties.launch
new file mode 100644 (file)
index 0000000..65d3b94
--- /dev/null
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication">
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
+<listEntry value="/jpf-core/src/main/gov/nasa/jpf/tool/RunJPF.java"/>
+</listAttribute>
+<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
+<listEntry value="1"/>
+</listAttribute>
+<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="gov.nasa.jpf.tool.RunJPF"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-addproject"/>
+<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="jpf-core"/>
+<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${project_loc}"/>
+</launchConfiguration>
diff --git a/jpf.properties b/jpf.properties
new file mode 100644 (file)
index 0000000..0bdb036
--- /dev/null
@@ -0,0 +1,427 @@
+# JPF configuration for "jpf-core" component
+
+# path elements can either be relative to the property file location, or
+# use the JPF component name (e.g. "jpf-core") as a variable prefix
+# (e.g. "jpf-core.sourcepath=${jpf-core}/src/examples").
+# If a jpf.properties is to be used within NB, it has to use the variable prefix
+# (prepending the project root is only done during JPF init)
+
+# we use the ';' separator here because it is recognized by NetBeans (as opposed
+# to ',')
+
+# 'config_path' is set automatically by gov.nasa.jpf.Config during JPF init.
+# If used from within an Ant build system, 'jpf-core' has to be set explicitly
+# (ant does ${..} property expansion, but ignores property redefinition, so the
+# following line will be ignored)
+
+jpf-core = ${config_path}
+
+jpf-core.native_classpath=\
+  ${jpf-core}/build/jpf.jar;\
+  ${jpf-core}/build/jpf-annotations.jar
+
+jpf-core.classpath=\
+  ${jpf-core}/build/jpf-classes.jar;\
+  ${jpf-core}/build/examples
+
+jpf-core.sourcepath=\
+  ${jpf-core}/src/examples
+
+jpf-core.test_classpath=\
+  ${jpf-core}/build/tests
+
+jpf-core.peer_packages = gov.nasa.jpf.vm,<model>,<default>
+
+
+
+# the default jpf-core properties file with keys for which we need mandatory defaults
+
+########################### 0. global part ###############################
+
+# instances that are both search and VM listeners
+#listener = ..
+
+# do we want JPF exceptions to print their stack traces (that's only for
+# debugging)
+jpf.print_exception_stack = true
+
+
+# this is where we can specify additional classpath entries that are
+# not in the system property class.path (e.g. when running JPF from
+# an environment that uses it's own loaders, like Eclipse plugins etc.)
+#jpf.native_classpath = ..
+
+
+########################### 1. Search part ###############################
+search.class = gov.nasa.jpf.search.DFSearch
+
+
+# This flag indicates whether state matching will only be done when a state
+# is revisited at a lower depth. By default this is false. If it is set to
+# true and no error is found in a limited-depth search then it is guaranteed
+# not to have such error below that depth. Note that for traditional
+# depth limited search this does not hold
+search.match_depth = false
+
+# This flag indicates whether JPF should produce more than one error
+# or stop at the first one
+search.multiple_errors = false
+
+# the minimum free memory bounds. If we fall below that threshold, we
+# stop the search
+search.min_free = 1M
+
+# name of the file in which we store error paths. If not set, we don't store
+#search.error_path = error.xml
+
+# the standard properties we want to check for
+search.properties=\
+gov.nasa.jpf.vm.NotDeadlockedProperty,\
+gov.nasa.jpf.vm.NoUncaughtExceptionsProperty
+
+
+# various heuristic parameters
+
+# This number specifies the maximum number of states to keep on the queue
+# during a heuristic search. By default it is set to -1
+search.heuristic.queue_limit = -1
+
+# This flag indicates whether branches with counts less than branch-start
+# are to be ranked according to how many times they have been taken.
+# It is set to true by default. If it is set to false, they are all valued
+# the same
+search.heuristic.branch.count_early = true
+
+# This number determines at what point branches are heuristically valued as worse
+# than non-branching transitions. By default this value is 1.
+branch_start = 1
+
+
+# This number if greater than 0 is returned as the heuristic value for
+# non-branching transitions. By default it is set to -1 in which case the
+# value of branch-start is returned instead
+search.heuristic.branch.no_branch_return = -1
+
+# exclusive search listeners
+# search.listener =
+
+
+############################### 2. VM part ###############################
+
+
+# this is an ordered list of packages from which we try to locate native peers.
+# "<model>" means JPF tries the same package like the model class
+# "<default>" means no package at all
+# (this is going to be extended by jpf.properties files)
+#peer_packages = <model>,<default>
+
+
+vm.class = gov.nasa.jpf.vm.SingleProcessVM
+
+# the ClassLoaderInfo class used for startup
+vm.classloader.class = gov.nasa.jpf.jvm.JVMSystemClassLoaderInfo
+
+# class used to hash/store states (if not set, states are not matched)
+vm.storage.class = gov.nasa.jpf.vm.JenkinsStateSet
+
+# class used to maintain the backtrack stack
+vm.backtracker.class = gov.nasa.jpf.vm.DefaultBacktracker
+
+# serializer to be used by state set (vm.storage.class)
+vm.serializer.class = gov.nasa.jpf.vm.serialize.CFSerializer
+#vm.serializer.class = gov.nasa.jpf.vm.serialize.AdaptiveSerializer
+#vm.serializer.class = gov.nasa.jpf.vm.serialize.FilteringSerializer
+
+# the class that models static fields and classes
+vm.statics.class = gov.nasa.jpf.vm.OVStatics
+
+# the class that models the heap
+#vm.heap.class = gov.nasa.jpf.vm.PSIMHeap
+vm.heap.class = gov.nasa.jpf.vm.OVHeap
+
+# the class representing the list of all threads
+vm.threadlist.class = gov.nasa.jpf.vm.ThreadList
+
+# restorer to be used by backtracker such as DefaultBacktracker UNLESS a
+# serializer that is also a restorer (such as CollapsingSerializer) is used.
+# I.e. this is only read if serializer is not used or it's not a StateRestorer
+vm.restorer.class = .vm.DefaultMementoRestorer
+
+# where do we get the standard libraries from?
+# "<system>" is replaced by the host VM sun.boot.class.path setting
+vm.boot_classpath = <system>
+
+# instruction factory
+jvm.insn_factory.class = gov.nasa.jpf.jvm.bytecode.InstructionFactory
+
+# fields factory
+vm.fields_factory.class = gov.nasa.jpf.vm.DefaultFieldsFactory
+
+# pattern list for assertion enabled/disabled classes
+#vm.enable_assertions = *
+#vm.disable_assertions=
+
+# do we support the Verify.ignorePath() API (to imperatively ignore paths in modeled/instrumented programs)?
+vm.verify.ignore_path = true
+
+
+vm.scheduler.class = gov.nasa.jpf.vm.DelegatingScheduler
+vm.scheduler.sync.class = gov.nasa.jpf.vm.AllRunnablesSyncPolicy
+vm.scheduler.sharedness.class = gov.nasa.jpf.vm.PathSharednessPolicy
+
+# the following properties can be used to set filters for GenericSharednessPolicy instances
+
+# never break on exposure or shared field access from matching methods.
+# NOTE - this is transitive and hence should only include a minimal set of trusted methods
+vm.shared.never_break_methods=\
+      java.util.concurrent.ThreadPoolExecutor.processWorkerExit,\
+      java.util.concurrent.locks.Abstract*Synchronizer.*,\
+      java.util.concurrent.ThreadPoolExecutor.getTask,\
+      java.util.concurrent.atomic.Atomic*.*,\
+      java.util.concurrent.Exchanger.doExchange,\
+      java.util.concurrent.ThreadPoolExecutor.interruptIdleWorkers,\
+      java.util.concurrent.TimeUnit.*
+
+vm.shared.never_break_types=\
+      java.util.concurrent.TimeUnit
+
+# never break on shared access of the following fields. While this is less prone to
+# masking defects than never_break_methods, it should also be used sparingly.
+# NOTE - java.lang.Thread* fields should not be excluded if the SUT explicitly uses 
+# Thread or ThreadGroup objects
+vm.shared.never_break_fields=\
+      java.lang.Thread*.*,\
+      java.lang.System.*,\
+      java.lang.Runtime.*,\
+      java.lang.Boolean.*,\
+      java.lang.String.*,\
+      java.lang.*.TYPE,\
+      java.util.HashMap.EMPTY_TABLE,\
+      java.util.HashSet.PRESENT,\
+      java.util.concurrent.ThreadPoolExecutor*.*,\
+      java.util.concurrent.ThreadPoolExecutor*.*,\
+      java.util.concurrent.TimeUnit.*,\
+      java.util.concurrent.Exchanger.CANCEL,\
+      java.util.concurrent.Exchanger.NULL_ITEM,\
+      java.util.concurrent.Executors$DefaultThreadFactory.*,\
+      sun.misc.VM.*,\
+      sun.misc.SharedSecrets.*,\
+      sun.misc.Unsafe.theUnsafe,\
+      gov.nasa.jpf.util.test.TestJPF.*
+
+
+# do we also break transitions on reference field puts that could make the
+# referenced objects shared
+vm.shared.break_on_exposure = true
+
+# do we try to deduce if a field is supposed to be protected by a lock? In this
+# case, we stop to treat this field as a boundary step, but only if we see the lock
+vm.shared.sync_detection = true
+
+# do we assume final fields to be race-safe (not really true, esp. for
+# instance fields with references leaking from a ctor, but a good starting point)
+vm.shared.skip_finals=true
+
+# ClassLoaders synchronize the loading of a class.
+# Thus, static final fields can never be included in a race condition since only 1 thread is allowed to access the class while it is loading.
+# Defaulted to false to maintain backward compatibility in JPF
+vm.shared.skip_static_finals=false
+
+# When an object's constructor has returned, then the final fields can not be changed.
+# Thus, instance final fields can never be included in a race condition (unless the this reference is leaked from a constructor)
+# Defaulted to false to maintain backward compatibility in JPF
+vm.shared.skip_constructed_finals=false
+
+
+# do we ignore explicitly set Thread.UncaughtHandlers
+vm.ignore_uncaught_handler=false
+
+# do we treat returned Thread.UncaughtHandler.uncaughtException() calls as normal thread termination
+vm.pass_uncaught_handler=true
+
+# do we reclaim unused memory (run garbage collection)
+vm.gc = true
+
+# threshold after which number of allocations to perform a garbage collection
+# (even within the same transition, to avoid lots of short living objects)
+# -1 means never
+vm.max_alloc_gc = -1 
+
+# do we run finalizers on collected objects (only makes sense with garbage collection)
+vm.finalize = false
+
+# this is a preemption boundary specifying the max number of instructions after which we
+# break the current transition if there are other runnable threads
+vm.max_transition_length = 50000
+
+# are thread ids of terminated threads with recycled thread objects reused when creating new
+# threads. This is required for programs that sequentially create many short living threads
+vm.reuse_tid = false
+
+# do we want to halt execution on each throw of an exception that matches
+# one of the given classname wildcard patterns w/o looking for exception handlers?
+# (useful for empty handler blocks, over-permissive catches or generally
+# misused exceptions)
+#vm.halt_on_throw = *
+
+# class that is used to create scheduling relevant choice generators.
+# this will replace the scheduler
+vm.scheduler_factory.class = gov.nasa.jpf.vm.DefaultSchedulerFactory
+
+# print output as it is generated during the search (for all paths)
+vm.tree_output = true
+
+# collect output inside the stored path (to create program trace outout)
+vm.path_output = false
+
+# do we want to store the whole path no matter if we report them
+vm.store_steps=false
+
+# untracked property
+vm.untracked = true
+
+# from where do we initialize the system properties
+#   SELECTED - keys specified as vm.system.properties, values from host
+#   FILE - Java properties file (key=value pairs)
+#   HOST - all system properties from underlying host VM
+vm.sysprop.source = SELECTED
+
+# pathname of property file with system properties
+#vm.sysprop.file =
+
+# list of key names to load from host VM
+#vm.sysprop.keys =
+
+# class we use to model execution time
+vm.time.class = gov.nasa.jpf.vm.SystemTime
+
+# if this is set to true, we throw an exception if we encounter any orphan native peer methods
+vm.no_orphan_methods = false
+
+# if this is set to true, overriden finalize() methods execute upon objects garbage collections
+vm.process_finalizers = false
+
+
+### jvm specifics
+
+# di=o we model nested locks during class init (to detect possible hotspot dealocks during init)
+# (off by default since it can cause state explosion)
+jvm.nested_init=false
+
+# if so, for which classes (default is to exclude system classes)
+jvm.nested_init.exclude=java.*,javax.*,sun.misc.*
+
+
+############################### 3. CG part ###############################
+
+# choice randomization policy in effect:
+#   "NONE" - choice sets are not randomized
+#   "FIXED_SEED" - choice sets are randomized using a fixed seed for each JPF run (reproducible)
+#   "VAR_SEED" - choice sets are randomized using a variable seed for each JPF run
+cg.randomize_choices = NONE
+
+# the standard seed value used for the FIXED_SEED policy
+cg.seed = 42
+
+
+# if this is set, we create choice generators even if there is only a single
+# choice. This is to ensure state storage/matching at all locations where a
+# choice generator *could* be created. The default should be to turn it off though,
+# since this can produce a lot of additional states (esp. with threads)
+cg.break_single_choice = false
+
+
+# default BooleanChoiceGenerator sequence: do we start with 'false'
+cg.boolean.false_first = true
+
+# do we want java.util.Random. nextXX() enumerate choices, or just return a single value?
+# (isn't implemented for all types yet)
+cg.enumerate_random=false
+
+# maximum number of processors returned by Runtime.availableProcessors(). If this is
+# greater than 1, the call represents a ChoiceGenerator
+cg.max_processors=1
+
+# creates a CG upon Thread.start, i.e. breaks the starting transition. Note this is
+# required for data race detection (which depends on detecting access of shared objects)
+cg.threads.break_start=true
+
+# if this option is set we break the transition on Thread.yield()
+cg.threads.break_yield=true
+
+# if this option is set we break the transition on Thread.sleep()
+cg.threads.break_sleep=true
+
+# set if we shold also break on array instructions, e.g. to detect races
+# for array elements. This is off by default because it can cause serious
+# state explosion
+cg.threads.break_arrays=false
+
+# do we support atomic sections. If set to false, Verify.beginAtomic()/endAtomic()
+# will not do anything
+cg.enable_atomic=true
+
+############################### 3. Report Part ###############################
+log.handler.class=gov.nasa.jpf.util.LogHandler
+
+# Windows seem to have a different default
+log.level=warning
+
+report.class=gov.nasa.jpf.report.Reporter
+report.publisher=console
+
+report.console.class=gov.nasa.jpf.report.ConsolePublisher
+
+# this prints out repository information if the 'jpf' topic is set (for debugging)
+#jpf.report.show_repository=true
+
+#property violation reporting is configured separately
+report.console.start=jpf,sut
+
+report.console.transition=
+report.console.constraint=constraint,snapshot
+
+report.console.probe=statistics
+
+report.console.property_violation=error,snapshot
+report.console.show_steps=true
+report.console.show_method=true
+report.console.show_code=false
+
+report.console.finished=result,statistics
+
+#jpf.report.console.show_steps=true
+#jpf.report.console.show_method=true
+#jpf.report.console.show_code=true
+
+report.xml.class=gov.nasa.jpf.report.XMLPublisher
+
+report.html.class=gov.nasa.jpf.report.HTMLPublisher
+report.html.start=jpf,sut,platform,user,dtg,config
+report.html.constraint=constraint
+report.html.property_violation=
+report.html.finished=result,statistics,error,snapshot,output
+
+
+############################### 4. Listener part #############################
+
+# imperative list of listeners
+#listener=
+
+listener.autoload=\
+  gov.nasa.jpf.NonNull,\
+  gov.nasa.jpf.Const
+
+listener.gov.nasa.jpf.NonNull=gov.nasa.jpf.tools.NonNullChecker
+listener.gov.nasa.jpf.Const=gov.nasa.jpf.tools.ConstChecker
+
+
+### PreciseRaceDetector
+
+# we don't check for races in standard libraries
+race.exclude=java.*,javax.*
+
+
+############################### 5. test part #############################
+
+test.report.console.finished=result
diff --git a/nbproject/ide-file-targets.xml b/nbproject/ide-file-targets.xml
new file mode 100644 (file)
index 0000000..255a0d1
--- /dev/null
@@ -0,0 +1,255 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project basedir=".." name="jpf-core-IDE">
+
+    <path id="base.path">
+        <pathelement location="build/main"/>
+        <pathelement location="build/peers"/>
+        <pathelement location="build/annotations"/>
+        <pathelement location="build/tests"/>
+        <fileset dir=".">
+            <include name="lib/*.jar"/>
+        </fileset>
+    </path>
+
+    <sourcepath id="source.path">
+      <pathelement location="src/main"/>
+      <pathelement location="src/peers"/>
+    </sourcepath>
+
+<!--
+    <property name="testrunner" value="gov.nasa.jpf.util.test.TestJPF"/>
+-->
+    <property name="testrunner" value="gov.nasa.jpf.tool.RunTest"/>
+    
+
+    <!-- TODO: do we really have to duplicate this for each container dir? -->
+    <!-- TODO: !!! automatic recompile doesn't work, this is a NetBeans/Ant problem !!! -->
+
+    <!-- (more info: http://www.netbeans.org/kb/articles/freeform-config.html#runsingle) -->
+
+    <target name="test-project">
+        <!-- we need to set the formatter 'usefile' attribute
+             or the NB test runner will not show any tests -->
+        <property name="junit.usefile" value="false"/>
+        <ant antfile="build.xml" inheritall="true" target="test"/>
+    </target>
+
+
+    <!-- 'main' targets -->
+    <target name="run-selected-main">
+        <fail unless="run.class">Must set property 'run.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+        <input message="please enter arguments" addproperty="arg.input"/>
+        <java classname="${run.class}" failonerror="true" fork="true">
+            <classpath>
+                <path refid="base.path"/>
+            </classpath>
+            <jvmarg value="-ea"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+    <target name="debug-selected-main">
+        <fail unless="debug.class">Must set property 'debug.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <path id="cp.main">
+           <path refid="base.path"/>
+        </path>
+        <sourcepath id="sp.main">
+           <path refid="source.path"/>
+        </sourcepath>
+
+
+        <nbjpdastart addressproperty="jpda.address" name="${debug.class}" transport="dt_socket">
+            <classpath refid="cp.main"/>
+            <sourcepath refid="sp.main"/>
+        </nbjpdastart>
+
+        <input message="please enter arguments" addproperty="arg.input"/>
+
+        <java classname="${debug.class}" fork="true" >
+            <classpath refid="cp.main"/>
+            <jvmarg value="-ea"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+
+    <!-- 'example' targets -->
+    <target name="run-selected-example">
+        <fail unless="run.class">Must set property 'run.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+        <input message="please enter arguments" addproperty="arg.input"/>
+        <java classname="${run.class}" failonerror="true" fork="true">
+            <classpath>
+                <pathelement location="build/examples"/>
+                <path refid="base.path"/>
+            </classpath>
+            <jvmarg value="-ea"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+    <target name="debug-selected-example">
+        <fail unless="debug.class">Must set property 'debug.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <path id="cp.examples">
+           <pathelement location="build/examples"/>
+           <path refid="base.path"/>
+        </path>
+        <sourcepath id="sp.examples">
+           <pathelement location="src/examples"/>
+           <path refid="source.path"/>
+        </sourcepath>
+
+
+        <nbjpdastart addressproperty="jpda.address" name="${debug.class}" transport="dt_socket">
+            <classpath refid="cp.examples"/>
+            <sourcepath refid="sp.examples"/>
+        </nbjpdastart>
+
+        <input message="please enter arguments" addproperty="arg.input"/>
+
+        <java classname="${debug.class}" fork="true" >
+            <classpath refid="cp.examples"/>
+            <jvmarg value="-ea"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+    <target name="run-selected-example-jpf">
+        <fail unless="jpf.config">Must set property 'jpf.config'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+        <!-- -->
+        <java classname="gov.nasa.jpf.JPF" failonerror="true" fork="true">
+            <arg value="${jpf.config}"/>
+            <classpath>
+                <pathelement location="build/examples"/>
+                <path refid="base.path"/>
+            </classpath>
+        </java>
+    </target>
+
+    <target name="debug-selected-example-jpf">
+        <fail unless="jpf.config">Must set property 'jpf.config'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <path id="cp.examples">
+           <pathelement location="build/examples"/>
+           <path refid="base.path"/>
+        </path>
+        <sourcepath id="sp.examples">
+           <pathelement location="src/examples"/>
+           <path refid="source.path"/>
+        </sourcepath>
+
+        <nbjpdastart addressproperty="jpda.address" name="${jpf.config}" transport="dt_socket">
+            <classpath refid="cp.examples"/>
+            <sourcepath refid="sp.examples"/>
+        </nbjpdastart>
+
+        <java classname="gov.nasa.jpf.JPF" fork="true">
+            <classpath refid="cp.examples"/>
+            <jvmarg value="-ea"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+            <arg value="-c"/>
+            <arg value="${jpf.config}"/>
+        </java>
+    </target>
+
+
+    <!-- 'test' targets -->
+    <target name="run-selected-test">
+        <fail unless="run.class">Must set property 'run.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+        <input message="please enter arguments" addproperty="arg.input"/>
+        <java classname="${testrunner}" failonerror="true" fork="true">
+            <classpath>
+                <pathelement location="build/tests"/>
+                <path refid="base.path"/>
+            </classpath>
+            <jvmarg value="-ea"/>
+            <arg value="${run.class}"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+    <target name="debug-selected-test">
+        <fail unless="debug.class">Must set property 'debug.class'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <path id="cp.tests">
+          <pathelement location="build/tests"/>
+          <path refid="base.path"/>
+        </path>
+        <sourcepath id="sp.tests">
+          <pathelement location="src/tests"/>
+          <path refid="source.path"/>
+        </sourcepath>
+
+        <nbjpdastart addressproperty="jpda.address" name="${debug.class}" transport="dt_socket">
+            <classpath refid="cp.tests"/>
+            <sourcepath refid="sp.tests"/>
+        </nbjpdastart>
+
+        <input message="please enter arguments" addproperty="arg.input"/>
+
+        <java classname="${testrunner}" fork="true">
+            <classpath refid="cp.tests"/>
+            <jvmarg value="-ea"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+            <arg value="${debug.class}"/>
+            <arg line="${arg.input}"/>
+        </java>
+    </target>
+
+    <target name="run-selected-test-jpf">
+        <fail unless="jpf.config">Must set property 'jpf.config'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+        <java classname="gov.nasa.jpf.JPF" failonerror="true" fork="true">
+            <jvmarg value="-ea"/>
+            <arg value="${jpf.config}"/>
+            <classpath>
+                <pathelement location="build/tests"/>
+                <path refid="base.path"/>
+            </classpath>
+        </java>
+    </target>
+
+    <target  name="debug-selected-test-jpf">
+        <fail unless="jpf.config">Must set property 'jpf.config'</fail>
+        <ant antfile="build.xml" inheritall="false" target="compile"/>
+
+        <path id="cp.tests">
+          <pathelement location="build/tests"/>
+          <path refid="base.path"/>
+        </path>
+        <sourcepath id="sp.tests">
+          <pathelement location="src/tests"/>
+          <path refid="source.path"/>
+        </sourcepath>
+
+        <nbjpdastart addressproperty="jpda.address" name="${jpf.config}" transport="dt_socket">
+            <classpath refid="cp.tests"/>
+            <sourcepath refid="sp.tests"/>
+        </nbjpdastart>
+
+        <java classname="gov.nasa.jpf.JPF" fork="true">
+            <classpath refid="cp.tests"/>
+            <jvmarg value="-ea"/>
+            <jvmarg value="-Xdebug"/>
+            <jvmarg value="-Xrunjdwp:transport=dt_socket,address=${jpda.address}"/>
+            <arg value="-c"/>
+            <arg value="${jpf.config}"/>
+        </java>
+    </target>
+
+</project>
diff --git a/nbproject/project.xml b/nbproject/project.xml
new file mode 100644 (file)
index 0000000..69ebf85
--- /dev/null
@@ -0,0 +1,325 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://www.netbeans.org/ns/project/1">
+    <type>org.netbeans.modules.ant.freeform</type>
+    <configuration>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/1">
+            <name>jpf-core</name>
+        </general-data>
+        <general-data xmlns="http://www.netbeans.org/ns/freeform-project/2">
+            <!-- Do not use Project Properties customizer when editing this file manually. -->
+            <name>jpf-core</name>
+            <properties/>
+            <folders>
+                <source-folder>
+                    <label>jpf-core</label>
+                    <location>.</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/main</label>
+                    <type>java</type>
+                    <location>src/main</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/peers</label>
+                    <type>java</type>
+                    <location>src/peers</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/annotations</label>
+                    <type>java</type>
+                    <location>src/annotations</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/classes</label>
+                    <type>java</type>
+                    <location>src/classes</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/tests</label>
+                    <type>java</type>
+                    <location>src/tests</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+                <source-folder>
+                    <label>src/examples</label>
+                    <type>java</type>
+                    <location>src/examples</location>
+                    <encoding>UTF-8</encoding>
+                </source-folder>
+            </folders>
+            <ide-actions>
+                <action name="build">
+                    <target>build</target>
+                </action>
+                <action name="clean">
+                    <target>clean</target>
+                </action>
+                <action name="test">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>test-project</target>
+                </action>
+                <action name="rebuild">
+                    <target>clean</target>
+                    <target>build</target>
+                </action>
+                <!--  run single file actions -->
+                <!-- run stuff in main -->
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-main</target>
+                    <context>
+                        <property>run.class</property>
+                        <folder>src/main</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-main</target>
+                    <context>
+                        <property>debug.class</property>
+                        <folder>src/main</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <!-- run stuff in examples -->
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-example</target>
+                    <context>
+                        <property>run.class</property>
+                        <folder>src/examples</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-example</target>
+                    <context>
+                        <property>debug.class</property>
+                        <folder>src/examples</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-example-jpf</target>
+                    <context>
+                        <property>jpf.config</property>
+                        <folder>src/examples</folder>
+                        <pattern>\.jpf$</pattern>
+                        <format>absolute-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-example-jpf</target>
+                    <context>
+                        <property>jpf.config</property>
+                        <folder>src/examples</folder>
+                        <pattern>\.jpf$</pattern>
+                        <format>absolute-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <!-- run stuff in test -->
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-test</target>
+                    <context>
+                        <property>run.class</property>
+                        <folder>src/tests</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-test</target>
+                    <context>
+                        <property>debug.class</property>
+                        <folder>src/tests</folder>
+                        <pattern>\.java$</pattern>
+                        <format>java-name</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="run.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>run-selected-test-jpf</target>
+                    <context>
+                        <property>jpf.config</property>
+                        <folder>src/tests</folder>
+                        <pattern>\.jpf$</pattern>
+                        <format>absolute-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+                <action name="debug.single">
+                    <script>nbproject/ide-file-targets.xml</script>
+                    <target>debug-selected-test-jpf</target>
+                    <context>
+                        <property>jpf.config</property>
+                        <folder>src/tests</folder>
+                        <pattern>\.jpf$</pattern>
+                        <format>absolute-path</format>
+                        <arity>
+                            <one-file-only/>
+                        </arity>
+                    </context>
+                </action>
+            </ide-actions>
+            <export>
+                <type>folder</type>
+                <location>build/main</location>
+                <build-target>build</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>build/peers</location>
+                <build-target>build</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>build/classes</location>
+                <build-target>build</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>build/examples</location>
+                <build-target>build</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>build/annotations</location>
+                <build-target>build</build-target>
+            </export>
+            <export>
+                <type>folder</type>
+                <location>build/tests</location>
+                <build-target>build</build-target>
+            </export>
+            <view>
+                <items>
+                    <source-folder style="packages">
+                        <label>src/main</label>
+                        <location>src/main</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>src/peers</label>
+                        <location>src/peers</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>src/annotations</label>
+                        <location>src/annotations</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>src/classes</label>
+                        <location>src/classes</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>src/tests</label>
+                        <location>src/tests</location>
+                    </source-folder>
+                    <source-folder style="packages">
+                        <label>src/examples</label>
+                        <location>src/examples</location>
+                    </source-folder>
+                    <source-file>
+                        <location>build.xml</location>
+                    </source-file>
+                </items>
+                <context-menu>
+                    <ide-action name="build"/>
+                    <ide-action name="rebuild"/>
+                    <ide-action name="clean"/>
+                    <ide-action name="test"/>
+                </context-menu>
+            </view>
+            <subprojects/>
+        </general-data>
+        <java-data xmlns="http://www.netbeans.org/ns/freeform-project-java/4">
+            <compilation-unit>
+                <package-root>src/main</package-root>
+                <classpath mode="compile">build/annotations</classpath>
+                <built-to>build/main</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>src/peers</package-root>
+                <classpath mode="compile">build/annotations;build/main</classpath>
+                <built-to>build/peers</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>src/classes</package-root>
+                <classpath mode="compile">build/annotations;build/main</classpath>
+                <built-to>build/classes</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>src/examples</package-root>
+                <unit-tests/>
+                <classpath mode="compile">build/main</classpath>
+                <built-to>build/examples</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>src/annotations</package-root>
+                <built-to>build/annotations</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+            <compilation-unit>
+                <package-root>src/tests</package-root>
+                <unit-tests/>
+                <classpath mode="compile">build/main:build/annotations:build/peers:build/classes</classpath>
+                <built-to>build/tests</built-to>
+                <source-level>1.8</source-level>
+            </compilation-unit>
+        </java-data>
+        <preferences xmlns="http://www.netbeans.org/ns/auxiliary-configuration-preferences/1">
+            <module name="org-netbeans-modules-editor-indent">
+                <node name="CodeStyle">
+                    <property name="usedProfile" value="default"/>
+                    <node name="project">
+                        <property name="tab-size" value="8"/>
+                        <property name="text-limit-width" value="80"/>
+                    </node>
+                </node>
+            </module>
+        </preferences>
+    </configuration>
+</project>
diff --git a/src/annotations/gov/nasa/jpf/annotation/FilterField.java b/src/annotations/gov/nasa/jpf/annotation/FilterField.java
new file mode 100644 (file)
index 0000000..a94b57c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a field in the model should not be considered during
+ * state matching.
+ * @author peterd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface FilterField {
+  /**
+   * If not the empty string, specifies a property that must be "true" to
+   * activate this filter--unless <code>invert</code> is set.
+   */
+  String condition() default "";
+  
+  /**
+   * If set to <code>true</code>, property must be "false" to activate filter.
+   * Does nothing if <code>condition</code> is empty string.
+   */
+  boolean invert() default false;
+}
\ No newline at end of file
diff --git a/src/annotations/gov/nasa/jpf/annotation/FilterFrame.java b/src/annotations/gov/nasa/jpf/annotation/FilterFrame.java
new file mode 100644 (file)
index 0000000..d659ddc
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * USE CAREFULLY - Indicates that the stack frame of a method should not,
+ * in specified ways, be considered during state matching.
+ *
+ * This can easily cause the search to be cut off even though the VM has made
+ * progress, so USE WISELY!
+ *
+ * @author peterd
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface FilterFrame {
+  /**
+   * True means locals (incl. parameters) and operand stack will be filtered.
+   */
+  boolean filterData() default true;
+
+  /**
+   * True means the location of the next instruction will be filtered.
+   */
+  boolean filterPC() default true;
+
+  /**
+   * True means frames below this one will not appear at all in the abstracted
+   * state.
+   */
+  boolean filterSubframes() default true;
+}
\ No newline at end of file
diff --git a/src/annotations/gov/nasa/jpf/annotation/JPFAttribute.java b/src/annotations/gov/nasa/jpf/annotation/JPFAttribute.java
new file mode 100644 (file)
index 0000000..f33b568
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * a model annotation that causes JPF to automatically set a corresponding
+ * attribute for the target construct upon class loading
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JPFAttribute {
+  String[] value();
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/JPFConfig.java b/src/annotations/gov/nasa/jpf/annotation/JPFConfig.java
new file mode 100644 (file)
index 0000000..dd09a87
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * annotation that can be used to change JPF config properties
+ * from within the SuT
+ * using such annotations should NOT make the SuT JPF dependent
+ */
+@Retention(RetentionPolicy.CLASS)
+public @interface JPFConfig {
+  String[] value(); // each element is a "key[+]=val" pair
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/JPFOption.java b/src/annotations/gov/nasa/jpf/annotation/JPFOption.java
new file mode 100644 (file)
index 0000000..0380011
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+public @interface JPFOption {
+  String type();
+  String key();
+  String defaultValue(); 
+  String comment();
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/JPFOptions.java b/src/annotations/gov/nasa/jpf/annotation/JPFOptions.java
new file mode 100644 (file)
index 0000000..4f8ecb4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+public @interface JPFOptions {
+  
+  JPFOption[] value();
+  
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/MJI.java b/src/annotations/gov/nasa/jpf/annotation/MJI.java
new file mode 100644 (file)
index 0000000..e87f5cf
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * An annotation used to specify native peer methods
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface MJI {
+  boolean noOrphanWarning() default false;
+}
\ No newline at end of file
diff --git a/src/annotations/gov/nasa/jpf/annotation/NeverBreak.java b/src/annotations/gov/nasa/jpf/annotation/NeverBreak.java
new file mode 100644 (file)
index 0000000..207b5cb
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * annotation that is used to mark fields or types which should not
+ * have shared field access CGs
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NeverBreak {
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/NoJPFExecution.java b/src/annotations/gov/nasa/jpf/annotation/NoJPFExecution.java
new file mode 100644 (file)
index 0000000..d04d2ec
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * annotation used to specify that a method is only supposed to be
+ * executed when running outside JPF
+ *
+ * This is useful for model classes that have methods which are intercepted
+ * or cut off by native peers, and we want to ensure that we never execute
+ * these when running under JPF. The standard case for such assertions is
+ * if we refer to JPF itself, and we don't want to get recursive when already
+ * executing under JPF
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface NoJPFExecution {
+}
diff --git a/src/annotations/gov/nasa/jpf/annotation/NonShared.java b/src/annotations/gov/nasa/jpf/annotation/NonShared.java
new file mode 100644 (file)
index 0000000..b53739b
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.annotation;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * instances of @NonShared classes are not allowed to be referenced from
+ * more than one thread (but do allow hand-over)
+ */
+@Retention(RetentionPolicy.RUNTIME)
+public @interface NonShared {
+
+}
diff --git a/src/classes/gov/nasa/jpf/AnnotationProxyBase.java b/src/classes/gov/nasa/jpf/AnnotationProxyBase.java
new file mode 100644 (file)
index 0000000..43d84e4
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+
+/**
+ * common stuff used by all Annotation Proxies
+ */
+public class AnnotationProxyBase {
+
+  public native Class<?> annotationType();
+  
+  // this is just here to be intercepted by the native peer
+  @Override
+  public native String toString();
+  
+  /***
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append('@');
+    
+    Class<?> cls = getClass();
+    String clsName = cls.getName();
+    int idx = clsName.lastIndexOf('$');
+    sb.append(clsName.substring(0, idx));
+    
+    Field[] fields = cls.getDeclaredFields();  
+    if (fields.length > 0){
+      sb.append('(');
+      for (int i=0; i<fields.length; i++){
+        fields[i].setAccessible(true);
+        
+        if (i>0){
+          sb.append(',');
+        }
+        sb.append(fields[i].getName());
+        sb.append('=');
+        
+        try {
+          Object v = fields[i].get(this);
+          Class<?> vcls = v.getClass();
+
+          if (vcls.isArray()){
+            sb.append('[');
+            int n = Array.getLength(v);
+            for (int j=0; j<n; j++){
+              if (j>0){
+                sb.append(',');
+              }
+              sb.append(Array.get(v,j));
+            }            
+            sb.append(']');
+          } else {
+            sb.append(fields[i].get(this));
+          }
+        } catch (IllegalAccessException iax){}
+      }
+      sb.append(')');
+    }
+    
+    return sb.toString();
+  }
+  ***/
+}
diff --git a/src/classes/gov/nasa/jpf/BoxObjectCaches.java b/src/classes/gov/nasa/jpf/BoxObjectCaches.java
new file mode 100644 (file)
index 0000000..ea5076d
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *
+ * Refereces to the caches for the types Byte, Character, Short, Integer, 
+ * Long.
+ * 
+ * This class is added to the startup class list in VM.initialize, to be
+ * loaded before the valueOf()s are called.
+ */
+public class BoxObjectCaches {
+  // Byte cache
+  static Byte byteCache[];
+
+  // Character cache
+  static Character charCache[];
+
+  // Short
+  static Short shortCache[];
+
+  // Integer cache
+  static Integer intCache[];
+
+  // Long cache
+  static Long longCache[];
+}
\ No newline at end of file
diff --git a/src/classes/gov/nasa/jpf/CachedROHttpConnection.java b/src/classes/gov/nasa/jpf/CachedROHttpConnection.java
new file mode 100644 (file)
index 0000000..38f9e96
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.Proxy;
+import java.net.URL;
+
+import sun.net.www.protocol.http.Handler;
+
+/**
+ * this is just a very rough abstraction at this point, which only supports
+ * reading static URL contents. The data is cached for subsequent
+ * access, to avoid DOS by means of model checking
+ */
+public class CachedROHttpConnection extends java.net.HttpURLConnection {
+
+  @Override
+  public void disconnect() {
+    //throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  @Override
+  public boolean usingProxy() {
+    return false;
+  }
+
+  @Override
+  public void connect() throws IOException {
+    //throw new UnsupportedOperationException("Not supported yet.");
+  }
+
+  String host;
+  int port;
+
+  public CachedROHttpConnection(URL u, String host, int port){
+    super(u);
+
+    this.host = host;
+    this.port = port;
+  }
+
+  public CachedROHttpConnection(URL u, Proxy p, Handler handler){
+    super(u);
+  }
+
+  public CachedROHttpConnection(URL u, Proxy p) {
+    this (u, p, new Handler());
+  }
+
+  protected CachedROHttpConnection(URL u, Handler handler)  throws IOException {
+    this(u, null, handler);
+  }
+
+
+
+  private native byte[] getContents(String url);
+
+  @Override
+  public synchronized InputStream getInputStream() throws IOException {
+    byte[] data = getContents(url.toString());
+    return new ByteArrayInputStream(data);
+  }
+
+
+}
diff --git a/src/classes/gov/nasa/jpf/ConsoleOutputStream.java b/src/classes/gov/nasa/jpf/ConsoleOutputStream.java
new file mode 100644 (file)
index 0000000..43a770d
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+/**
+ * this is what we use for System.out and System.err, hence we go native
+ * as quickly as possible, hence we don't need an underlying stream.
+ * It's already slow enough as it is
+ * 
+ * NOTE - we have to intercept *everything* that might go to our base class, since
+ * it is not initialized properly (we want to avoid costly PrinStream init unless
+ * we really need it 
+ */
+public class ConsoleOutputStream extends PrintStream {
+  
+  
+  public ConsoleOutputStream() {
+    // that's a hack - it only works because we intercept the ctor in the native peer
+    // otherwise it would throw an exception
+    super((OutputStream)null);
+  }
+  
+  @Override
+  public void flush() {
+    // we are not buffered anyways
+  }
+  
+  @Override
+  public void close() {
+    // nothing to close
+  }
+  
+  @Override
+  public native void print (boolean b);
+  public native void print (byte b);
+  @Override
+  public native void print (char c);
+  public native void print (short s);
+  @Override
+  public native void print (int i);
+  @Override
+  public native void print (long l);
+  @Override
+  public native void print (float f);
+  @Override
+  public native void print (double d);
+  @Override
+  public native void print (String s);
+  
+  @Override
+  public void print (Object o){
+    if (o == null) {
+      print("null");
+    } else {
+      print(o.toString());
+    }
+  }
+    
+  @Override
+  public native void println (boolean b);
+  public native void println (byte b);
+  @Override
+  public native void println (char c);
+  public native void println (short s);
+  @Override
+  public native void println (int i);
+  @Override
+  public native void println (long l);
+  @Override
+  public native void println (float f);
+  @Override
+  public native void println (double d);
+  @Override
+  public native void println (String s);
+  
+  @Override
+  public void println (Object o){
+    if (o == null) {
+      println("null");
+    } else {
+      println(o.toString());
+    }
+  }
+  
+  @Override
+  public native void println();
+  
+  @Override
+  public native PrintStream printf (String fmt, Object... args);
+  @Override
+  public PrintStream format (String fmt, Object... args){
+    return printf(fmt,args);
+  }
+  
+  @Override
+  public native void write (int b);
+  @Override
+  public native void write (byte[] buf, int off, int len);
+}
diff --git a/src/classes/gov/nasa/jpf/EventProducer.java b/src/classes/gov/nasa/jpf/EventProducer.java
new file mode 100644 (file)
index 0000000..7b891b7
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * a model class that produces events based on native specs
+ */
+public class EventProducer {
+
+  /**
+   * the main interface to be used from clients (test drivers)
+   * @return false if there was no next event (i.e. this is the end of a trace)
+   */
+  public native boolean processNextEvent();
+  
+  public native String getEventName();
+  
+  //--- for testing and debugging purposes
+  public native boolean checkPath();
+  public native boolean isCompletelyCovered();
+}
diff --git a/src/classes/gov/nasa/jpf/FinalizerThread.java b/src/classes/gov/nasa/jpf/FinalizerThread.java
new file mode 100644 (file)
index 0000000..af1951f
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *  
+ * Represents the finalizer thread which runs finalize() methods upon
+ * garbage collection of finalizable objects, i.e. the class of a finalizable 
+ * object overrides the Object.finalize() method.
+ * 
+ * Note that, by default, we don't process finalizers. To run finalizers one
+ * needs to set the property "vm.process_finalizers" to true.
+ */
+public class FinalizerThread extends Thread {
+  
+  // upon their garbage collection, finalizable objects are added into this list instead 
+  // of being removed by gc().sweep()
+  private Object[] finalizeQueue;
+  
+  private Object semaphore;
+  private boolean done;
+  
+  
+  private void runAllFinalizers() {
+    while(!isEmpty()) {
+      try {
+        // runFinalizer processes and removes the first element in the list
+        runFinalizer(finalizeQueue[0]);
+      } catch (Throwable e) { 
+        // FinalizerThread ignores exceptions thrown by finalizers.
+      }
+    }
+  }
+  
+  boolean isEmpty() {
+    return (finalizeQueue.length == 0);
+  }
+  
+  // invoke finalize() on the give object
+  private native void runFinalizer(Object o);
+  
+  private native void manageState();
+  
+  private void processFinalizers() {
+    runAllFinalizers();
+    manageState();
+  }
+
+  @Override
+  public void run() {
+    while (!done){
+      processFinalizers();
+    }
+  }
+}
diff --git a/src/classes/gov/nasa/jpf/SerializationConstructor.java b/src/classes/gov/nasa/jpf/SerializationConstructor.java
new file mode 100644 (file)
index 0000000..012b25b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import java.lang.reflect.Constructor;
+
+public class SerializationConstructor<T> extends Constructor<T> {
+
+  // those are set by the the sun.reflect.ReflectionFactory
+  Class<T> mdc;
+  Constructor<?> firstNonSerializableCtor;
+  
+  @Override
+  public Class<T> getDeclaringClass() {
+    return mdc;
+  }
+  
+  // this has to be native because we need to create a new object of the
+  // mdc without initializing it
+  @Override
+  public native T newInstance (Object... args);
+  
+  @Override
+  public boolean isSynthetic () {
+    return true;
+  }
+
+}
diff --git a/src/classes/java/io/File.java b/src/classes/java/io/File.java
new file mode 100644 (file)
index 0000000..6d57bde
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import gov.nasa.jpf.annotation.FilterField;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+
+/**
+ * MJI model class for java.io.File
+ *
+ * NOTE - a number of methods are only stubbed out here to make Eclipse compile
+ * JPF code that uses java.io.File (there is no way to tell Eclipse to exclude the
+ * model classes from ths build-path)
+ *
+ * @author Owen O'Malley
+ */
+public class File
+{
+  public static final String separator = System.getProperty("file.separator");
+  public static final char separatorChar = separator.charAt(0);
+  public static final String pathSeparator = System.getProperty("path.separator");
+  public static final char pathSeparatorChar = pathSeparator.charAt(0);
+
+  @FilterField int id; // link to the real File object
+  private String filename;
+
+  public File(String filename) {
+    if (filename == null){
+      throw new NullPointerException();
+    }
+    
+    this.filename = filename;
+  }
+
+  public File (String parent, String child) {
+       filename = parent + separator + child;
+  }
+  
+  public File (File parent, String child) {
+    filename = parent.filename + separator + child;
+  }
+  
+  public File(java.net.URI uri) { throw new UnsupportedOperationException(); }
+  
+  public String getName() {
+    int idx = filename.lastIndexOf(separatorChar);
+    if (idx >= 0){
+      return filename.substring(idx+1);
+    } else {
+      return filename;
+    }
+  }
+
+  public String getParent() {
+    int idx = filename.lastIndexOf(separatorChar);
+    if (idx >= 0){
+      return filename.substring(0,idx);
+    } else {
+      return null;
+    }
+  }
+  
+  public int compareTo(File that) {
+    return this.filename.compareTo(that.filename);
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof File){
+      File otherFile = (File) o;
+      return filename.equals(otherFile.filename);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public int hashCode() {
+    return filename.hashCode();
+  }
+  
+  @Override
+  public String toString()  {
+    return filename;
+  }
+  
+  
+  //--- native peer intercepted (hopefully)
+  
+  int getPrefixLength() { return 0; }
+  public native File getParentFile();
+  
+  public String getPath() {
+    return filename;
+  }
+
+  public native boolean isAbsolute();
+  public native String getAbsolutePath();
+  public native File getAbsoluteFile();
+  public native String getCanonicalPath() throws java.io.IOException;
+
+  public native File getCanonicalFile() throws java.io.IOException;
+
+  private native String getURLSpec();
+  public java.net.URL toURL() throws java.net.MalformedURLException {
+    return new URL(getURLSpec());
+  }
+
+  private native String getURISpec();
+  public java.net.URI toURI() {
+    try {
+      return new URI(getURISpec());
+    } catch (URISyntaxException x){
+      return null;
+    }
+  }
+
+  public native boolean canRead();
+  public native boolean canWrite();
+  public native boolean exists();
+  public boolean isDirectory() { return false; }
+  public boolean isFile() { return false; }
+  public boolean isHidden() { return false; }
+  public long lastModified() { return -1L; }
+  public long length() { return -1; }
+  public native boolean createNewFile() throws java.io.IOException;
+  public boolean delete()  { return false; }
+  public void deleteOnExit() {}
+  public String[] list()  { return null; }
+  public String[] list(FilenameFilter fnf)  { return null; }
+  public File[] listFiles()  { return null; }
+  public File[] listFiles(FilenameFilter fnf)  { return null; }
+  public File[] listFiles(FileFilter ff)  { return null; }
+  public boolean mkdir()  { return false; }
+  public boolean mkdirs() { return false; }
+  public boolean renameTo(File f)  { return false; }
+  public boolean setLastModified(long t)  { return false; }
+  public boolean setReadOnly()  { return false; }
+  
+  public static native File[] listRoots();
+  
+  public static File createTempFile(String prefix, String suffix, File dir) throws IOException  {
+    if (prefix == null){
+      throw new NullPointerException();
+    }
+    
+    String tmpDir;
+    if (dir == null){
+      tmpDir = System.getProperty("java.io.tmpdir");
+      if (tmpDir == null){
+        tmpDir = ".";
+      }
+      if (tmpDir.charAt(tmpDir.length()-1) != separatorChar){
+        tmpDir += separatorChar;
+      }
+      
+      if (suffix == null){
+        suffix = ".tmp";
+      }
+    } else {
+      tmpDir = dir.getPath();
+    }
+    
+    return new File(tmpDir + prefix + suffix);
+  }
+  
+  public static File createTempFile(String prefix, String suffix) throws IOException  {
+    return createTempFile(prefix, suffix, null);
+  }
+}
diff --git a/src/classes/java/io/FileDescriptor.java b/src/classes/java/io/FileDescriptor.java
new file mode 100644 (file)
index 0000000..2e5523d
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import gov.nasa.jpf.annotation.FilterField;
+
+/**
+ * a simple abstraction for a file descriptor, which for us is little more
+ * than just an id for a native data buffer (we don't want to keep the
+ * data itself in the JPF space)
+ * 
+ * <2do> still needs the standard descriptors
+ */
+public class FileDescriptor {
+  @FilterField int fd; // to be set from the native side
+  
+  static final int FD_READ = 0;
+  static final int FD_WRITE = 1;
+  
+  static final int FD_NEW = 0;
+  static final int FD_OPENED = 1;
+  static final int FD_CLOSED = 2;
+  
+  String fileName;
+  int mode; // no use to turn it into an Enum - it's accessed from the native peer
+  
+  // we can't use the 'fd' field for it, because we need to keep that in
+  // case we have to automatically reopen after a backtrack
+  int state = FD_NEW;
+  
+  long off; // we need to keep this on the model side to make it backtrackable
+    
+  public FileDescriptor () {
+    fd = -1;
+  }
+  
+  FileDescriptor (String fname, int mode) throws IOException, FileNotFoundException  {
+    fileName = fname;
+    this.mode = mode;
+    
+    fd = open(fname, mode);
+    
+    if (fd != -1){
+      state = FD_OPENED;
+    } else {
+      throw new FileNotFoundException(fname);
+    }
+  }
+  
+  public boolean valid () {
+    return (fd != -1);
+  }
+  
+  public void close () throws IOException {
+    close0();
+    state = FD_CLOSED;
+  }
+  
+  //--- those are the real work horses
+  native int open (String fname, int mode) throws IOException;
+  public native void sync();  
+  native int read () throws IOException;
+  public native int read (byte[] buf, int off, int len);
+  native long skip(long n) throws IOException;
+  native int available () throws IOException;
+  native void close0 () throws IOException;
+  
+  native void write (int b) throws IOException;
+  public native void write (byte[] buf, int off, int len);
+}
diff --git a/src/classes/java/io/FileInputStream.java b/src/classes/java/io/FileInputStream.java
new file mode 100644 (file)
index 0000000..98c2454
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import java.nio.channels.FileChannel;
+
+/**
+ * a simple model to read data w/o dragging the file system content into
+ * the JPF memory
+ */
+public class FileInputStream extends InputStream {
+
+  FileDescriptor fd;
+  private FileChannel fc = null;
+
+  public FileInputStream (String fname) throws FileNotFoundException {
+    try {
+      fd = new FileDescriptor(fname, FileDescriptor.FD_READ);
+    } catch (IOException iox){
+      throw new FileNotFoundException(fname);
+    }
+  }
+  
+  public FileInputStream (File file) throws FileNotFoundException {
+    this( file.getAbsolutePath());
+  }
+  
+  public FileInputStream (FileDescriptor fd) {
+    this.fd = fd;
+  }
+  
+  @Override
+  public int read(byte b[]) throws IOException {
+    return read(b,0,b.length);
+  }
+
+  public FileChannel getChannel() {
+    if(this.fc ==null){
+      this.fc = new FileChannel(fd);
+    }
+    return this.fc;
+  }
+  
+  //--- our native peer methods
+  
+  boolean open (String fname) {
+    // this sets the FileDescriptor from the peer side
+    return false;
+  }
+  
+  @Override
+  public int read() throws IOException {
+    return fd.read();
+  }
+
+  @Override
+  public int read(byte b[], int off, int len) throws IOException {
+    return fd.read(b,off,len);
+  }
+  
+  @Override
+  public long skip(long n) throws IOException {
+    return fd.skip(n);
+  }
+
+  @Override
+  public int available () throws IOException {
+    return fd.available();
+  }
+  
+  @Override
+  public void close () throws IOException {
+    fd.close();
+  }
+  
+  
+}
diff --git a/src/classes/java/io/FileOutputStream.java b/src/classes/java/io/FileOutputStream.java
new file mode 100644 (file)
index 0000000..169837d
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import java.nio.channels.FileChannel;
+
+public class FileOutputStream extends OutputStream {
+
+  FileDescriptor fd;
+  private FileChannel fc = null;
+  
+  public FileOutputStream (String fname) throws FileNotFoundException {
+    try {
+      fd = new FileDescriptor(fname, FileDescriptor.FD_WRITE);
+    } catch (IOException iox){
+      throw new FileNotFoundException(fname);
+    }
+  }
+  
+  public FileOutputStream (File file) throws FileNotFoundException {
+    this( file.getAbsolutePath());
+  }
+  
+  public FileOutputStream (FileDescriptor fd) {
+    this.fd = fd;
+  }
+  
+  public FileChannel getChannel() {
+    if(this.fc ==null){
+      this.fc = new FileChannel(fd);
+    }
+    return this.fc;
+  }
+  
+  public FileDescriptor getFD() {
+    return fd;
+  }
+  
+  //--- our native peer methods
+  
+  boolean open (String fname) {
+    // this sets the FileDescriptor from the peer side
+    return false;
+  }
+  
+  @Override
+  public void write (int b) throws IOException {
+    fd.write(b);
+  }
+
+  @Override
+  public void write (byte[] buf, int off, int len) throws IOException {
+    fd.write(buf, off, len);
+  }
+  
+  @Override
+  public void close () throws IOException {
+    fd.close();
+  }
+
+  @Override
+  public void flush () throws IOException {
+    fd.sync();
+  }
+}
diff --git a/src/classes/java/io/InputStreamReader.java b/src/classes/java/io/InputStreamReader.java
new file mode 100644 (file)
index 0000000..f235a10
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+
+/**
+ * how hard can it be to transform byte(s) into a char? I hate Unicode
+ */
+public class InputStreamReader extends Reader {
+
+  static final int BUF_SIZE=128;
+  private static Object lock = new Object(); // our peer has state
+  
+  InputStream in;
+  byte[] bbuf = new byte[BUF_SIZE];
+  String charSetName=null;
+  
+  public InputStreamReader (InputStream in){
+    this.in = in;
+  }  
+  
+  public InputStreamReader (InputStream in,String charSetName){
+    this.in = in;
+    this.charSetName = charSetName;
+  }  
+  
+  @Override
+  public void close () throws IOException {
+    in.close();
+  }
+
+  private native int decode (int b, boolean endOfInput);
+
+  @Override
+  public boolean ready() {
+    try {
+      return (in.available() > 0);
+    } catch (IOException iox){
+      return false;
+    }
+  }
+  
+  @Override
+  public int read () throws IOException {    
+    synchronized (lock){
+      while (true){
+        
+        int b = in.read();
+        if (b < 0){
+          return -1;
+        }
+
+        int c = decode(b, (in.available() == 0));
+        if (c >= 0 ) {
+          return c;
+        }
+      }
+    }
+  }
+    
+  
+  
+  native int decode (byte[] inBuf,int len,
+                            char[] outBuf, int off,
+                            boolean endOfInput);
+
+  
+  @Override
+  public int read (char[] cbuf, int off, int len) throws IOException {
+    int r = 0;
+    boolean available = true;
+    
+    synchronized (lock){
+      while (available && r < len){
+        // <2do> - so what if that backtracks? the peer might have 
+        // iteration-specific state that gets lost. see native peer comments        
+        int b = in.read(bbuf, 0, Math.min(len-r,bbuf.length));
+        if (b < 0){
+          return (r == 0) ? -1 : r;
+        }
+
+        // true if we have not reach the end of the input stream "in"
+        // and there are still more bytes available to be read
+        available = (in.available() > 0);
+        
+        r += decode(bbuf,b, cbuf,off+r, !available);
+      }
+    }
+      
+    return r;
+  }
+  
+}
diff --git a/src/classes/java/io/OutputStreamWriter.java b/src/classes/java/io/OutputStreamWriter.java
new file mode 100644 (file)
index 0000000..070165f
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import gov.nasa.jpf.vm.Verify;
+
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+/**
+ * natively convert char output into byte output
+ *
+ * <2do> - this needs to be reworked. It trades locks for saving heap objects,
+ * and that is a choice between a rock and a hard place. We shouldn't call
+ * write() from within the native encode() (which we would have to since this
+ * could be any OutputStream). We shouldn't create a very short
+ * living JPF byte[] object per write() call, since that kills the heap. We
+ * shouldn't use an explicit lock since that blows up the state space. That
+ * leaves us with atomic sections, but it's not really safe since clients
+ * might use their own OutputStream classes with synchronized write() methods.
+ * At least this would not go unnoticed
+ */
+public class OutputStreamWriter extends Writer {
+
+  static final int BUF_SIZE=128;
+  //private static Object lock = new Object();
+  
+  OutputStream out;
+  
+  byte[] buf = new byte[BUF_SIZE*6]; // worst case UTF-8 
+  
+  public OutputStreamWriter(OutputStream os) {
+    out = os;
+  }
+  
+  public OutputStreamWriter(OutputStream os, Charset cs) {
+    out = os;
+    throw new UnsupportedOperationException("OutputStreamWriter model does not fully implement this constructor");
+  }
+  
+  public OutputStreamWriter(OutputStream os, CharsetEncoder end) {
+    out = os;
+    throw new UnsupportedOperationException("OutputStreamWriter model does not fully implement this constructor");
+  }
+  
+  public OutputStreamWriter(OutputStream os, String charsetName) {
+    out = os;
+    throw new UnsupportedOperationException("OutputStreamWriter model does not fully implement this constructor");
+  }
+  
+  @Override
+  public void close() throws IOException {
+    out.close();
+  }
+  
+  @Override
+  public void flush() {
+    // nothing
+  }
+
+  void flushBuffer() {
+    // nothing
+  }
+  
+  public native String getEncoding();
+  
+  native int encode (char[] cbuf, int off, int len, byte[] buf);
+  
+  @Override
+  public void write(char[] cbuf, int off, int len) throws IOException {
+    int w=0;
+    
+    //synchronized(lock){
+    Verify.beginAtomic();
+      while (w < len){
+        int n = encode(cbuf, off+w, len-w, buf);
+        out.write(buf, 0, n);
+        w += n;
+      }
+    Verify.endAtomic();
+    //}
+  }
+  
+  private native int encode (char c, byte[] buf);
+
+  @Override
+  public void write (int c) throws IOException {
+    //synchronized(lock){
+    Verify.beginAtomic();
+      int n = encode((char)c, buf);
+      out.write(buf,0,n);
+    Verify.endAtomic();
+    //}
+  }
+  
+  private native int encode (String s, int off, int len, byte[] buf);
+  
+  @Override
+  public void write(String s, int off, int len) throws IOException {
+    int w=0;
+    
+    //synchronized(lock){
+    Verify.beginAtomic();
+      while (w < len){
+        int n = encode(s, off+w, len-w, buf);
+        out.write(buf, 0, n);
+        w += n;
+      }
+    Verify.endAtomic();
+    //}
+  }
+}
diff --git a/src/classes/java/io/RandomAccessFile.java b/src/classes/java/io/RandomAccessFile.java
new file mode 100644 (file)
index 0000000..39f2f97
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.io;
+
+import java.nio.channels.FileChannel;
+
+import java.io.FileDescriptor;
+/**
+ * MJI model class for java.io.RandomAccessFile
+ * This class cannot yet handle File I/O correctly
+ * Some work about the use of RandomAccessFile can be 
+ * found here : https://bitbucket.org/pfjeau/jpf_for_nanohttpd/src/8f880ee27410026c69cf37f1904b159965d1576e/?at=raf-progress
+ * Another way to implement all the missing features is to fix the jpf-bfs project in order to handle file I/O
+ *
+ * @author Owen O'Malley
+ */
+@SuppressWarnings("unused")
+public class RandomAccessFile {
+  public RandomAccessFile(File name, String permissions
+                         ) throws FileNotFoundException {
+    filename = name;
+    isOpen = true;
+    isReadOnly = "r".equals(permissions);
+    setDataMap();
+  }
+
+  public RandomAccessFile(String name, String permissions
+                         ) throws FileNotFoundException {
+    this(new File(name), permissions);
+  }
+
+  public void seek(long posn) throws IOException {
+    currentPosition = posn;
+  }
+
+  public long length() throws IOException {
+    return currentLength;
+  }
+
+  public native void setDataMap();
+  
+  public native void writeByte(int data) throws IOException;
+
+  public native void write(byte[] data, int start, int len
+                          ) throws IOException;
+
+
+  public native void setLength(long len) throws IOException;
+
+  public native int read(byte[] data, int start, int len
+                         ) throws IOException;
+
+  public native byte readByte() throws IOException;
+
+  public void close() throws IOException {
+    isOpen = false;
+  }
+
+  public FileChannel getChannel(){
+    return null;//TODO
+  }
+
+  public FileDescriptor getFD(){
+    return null;//TODO
+  }
+
+  private static class DataRepresentation {
+    DataRepresentation next;
+    long chunk_index;
+    int[] data;
+  }
+
+  private final static void printList(DataRepresentation node) {
+    DataRepresentation cur = node;
+    System.out.print("Chunks:");
+    while (cur != null) {
+      System.out.print(" " + cur.chunk_index);
+      cur = cur.next;
+    }
+    System.out.println();
+  }
+
+  private static final int CHUNK_SIZE = 256;
+  private File filename;
+  private boolean isOpen;
+  private boolean isReadOnly;
+  private long currentLength;
+  private long currentPosition;
+  private DataRepresentation data_root = null;
+}
+
diff --git a/src/classes/java/lang/Class.java b/src/classes/java/lang/Class.java
new file mode 100644 (file)
index 0000000..379b60a
--- /dev/null
@@ -0,0 +1,362 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AnnotatedElement;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.GenericDeclaration;
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+
+import sun.reflect.ConstantPool;
+import sun.reflect.annotation.AnnotationType;
+
+/**
+ * MJI model class for java.lang.Class library abstraction
+ *
+ * This is a JPF specific version of a system class because we can't use the real,
+ * platform VM specific version (it's native all over the place, its field
+ * structure isn't documented, most of its methods are private, hence we can't
+ * even instantiate it properly).
+ *
+ * Note that this class never gets seen by the real VM - it's for JPF's eyes only.
+ *
+ * For now, it's only a fragment to test the mechanism, and provide basic
+ * class.getName() / Class.ForName() support (which is (almost) enough for
+ * Java assertion support
+ */
+@SuppressWarnings("unused")  // native peer uses
+public final class Class<T> implements Serializable, GenericDeclaration, Type, AnnotatedElement {
+
+  /** don't use serialVersionUID from JDK 1.1 for interoperability */
+  private static final long serialVersionUID = 3206093459760846163L + 1;
+
+   // we init this on demand (from MJIEnv) since it's not used too often
+  private static Annotation[] emptyAnnotations; // = new Annotation[0];
+  
+  private String name;
+
+  private ClassLoader classLoader;
+  
+  /**
+   * search global id of the corresponding ClassInfo, which factors in the classloader
+   */
+  private int nativeId;
+
+  /**
+   * to be set during <clinit> of the corresponding class
+   */
+  private boolean isPrimitive;
+  
+  private Class() {}
+
+  public native boolean isArray ();
+
+  @Override
+  public native Annotation[] getAnnotations();
+
+  @Override
+  public native <A extends Annotation> A getAnnotation( Class<A> annotationCls);
+
+  // those are from Java 6
+  public native boolean isAnnotation ();
+  @Override
+  public native boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);
+
+  public native Class<?> getComponentType ();
+
+  public native Field[] getFields() throws SecurityException;
+  
+  public native Field getDeclaredField (String fieldName) throws NoSuchFieldException,
+                                                          SecurityException;
+
+  public native Field[] getDeclaredFields () throws SecurityException;
+
+  public native Method getDeclaredMethod (String mthName, Class<?>... paramTypes)
+                            throws NoSuchMethodException, SecurityException;
+
+  public native Method getMethod (String mthName, Class<?>... paramTypes)
+                    throws NoSuchMethodException, SecurityException;
+
+  public native Method[] getDeclaredMethods () throws SecurityException;
+
+  public native Method[] getMethods () throws SecurityException;
+    
+  public native Constructor<?>[] getDeclaredConstructors() throws SecurityException;
+
+  public native Constructor<?>[] getConstructors() throws SecurityException;
+
+  private native byte[] getByteArrayFromResourceStream(String name);
+
+  public InputStream getResourceAsStream (String name) {
+    byte[] byteArray = getByteArrayFromResourceStream(name);
+    if (byteArray == null) return null;
+    return new ByteArrayInputStream(byteArray);
+  }
+
+  private native String getResolvedName (String rname);
+
+  public URL getResource (String rname) {
+    String resolvedName = getResolvedName(rname);
+    return getClassLoader().getResource(resolvedName);
+  }
+
+  public Package getPackage() {
+    // very very crude version for now, only supports the package name
+    String pkgName = null;
+
+    int idx = name.lastIndexOf('.');
+    if (idx >=0){
+      pkgName = name.substring(0,idx);
+
+      Package pkg = new Package(pkgName,
+                                "spectitle", "specversion", "specvendor",
+                                "impltitle", "implversion", "implvendor",
+                                 null, getClassLoader());
+        return pkg;
+
+    } else { // weird, but there is no Package object for the default package
+      return null;
+    }
+
+  }
+
+  //--- enum support ()
+  // Java 1.5
+  public native T[] getEnumConstants();
+
+  // Java 6
+  T[] getEnumConstantsShared() {
+    return getEnumConstants();
+  }
+  
+  // lazy initialized map for field name -> Enum constants
+  // <2do> we should move this to the native side, since Enum constants don't change
+  private transient Map<String, T> enumConstantDirectory = null;
+
+  // package private helper for Enum.valueOf()
+  Map<String,T> enumConstantDirectory() {
+    if (enumConstantDirectory == null) {
+      Map<String,T> map = new HashMap<String,T>();
+
+      T[] ae = getEnumConstants();
+      for (T e: ae) {
+        map.put(((Enum)e).name(), e);
+      }
+
+      enumConstantDirectory = map;
+    }
+    return enumConstantDirectory;
+  }
+
+
+  public native Constructor<T> getDeclaredConstructor (Class<?>... paramTypes)
+              throws NoSuchMethodException, SecurityException;
+
+  public native Field getField (String fieldName) throws NoSuchFieldException,
+                                                  SecurityException;
+
+  public native boolean isInstance (Object o);
+
+  public native boolean isAssignableFrom (Class<?> clazz);
+
+  public native boolean isInterface();
+
+  public native Constructor<T> getConstructor (Class<?>... argTypes) throws NoSuchMethodException, SecurityException;
+
+  public native int getModifiers();
+
+  public native Class<?>[] getInterfaces();
+
+  // no use to have a ctor, we can't call it
+  public String getName () {
+    return name;
+  }
+
+  public String getSimpleName () {
+    int idx; // <2do> not really - inner classes?
+    Class<?> enclosingClass = getEnclosingClass();
+    
+    if(enclosingClass!=null){
+      idx = enclosingClass.getName().length();
+    } else{
+      idx = name.lastIndexOf('.');
+    }
+    
+    return name.substring(idx+1);
+  }
+
+  static native Class<?> getPrimitiveClass (String clsName);
+
+   /**
+    * this one is in JPF reflection land, it's 'native' for us
+    */
+  public static native Class<?> forName (String clsName) throws ClassNotFoundException;
+
+  public static Class<?> forName (String clsName, boolean initialize, ClassLoader loader) throws ClassNotFoundException {
+    Class<?> cls;
+    if (loader == null){
+      cls = forName(clsName);
+    } else {
+      cls = loader.loadClass(clsName);
+    }
+    
+    if (initialize) {
+      cls.initialize0();
+    }
+    return cls;
+  }
+
+  /**
+   * forces clinit without explicit field or method access
+   */
+  private native void initialize0 ();
+  
+  public boolean isPrimitive () {
+    return isPrimitive;
+  }
+
+  public native Class<? super T> getSuperclass ();
+
+  public native T newInstance () throws InstantiationException,
+                                      IllegalAccessException;
+
+  @Override
+  public String toString () {
+    return (isInterface() ? "interface " : "class ") + name;
+  }
+
+  @SuppressWarnings("unchecked")
+  public T cast(Object o) {
+    if (o != null && !isInstance(o)) throw new ClassCastException();
+    return (T) o;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public <U> Class<? extends U> asSubclass(Class<U> clazz) {
+    if (clazz.isAssignableFrom(this)) {
+      return (Class<? extends U>) this;
+    } else {
+      throw new ClassCastException("" + this + " is not a " + clazz);
+    }
+  }
+
+  native public boolean desiredAssertionStatus ();
+
+  public ClassLoader getClassLoader() {
+    return classLoader;
+  }
+
+  native ConstantPool getConstantPool();
+
+  native void setAnnotationType (AnnotationType at);
+
+  native AnnotationType getAnnotationType();
+  
+  @Override
+  public TypeVariable<Class<T>>[] getTypeParameters() {
+    throw new UnsupportedOperationException();
+  }
+  
+  public Type getGenericSuperclass() {
+    throw new UnsupportedOperationException();
+  }
+  
+  public Type[] getGenericInterfaces() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Object[] getSigners() {
+    throw new UnsupportedOperationException();
+  }
+
+  void setSigners(Object[] signers) {
+    signers = null;  // Get rid of IDE warning 
+    throw new UnsupportedOperationException();
+  }
+  
+  public Method getEnclosingMethod() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Constructor<?> getEnclosingConstructor() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Class<?> getDeclaringClass() {
+    throw new UnsupportedOperationException();
+  }
+
+  public native Class<?> getEnclosingClass();
+  
+  public String getCanonicalName() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isAnonymousClass() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isLocalClass() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isMemberClass() {
+    throw new UnsupportedOperationException();
+  }
+
+  public Class<?>[] getClasses() {
+    throw new UnsupportedOperationException();
+  }
+  
+  public Class<?>[] getDeclaredClasses() throws SecurityException {
+    throw new UnsupportedOperationException();
+  }
+  
+  public java.security.ProtectionDomain getProtectionDomain() {
+    throw new UnsupportedOperationException();
+  }
+
+  void setProtectionDomain0(java.security.ProtectionDomain pd) {
+    pd = null;  // Get rid of IDE warning 
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isEnum() {
+    throw new UnsupportedOperationException();
+  }
+  
+  @Override
+  public Annotation[] getDeclaredAnnotations() {
+    throw new UnsupportedOperationException();
+  }
+
+  public boolean isSynthetic (){
+    final int SYNTHETIC = 0x00001000;
+    return (getModifiers() & SYNTHETIC) != 0;
+  }
+}
diff --git a/src/classes/java/lang/ClassLoader.java b/src/classes/java/lang/ClassLoader.java
new file mode 100644 (file)
index 0000000..6bc56aa
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.ByteBuffer;
+import java.security.ProtectionDomain;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import sun.misc.CompoundEnumeration;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ *  Model class for java.lang.ClassLoader
+ */
+public abstract class ClassLoader {
+  
+  private ClassLoader parent;
+
+  // This is JPF internal identifier which set to the globalId of the classLoader
+  private int nativeId;
+
+  //--- internals
+
+  protected ClassLoader() {
+    // constructed on the native side
+  }
+
+  protected ClassLoader (ClassLoader parent){
+    // constructed on the native side
+  }
+
+  private native String getResource0 (String rname);
+
+  public URL getResource(String name) {
+    URL url = null;
+
+    if(parent == null) {
+      String resourcePath = getSystemClassLoader().getResource0(name);
+      try {
+        url = new URL(resourcePath);
+      } catch (MalformedURLException x){
+        url = null;
+      }
+    } else {
+      url = parent.getResource(name);
+    }
+
+    if (url == null) {
+      url = findResource(name);
+    }
+    return url;
+  }
+
+  /**
+   * Finds the resource with the given name. Class loader implementations
+   * should override this method to specify where to find resources.
+   */
+  protected URL findResource(String name) {
+      return null;
+  }
+
+  private native String[] getResources0 (String rname);
+
+  /**
+   * Returns an array of URL including all resources with the given name 
+   * found in the classpath of this classloader.
+   */
+  private Enumeration<URL> getResourcesURL(String name) {
+    String[] urls = getResources0(name);
+    Vector<URL> list = new Vector<URL>(0);
+    for(String url: urls) {
+      try {
+        list.add(new URL(url));
+      } catch (MalformedURLException x){
+        // process the rest
+      }
+    }
+
+    return list.elements();
+  }
+
+  @SuppressWarnings({"unchecked","rawtypes"})
+  public Enumeration<URL> getResources(String name) throws IOException {
+    Enumeration<URL>[] resEnum = new Enumeration[2];
+
+    if(parent == null) {
+      resEnum[0] = getSystemClassLoader().getResourcesURL(name);
+    } else{
+      resEnum[0] = parent.getResources(name);
+    }
+    resEnum[1] = findResources(name);
+
+    return new CompoundEnumeration<URL>(resEnum);
+  }
+
+  /**
+   * Returns an enumeration representing all the resources with the given 
+   * name. Class loader implementations should override this method to 
+   * specify where to load resources from.
+   */
+  protected Enumeration<URL> findResources(String name) throws IOException {
+      return (new Vector<URL>()).elements();
+  }
+
+  public InputStream getResourceAsStream (String name){
+    URL foundResource = getResource(name);
+    if (foundResource != null) {
+      try {
+        return foundResource.openStream();
+      } catch (IOException e) {
+        System.err.println("cannot open resource " + name);
+      }
+    }
+    return null;
+  }
+
+  public native static ClassLoader getSystemClassLoader ();
+
+  public static URL getSystemResource(String name){
+    return getSystemClassLoader().getResource(name);
+  }
+
+  public static InputStream getSystemResourceAsStream(String name) {
+    return getSystemClassLoader().getResourceAsStream(name);
+  }
+
+  public static Enumeration<URL> getSystemResources(String name) throws IOException {
+    return getSystemClassLoader().getResources(name);
+  }
+
+  public ClassLoader getParent() {
+    return parent;
+  }
+
+  /**
+   * If the class with the given name has been already defined, it is returned. OW, it
+   * returns null.
+   */
+  protected native final Class<?> findLoadedClass(String name);
+
+  protected native final Class<?> findSystemClass(String name) throws ClassNotFoundException;
+
+  public Class<?> loadClass(String name) throws ClassNotFoundException {
+    Class<?> c = findLoadedClass(name);
+
+    if(c == null) {
+      try {
+        if (parent != null && parent != getSystemClassLoader()) {
+          c = parent.loadClass(name, false);
+        } else {
+          c = findSystemClass(name);
+        }
+      } catch (ClassNotFoundException e) {
+        c = findClass(name);
+      }
+    }
+
+    return c;
+  }
+
+  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+    return loadClass(name);
+  }
+
+  /**
+   * Finds the class with a given name. This method should be overridden by 
+   * ClassLoader subclasses, and it will be used by loadClass().
+   */
+  protected Class<?> findClass(String name) throws ClassNotFoundException {
+      throw new ClassNotFoundException(name);
+  }
+
+  /**
+   * All the class objects are resolved internally by JPF. So this method
+   * does nothing.
+   */
+  protected final void resolveClass(Class<?> c) {
+  }
+
+  private native Class<?> defineClass0(String name, byte[] b, int off, int len);
+
+  protected final Class<?> defineClass(String name, byte[] b, int off, int len) throws ClassFormatError {
+    return defineClass0(name, b, off, len);
+  }
+
+  protected final Class<?> defineClass(String name, byte[] b, int off, int len, ProtectionDomain protectionDomain) throws ClassFormatError {
+    return defineClass(name, b, off, len);
+  }
+
+  protected String findLibrary(String libname) {
+    return null;
+  }
+
+  protected native Package getPackage(String name);
+
+  protected native Package[] getPackages();
+
+  public native void setDefaultAssertionStatus(boolean enabled);
+
+  public native void setClassAssertionStatus(String className, boolean enabled);
+
+  public native void setPackageAssertionStatus(String packageName, boolean enabled);
+
+  public native void clearAssertionStatus();
+
+  //--- unsupported methods
+
+  protected static boolean registerAsParallelCapable() {
+    return true; // dummy, in prep for jdk7
+  }
+
+  protected Object getClassLoadingLock(String className) {
+    throw new UnsupportedOperationException();
+  }
+
+  protected final Class<?> defineClass(byte[] b, int off, int len) 
+      throws ClassFormatError {
+    throw new UnsupportedOperationException();
+  }
+
+  protected final Class<?> defineClass(String name, ByteBuffer b, ProtectionDomain protectionDomain) 
+      throws ClassFormatError {
+    throw new UnsupportedOperationException();
+  }
+
+  protected final void setSigners(Class<?> c, Object[] signers) {
+    throw new UnsupportedOperationException();
+  }
+
+  protected Package definePackage(String name, String specTitle, String specVersion, 
+                                  String specVendor, String implTitle, String implVersion,
+                                  String implVendor, URL sealBase) 
+                                      throws IllegalArgumentException {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/classes/java/lang/InheritableThreadLocal.java b/src/classes/java/lang/InheritableThreadLocal.java
new file mode 100644 (file)
index 0000000..496efe9
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+/**
+ * model of java.lang.InheritableThreadLocal
+ * 
+ * since we model ThreadLocal, we also need to model this one
+ */
+public class InheritableThreadLocal<T> extends ThreadLocal<T> {
+  
+  public InheritableThreadLocal (){
+    // nothing here
+  }
+  
+  // watch out - this can be overridden
+  protected T childValue (T parentVal){
+    return parentVal;
+  }
+
+}
diff --git a/src/classes/java/lang/Object.java b/src/classes/java/lang/Object.java
new file mode 100644 (file)
index 0000000..67269c5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+/**
+ * modeled root class of all Java objects. This is modeled because we already have a peer because of
+ * the synchronization methods and have to cut off the registerNatives() call
+ */
+public class Object {
+  public Object() {
+    // nothing here
+  }
+  
+  public final native Class<?> getClass();
+  public native int hashCode();
+  
+  /**
+   * default implementation is just an identity check
+   */
+  public boolean equals (Object o) {
+    return o == this;
+  }
+
+  
+  public String toString() {
+    // intercepted by peer
+    return null;
+  }
+
+  protected native Object clone() throws java.lang.CloneNotSupportedException;
+  
+  public final native void notify();
+  public final native void notifyAll();
+  
+  public final native void wait (long timeout) throws java.lang.InterruptedException;
+  
+  public final void wait (long timeout, int nanos) throws java.lang.InterruptedException{
+    // intercepted by peer
+  }
+  
+  public final void wait() throws java.lang.InterruptedException {
+    // intercepted by peer   
+  }
+  
+  protected void finalize() throws java.lang.Throwable {
+    
+  }
+}
diff --git a/src/classes/java/lang/StackTraceElement.java b/src/classes/java/lang/StackTraceElement.java
new file mode 100644 (file)
index 0000000..0f4e452
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.File;
+
+/**
+ * MJI model class for java.lang.StackTraceElement
+ */
+public class StackTraceElement {
+  
+  String clsName;
+  String fileName;
+  String mthName;
+  int    line;
+  
+  public StackTraceElement() {
+     // nothing to do
+  }
+
+  public StackTraceElement (String clsName, String mthName, String fileName, int line) {
+    if (clsName == null) {
+      throw new NullPointerException("Declaring class is null");
+    } 
+
+    if (mthName == null) {
+      throw new NullPointerException("Method name is null");
+    }
+
+    this.clsName = clsName;
+    this.mthName = mthName;
+    this.fileName = fileName;
+    this.line = line;
+  }
+
+  public String getClassName () {
+    return clsName;
+  }
+
+  public String getFileName () {
+    return fileName;
+  }
+
+  public int getLineNumber () {
+    return line;
+  }
+
+  public String getMethodName () {
+    return mthName;
+  }
+
+  public boolean isNativeMethod () {
+    return false;
+  }
+
+  /**
+  public int hashCode () {
+    return 0;
+  }
+  **/
+
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder();
+    sb.append(clsName);
+    sb.append('.');
+    sb.append(mthName);
+    sb.append("(");
+
+    sb.append(new File(fileName).getName());
+    
+    if (line >= 0){
+      sb.append(':');
+      sb.append(line);
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+}
diff --git a/src/classes/java/lang/String.java b/src/classes/java/lang/String.java
new file mode 100644 (file)
index 0000000..a37c6fa
--- /dev/null
@@ -0,0 +1,372 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.ObjectStreamField;
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Locale;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * MJI adapter for java.lang.String, based on jdk 1.7.0_07 source.
+ * Adapted by frank.
+ * 
+ * We have to model java.lang.String since it changed its implementation between
+ * Java 1.6 and Java 1.7, and JPF is initializing string objects internally without
+ * doing roundtrips (for performance reasons), i.e. we need to be able to rely
+ * on fields of the String implementation.
+ * 
+ * The silver lining is that most methods are now native and don't appear in
+ * traces anymore. Spending a lot of JPF cycles (instruction.execute()) on
+ * String is just not worth it in terms of potential properties.
+ */
+
+public final class String
+implements java.io.Serializable, Comparable<String>, CharSequence {
+
+       /** The value is used for character storage. */
+       private final char value[];
+
+       /** Cache the hash code for the string */
+       private int hash; // Default to 0
+
+       private static final long serialVersionUID = -6849794470754667710L;
+
+
+       private static final ObjectStreamField[] serialPersistentFields =
+                       new ObjectStreamField[0];
+
+       public String() {
+               this.value = new char[0];
+       }
+
+       public String(String original) {
+               this.value = original.value;
+               this.hash = original.hash;
+       }
+
+       public String(char value[]) {
+               this.value = Arrays.copyOf(value, value.length);
+       }
+
+       public String(char value[], boolean share) {
+               this.value = Arrays.copyOf(value, value.length);
+       }
+       
+       public String(char value[], int offset, int count) {
+               String proxy=init(value,offset,count);
+               this.value=proxy.value;
+               this.hash=proxy.hash;
+       }
+
+       private native String init(char[] value, int offset, int count);
+
+       public String(int[] codePoints, int offset, int count) {
+               String proxy=init(codePoints,offset,count);
+               this.value=proxy.value;
+               this.hash=proxy.hash;
+       }
+
+       private native String init(int[] codePoints, int offset, int count);
+
+       @Deprecated
+       public String(byte ascii[], int hibyte, int offset, int count) {
+               String proxy=init(ascii,hibyte,offset,count);
+               this.value=proxy.value;
+               this.hash=proxy.hash;
+       }
+
+       private native String init(byte ascii[], int hibyte, int offset, int count);
+
+       @Deprecated
+       public String(byte ascii[], int hibyte) {
+               this(ascii, hibyte, 0, ascii.length);
+       }
+
+
+
+       public String(byte bytes[], int offset, int length, String charsetName){
+               String proxy=init(bytes,offset,length,charsetName);
+               this.value=proxy.value;
+               this.hash=proxy.hash;
+       }
+
+       private native String init(byte bytes[], int offset, int length, String charsetName);
+
+
+       public String(byte x[], int offset, int length, Charset cset) {
+               // no Charset model
+               if (cset == null){
+                       throw new NullPointerException("cset");
+               }
+               if (length < 0){
+                       throw new StringIndexOutOfBoundsException(length);
+               }
+               if (offset < 0){
+                       throw new StringIndexOutOfBoundsException(offset);
+               }
+               if (offset > x.length - length){
+                       throw new StringIndexOutOfBoundsException(offset + length);
+               }
+
+               this.value =  StringCoding.decode(cset, x, offset, length);
+       }
+
+       public String(byte bytes[], String charsetName)
+                       throws UnsupportedEncodingException {
+               this(bytes, 0, bytes.length, charsetName);
+       }
+
+       public String(byte bytes[], Charset charset) {
+               this(bytes, 0, bytes.length, charset);
+       }
+
+       public String(byte bytes[], int offset, int length) {
+               String proxy=init(bytes,offset,length);
+               this.value=proxy.value;
+               this.hash=proxy.hash;
+       }
+
+
+       private native String init(byte bytes[], int offset, int length);
+
+       public String(byte bytes[]) {
+               this(bytes, 0, bytes.length);
+       }
+
+
+       public String(StringBuffer x) {
+               synchronized(x) {
+                       this.value = Arrays.copyOf(x.getValue(), x.length());
+               }
+       }
+
+
+       public String(StringBuilder x) {
+               this.value = Arrays.copyOf(x.getValue(), x.length());
+       }
+
+       @Deprecated
+       String(int offset, int count, char[] value) {
+               this(value, offset, count);
+       }
+       @Override
+       public int length() {
+               return value.length;
+       }
+       public boolean isEmpty() {
+               return value.length == 0;
+       }
+       @Override
+       public char charAt(int index) {
+               if ((index < 0) || (index >= value.length)) {
+                       throw new StringIndexOutOfBoundsException(index);
+               }
+               return value[index];
+       }
+
+       native public int codePointAt(int index);
+       native public int codePointBefore(int index);
+       native public int codePointCount(int beginIndex, int endIndex);
+       native public int offsetByCodePoints(int index, int codePointOffset);
+       native public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin);
+       native void getChars(char dst[], int dstBegin);
+
+       @Deprecated
+       native public void getBytes(int srcBegin, int srcEnd, byte dst[], int dstBegin);
+       native public byte[] getBytes(String charsetName)
+                       throws UnsupportedEncodingException;
+
+       public byte[] getBytes(Charset x){
+               // No Charset model.
+               if (x == null){
+                       throw new NullPointerException();
+               }
+               return StringCoding.encode(x, value, 0, value.length);
+       }
+
+       native public byte[] getBytes();
+       @Override
+       native public boolean equals(Object anObject);
+       public boolean contentEquals(StringBuffer stringBuffer){
+               // No StringBuffer model.
+               synchronized (stringBuffer) {
+                       return contentEquals((CharSequence) stringBuffer);
+               }
+       }
+
+  native static boolean equals0 (char[] a, char[] b, int len);
+  
+  /**
+   * we can't turn this into a native method at top level since it would require a bunch
+   * of round trips
+   */
+       public boolean contentEquals (CharSequence charSequence){
+               if (value.length != charSequence.length()){
+                       return false;
+    }
+
+    // that should be the common case (String)
+               if (charSequence.equals(this)){
+                       return true;
+               }
+    
+    // we can do that natively, too
+               if (charSequence instanceof AbstractStringBuilder) {
+      return equals0( value,  ((AbstractStringBuilder) charSequence).getValue(), value.length);
+               }
+
+               // generic CharSequence - expensive
+    char v[] = value;
+    for (int n=value.length, i=0; n >= 0; n--,i++){
+      if (v[i] != charSequence.charAt(i)){
+        return false;
+      }
+    }
+               return true;
+       }
+
+       native public boolean equalsIgnoreCase(String anotherString);
+       @Override
+       native public int compareTo(String anotherString);
+
+       public static final Comparator<String> CASE_INSENSITIVE_ORDER = new CaseInsensitiveComparator();
+       private static class CaseInsensitiveComparator implements Comparator<String>, java.io.Serializable {
+               // use serialVersionUID from JDK 1.2.2 for interoperability
+               private static final long serialVersionUID = 8575799808933029326L;
+
+               @Override
+               public int compare(String s1, String s2) {
+                       return MJIcompare(s1,s2);
+               }
+       }
+
+       native private static int MJIcompare(String s1,String s2);
+       public int compareToIgnoreCase(String str) {
+               return CASE_INSENSITIVE_ORDER.compare(this, str);
+       }
+
+       native public boolean regionMatches(int toffset, String other, int ooffset, int len);
+       native public boolean regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len);
+       native public boolean startsWith(String prefix, int toffset);
+       public boolean startsWith(String prefix) {
+               return startsWith(prefix, 0);
+       }
+
+       public boolean endsWith(String suffix) {
+               return startsWith(suffix, value.length - suffix.value.length);
+       }
+
+       @Override
+       native public int hashCode();
+       public int indexOf(int ch) {
+               return indexOf(ch, 0);
+       }
+       native public int indexOf(int ch, int fromIndex);
+       native public int lastIndexOf(int ch);
+       native public int lastIndexOf(int ch, int fromIndex);
+       native public int indexOf(String str);
+       native public int indexOf(String str, int fromIndex);
+
+       public int lastIndexOf(String str) {
+               return lastIndexOf(str, value.length);
+       }
+       native public int lastIndexOf(String str, int fromIndex);
+       native public String substring(int beginIndex);
+       native public String substring(int beginIndex, int endIndex);
+       @Override
+       public CharSequence subSequence(int beginIndex, int endIndex) {
+               return this.substring(beginIndex, endIndex);
+       }
+       native public String concat(String str);
+       native public String replace(char oldChar, char newChar);
+       native public boolean matches(String regex);
+       public boolean contains(CharSequence charSequence) {
+               // No CharSequence model
+               return indexOf(charSequence.toString()) > -1;
+       }
+       native public String replaceFirst(String regex, String replacement);
+       native public String replaceAll(String regex, String replacement);
+       public String replace(CharSequence target, CharSequence other) {
+               // No CharSequence model
+               int PATTERN= 0x10;
+               Matcher pattern=Pattern.compile(target.toString(), PATTERN).matcher(this);
+               return pattern.replaceAll(Matcher.quoteReplacement(other.toString()));
+       }
+       native public String[] split(String regex, int limit);
+       native public String[] split(String regex);
+       native public String toLowerCase(Locale locale);
+       native public String toLowerCase();
+       native public String toUpperCase(Locale locale);
+       native public String toUpperCase();
+       native public String trim();
+       @Override
+       public String toString() {
+               return this;
+       }
+       native public char[] toCharArray();
+       native public static String format(String format, Object... args);
+       native public static String format(Locale l, String format, Object... args);
+       public static String valueOf(Object x){
+               // can't translate arbitrary object
+               return (x == null) ? "null" : x.toString();
+       }
+       public static String valueOf(char values[]) {
+               return new String(values);
+       }
+       public static String valueOf(char values[], int offset, int count) {
+               return new String(values, offset, count);
+       }
+       public static String copyValueOf(char values[], int offset, int count) {
+               return new String(values, offset, count);
+       }
+       public static String copyValueOf(char values[]) {
+               return new String(values);
+       }
+       public static String valueOf(boolean bool) {
+               return bool ? "true" : "false";
+       }
+       public static String valueOf(char character) {
+               char data[] = {character};
+               return new String(data);
+       }
+       native public static String valueOf(int i);
+       native public static String valueOf(long l);
+       native public static String valueOf(float f);
+       native public static String valueOf(double d);
+       public native String intern();
+
+  
+  /*
+   * methods to be compatible with Harmony/Android, which now has modified
+   * versions of the old (offset based) String
+   * 
+   * NOTE - if the changes get too large we have to create Android specific
+   * models and peers
+   */
+  
+  // used internally by Android's java.lang.AbstractStringBuffer
+  void _getChars(int start, int end, char[] buffer, int index) {
+    System.arraycopy(value, start, buffer, index, end - start);
+  }
+}
diff --git a/src/classes/java/lang/System.java b/src/classes/java/lang/System.java
new file mode 100644 (file)
index 0000000..e18ee64
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.nio.channels.Channel;
+import java.util.Map;
+import java.util.Properties;
+
+import sun.misc.JavaLangAccess;
+import sun.misc.SharedSecrets;
+import sun.nio.ch.Interruptible;
+import sun.reflect.ConstantPool;
+import sun.reflect.annotation.AnnotationType;
+
+
+public class System {
+
+  static Properties properties;
+
+  public static InputStream in; // new BufferedInputStream(...);  // <2do> not yet
+  public static PrintStream out;
+  public static PrintStream err;
+  
+  static {
+
+    out = createSystemOut();
+    err = createSystemErr();
+
+    properties = new Properties();
+
+    String[] kv = getKeyValuePairs();
+    for (int i=0; i<kv.length; i+=2){
+      String key = kv[i];
+      String val = kv[i+1];
+
+      if (key != null && val != null) {
+        properties.put(kv[i], kv[i+1]);
+      }
+    }
+
+    // this is the Java 6 sun.misc.SharedSecrets backdoor mechanism which I
+    // would have prefered not to learn about. It's a mess WRT Java 1.5 / 6 compatibility
+    // <2do> - most if this isn't supported yet
+    SharedSecrets.setJavaLangAccess( createJavaLangAccess());
+
+    // <2do> this is an approximation that isn't particularly safe since we don't
+    // initialize sun.misc.VM
+    //sun.misc.VM.booted();
+  }
+
+  static JavaLangAccess createJavaLangAccess () {
+    return new JavaLangAccess(){
+      @Override
+       public ConstantPool getConstantPool(Class<?> cls) {
+        throw new UnsupportedOperationException("JavaLangAccess.getConstantPool() not supported yet");
+        //return cls.getConstantPool();
+      }
+      @Override
+       public void setAnnotationType(Class<?> cls, AnnotationType type) {
+        throw new UnsupportedOperationException("JavaLangAccess.setAnnotationType() not supported yet");
+        //cls.setAnnotationType(type);
+      }
+      @Override
+       public AnnotationType getAnnotationType(Class<?> cls) {
+        throw new UnsupportedOperationException("JavaLangAccess.getAnnotationType() not supported yet");
+        //return cls.getAnnotationType();
+      }
+      @Override
+       public <E extends Enum<E>> E[] getEnumConstantsShared(Class<E> cls) {
+        return cls.getEnumConstantsShared();
+      }
+      @Override
+       public void blockedOn(Thread t, Interruptible b) {
+        throw new UnsupportedOperationException("JavaLangAccess.blockedOn() not supported yet");
+        //t.blockedOn(b);
+      }
+      @Override
+       public void registerShutdownHook(int slot, Runnable r) {
+        throw new UnsupportedOperationException("JavaLangAccess.registerShutdownHook() not supported yet");
+      }
+      @Override
+       public int getStackTraceDepth(Throwable t) {
+        return t.getStackTraceDepth();
+      }
+      @Override
+       public StackTraceElement getStackTraceElement(Throwable t, int i) {
+        StackTraceElement[] st = t.getStackTrace();
+        return st[i];
+      }
+    };
+  }
+
+  static private native String[] getKeyValuePairs();
+
+  static private native PrintStream createSystemOut();
+  static private native PrintStream createSystemErr();
+
+  //--- standard streams
+  public static void setIn (InputStream newIn) {
+    in = newIn;
+  }
+
+  public static void setOut (PrintStream newOut){
+    out = newOut;
+  }
+
+  public static void setErr (PrintStream newErr) {
+    err = newErr;
+  }
+
+  public static Channel inheritedChannel() {
+    throw new UnsupportedOperationException("inheritedChannel() not yet supported");
+  }
+
+  //--- misc
+  public static native void exit (int rc);
+  public static native void arraycopy (Object src, int srcPos,
+                                       Object dst, int dstPos, int len);
+  public static native void gc();
+  public static native void runFinalization();
+  public static native void runFinalizersOnExit(boolean cond);
+  static native Class<?> getCallerClass();
+  public static native int identityHashCode (Object o);
+
+
+  //--- time management
+  public static native long currentTimeMillis();
+  public static native long nanoTime();
+
+  //--- environment
+  public static native String getenv (String key);
+  public static Map<String,String> getenv() {
+    throw new UnsupportedOperationException("getenv() not yet supported");
+  }
+
+  //--- security manager
+  static SecurityManager securityManager;
+
+  public static void setSecurityManager (SecurityManager newManager) {
+    securityManager = newManager;
+  }
+
+  public static SecurityManager getSecurityManager() {
+    return securityManager;
+  }
+
+  //--- system properties
+
+  public static Properties getProperties() {
+    return properties;
+  }
+  public static void setProperties(Properties newProps){
+    properties = newProps;
+  }
+
+  public static String getProperty (String key) {
+    return properties.getProperty(key);
+  }
+
+  public static String getProperty (String key, String def){
+    String v = properties.getProperty(key);
+    if (v == null){
+      return def;
+    } else {
+      return v;
+    }
+  }
+
+  public static String setProperty (String key, String value){
+    String oldVal = properties.getProperty(key);
+    properties.put(key,value);
+    return oldVal;
+  }
+
+  public static String clearProperty (String key) {
+    String oldVal = properties.getProperty(key);
+    properties.remove(key);
+    return oldVal;
+  }
+
+  //--- native libs
+  public static void load (String pathName) {
+    // nothing, we don't have native libs
+    // (maybe we could on-demand load peers?)
+  }
+
+  public static void loadLibrary (String libName){
+    // nothing yet
+  }
+
+  public static String mapLibraryName (String libName){
+    // just a placeholder (Unix flavor)
+    return "lib" + libName + ".so";
+  }
+
+}
diff --git a/src/classes/java/lang/Thread.java b/src/classes/java/lang/Thread.java
new file mode 100644 (file)
index 0000000..8c538f5
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import gov.nasa.jpf.annotation.NeverBreak;
+import sun.nio.ch.Interruptible;
+
+/**
+ * MJI model class for java.lang.Thread library abstraction
+ * 
+ * <2do> this should not require the JPF ThreadList to retrieve corresponding ThreadInfos
+ * (the ThreadList might not store terminated threads)
+ */
+public class Thread implements Runnable {
+
+  public interface UncaughtExceptionHandler {
+    // note this doesn't stop the thread from being terminated
+    void uncaughtException (Thread t, Throwable x);
+  }
+  
+  static int nameThreadNum; // to construct the default thread name  
+
+  public static final int MIN_PRIORITY = 1;
+  public static final int NORM_PRIORITY = 5;
+  public static final int MAX_PRIORITY = 10;
+
+  // don't rename this - it's used by ThreadGoup.uncaughtException()
+  private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler; // null by default
+  
+  // initialized in init(), except of the main thread (which gets explicitly initialized by the VM)
+  ThreadGroup group;
+  Runnable target;
+  String name;
+  int priority;
+  boolean isDaemon;
+  
+  // this is an explicit thread state that gets set on a call of interrupt(), but
+  // only if the thread is not blocked. If it is, we only change the status.
+  // this gets cleared by calling interrupted()
+  boolean             interrupted;
+  
+  // those are only accessed from peers since thread obects are per se shared
+  @NeverBreak
+  ThreadLocal.Entry<?>[] threadLocals;
+  
+  // this is what we use for sun.misc.Unsafe.park()/unpark()
+  // this is accessed from the native peer, VM.createMainThread() and sun.misc.Unsafe
+  static class Permit {
+    boolean blockPark = true; // this is used to remember unpark() calls before park() (they don't accumulate)
+  }
+  Permit permit; // the object is used for wait/notify
+
+  // referenced by java.util.concurrent.locks.LockSupport via sun.misc.Unsafe
+  // DON'T CHANGE THIS NAME
+  volatile Object parkBlocker;
+
+  // used to store Thread.stop() exceptions
+  Throwable stopException;
+  
+  private volatile UncaughtExceptionHandler uncaughtExceptionHandler; // null by default
+
+  
+  public enum State { BLOCKED, NEW, RUNNABLE, TERMINATED, TIMED_WAITING, WAITING }
+
+  
+  public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler xh) {
+    defaultUncaughtExceptionHandler = xh;
+  }
+  
+  public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
+    return defaultUncaughtExceptionHandler;
+  }
+  
+  
+  public Thread () {
+    this(null, null, null, 0L);
+  }
+
+  public Thread (Runnable target) {
+    this(null, target, null, 0L);
+  }
+
+  public Thread (Runnable target, String name) {
+    this(null, target, name, 0L);
+  }
+
+  public Thread (String name) {
+    this(null, null, name, 0L);
+  }
+
+  public Thread (ThreadGroup group, String name) {
+    this(group, null, name, 0L);
+  }
+  
+  public Thread (ThreadGroup group, Runnable target) {
+    this(group, target, null, 0L);
+  }
+
+  public Thread (ThreadGroup group, Runnable target, String name) {
+    this(group, target, name, 0L);
+  }
+
+  public Thread (ThreadGroup group, Runnable target, String name, long stackSize) {
+    Thread cur = currentThread();
+
+    if (group == null) {
+      this.group = cur.getThreadGroup();
+    } else {
+      this.group = group;
+    }
+
+    this.group.add(this);
+
+    if (name == null) {
+      this.name = "Thread-" + ++nameThreadNum;
+    } else {
+      this.name = name;
+    }
+
+    this.permit = new Permit();
+
+    // those are always inherited from the current thread
+    this.priority = cur.getPriority();
+    this.isDaemon = cur.isDaemon();
+
+    this.target = target;
+
+    // do our associated native init
+    init0(this.group, target, this.name, stackSize);
+    
+    initThreadLocals(cur);
+  }
+
+  // this takes care of ThreadInfo initialization
+  native void init0 (ThreadGroup group, Runnable target, String name, long stackSize);
+  
+  // this is here since InheritableThreadLocals would require childValue(parentVal) roundtrips.
+  // Unfortunately we can't defer this until the ThreadLocal is actually accessed since
+  // we have to capture the value at the point of child creation
+  // Note this executes in the parent thread
+  private void initThreadLocals (Thread parent){
+    ThreadLocal.Entry<?>[] tl = parent.threadLocals;
+    if (tl != null){
+      int len = tl.length;
+      ThreadLocal.Entry<?>[] inherited = null;
+      int j=0;
+      
+      for (int i=0; i<len; i++){
+        ThreadLocal.Entry<?> e = tl[i];
+        ThreadLocal.Entry<?> ec = e.getChildEntry();
+        if (ec != null){
+          if (inherited == null){
+            inherited = new ThreadLocal.Entry<?>[len];
+          }
+          inherited[j++] = ec;
+        }
+      }
+      
+      if (inherited != null){
+        ThreadLocal.Entry<?>[] a = new ThreadLocal.Entry<?>[j];
+        System.arraycopy(inherited,0,a,0,j);
+        threadLocals = a;
+      }
+    }
+  }
+  
+  public static int activeCount () {
+    return 0;
+  }
+
+  public void setUncaughtExceptionHandler(UncaughtExceptionHandler xh) {
+    uncaughtExceptionHandler = xh;
+  }
+  
+  public UncaughtExceptionHandler getUncaughtExceptionHandler(){
+    if (uncaughtExceptionHandler != null){
+      return uncaughtExceptionHandler;
+    } else {
+      return group;
+    }
+  }
+  
+  public void setContextClassLoader (ClassLoader cl) {
+  }
+
+  public ClassLoader getContextClassLoader () {
+    // <NSY>
+    return null;
+  }
+
+  public synchronized void setDaemon (boolean isDaemon) {
+    this.isDaemon = isDaemon;
+    setDaemon0(isDaemon);
+  }
+
+  public boolean isDaemon () {
+    return isDaemon;
+  }
+
+  public native long getId();
+
+  public StackTraceElement[] getStackTrace() {
+    return null; // not yet implemented
+  }
+
+  public native int getState0();
+
+  public Thread.State getState() {
+    int i = getState0();
+    switch (i) {
+    case 0: return State.BLOCKED;
+    case 1: return State.NEW;
+    case 2: return State.RUNNABLE;
+    case 3: return State.TERMINATED;
+    case 4: return State.TIMED_WAITING;
+    case 5: return State.WAITING;
+    }
+
+    return null; // shoudl be intercepted by a getState0 assertion
+  }
+
+  public synchronized void setName (String name) {
+    if (name == null) {
+      throw new IllegalArgumentException("thread name can't be null");
+    }
+
+    this.name = name;
+    setName0(name);
+  }
+
+  public String getName () {
+    return name;
+  }
+
+  public void setPriority (int priority) {
+    if ((priority < MIN_PRIORITY) || (priority > MAX_PRIORITY)) {
+      throw new IllegalArgumentException("thread priority out of range");
+    }
+
+    this.priority = priority;
+    setPriority0(priority);
+  }
+
+  public int getPriority () {
+    return priority;
+  }
+
+  public ThreadGroup getThreadGroup () {
+    return group;
+  }
+
+  public void checkAccess () {
+    // <NSY>
+  }
+
+  public native int countStackFrames ();
+
+  public static native Thread currentThread ();
+
+  public void destroy () {
+  }
+
+  public static void dumpStack () {
+  }
+
+  public static int enumerate (Thread[] tarray) {
+    Thread cur = currentThread();
+
+    return cur.group.enumerate(tarray);
+  }
+
+  public static native boolean holdsLock (Object obj);
+
+  // this one needs to be native because it might change the thread status
+  public native void interrupt ();
+
+  // those don't have to be native, but we keep it symmetric
+  public static native boolean interrupted ();
+  public native boolean isInterrupted ();
+
+  public native boolean isAlive ();
+
+
+  /**
+   * note these are not synchronized anymore since they are intercepted by the
+   * native peer. The reason is that we don't want two CGs per join call (one for the
+   * sync call, and one for the wait) because this can cause serious
+   * performance degradation
+   */
+  public void join () throws InterruptedException {
+    synchronized(this){
+
+      if (interrupted()) {
+        throw new InterruptedException();
+      }
+
+      while (isAlive()) {
+        // apparently, the JDK doesn't throw InterruptedExceptions if
+        // we get interrupted after waiting in the join
+        wait();
+      }
+    }
+  }
+
+  public void join (long millis) throws InterruptedException {
+    join(millis, 0);
+  }
+
+  public void join (long millis, int nanos) throws InterruptedException {
+
+    if (millis < 0){
+      throw new java.lang.IllegalArgumentException("timeout value is negative");
+
+    } else if (millis == 0){
+      join();
+
+    } else {
+      synchronized(this){
+        if (interrupted()){
+          throw new InterruptedException();
+        }
+
+        wait(millis);
+      }
+    }
+  }
+
+    
+
+  @Override
+  public void run () {
+    if (target != null) {
+      target.run();
+    }
+  }
+
+  public static void sleep (long millis) throws InterruptedException {
+    sleep(millis, 0);
+  }
+
+  public static native void sleep (long millis, int nanos)
+                            throws InterruptedException;
+
+  public native void start();
+  public native void stop();
+  public native void stop(Throwable obj);
+
+  public native void suspend();
+  public native void resume();
+
+
+  @Override
+  public String toString () {
+    return ("Thread[" + name + ',' + priority + ',' + (group == null ? "" : group.getName()) + ']');
+  }
+
+  public static native void yield ();
+
+  native void setDaemon0 (boolean on);
+
+  native void setName0 (String name);
+
+  native void setPriority0 (int priority);
+
+
+
+  /**
+   * automatically called by system upon thread termination to clean up
+   * references.
+   * 
+   * NOTE - we clean up atomically during ThreadInfo.finish(), to avoid any
+   * additional states. This is important since group per se is a shared object
+   * We only include this method here as a specification for ThreadInfo
+   */
+  private void exit () {
+    if (group != null){
+      group.threadTerminated(this);
+      group = null;
+    }
+    
+    threadLocals = null;    
+    parkBlocker = null;
+    uncaughtExceptionHandler = null;
+  }
+
+  // some Java 6 mojo
+  // <2do> not implemented yet
+  native void blockedOn (Interruptible b);
+
+  
+  // we probably will remove these fields once we modeled java.util.concurrent.ThreadLocalRandom 
+  // to make it deterministic
+  long threadLocalRandomSeed;
+  int threadLocalRandomProbe;
+  int threadLocalRandomSecondarySeed;
+}
diff --git a/src/classes/java/lang/ThreadGroup.java b/src/classes/java/lang/ThreadGroup.java
new file mode 100644 (file)
index 0000000..a4c8393
--- /dev/null
@@ -0,0 +1,487 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.PrintStream;
+
+/**
+ * our model of java.lang.ThreadGroup, which we need since the VM relies on certain fields
+ * during bootstrapping
+ * 
+ * Note that we don't have a native peer, which would help with atomic container updates, because
+ * most of these methods are called by the VM and we don't want to introduce a dependency from
+ * VM to the peers. This is esp. true since we would have to dig out & cast the peer, call it,
+ * and then go backwards extracting the *Info objects we already had in the calling context.
+ * 
+ * If we ever want to introduce a peer, it could just delegate to the respective ThreadInfo methods.
+ */
+public class ThreadGroup implements Thread.UncaughtExceptionHandler {
+  private final ThreadGroup parent;
+  
+  //--- those are package visible, so we better keep names and access modifiers
+  
+  String name;
+  int maxPriority;
+  int nthreads;
+  Thread[] threads;
+  
+  int ngroups;
+  ThreadGroup[] groups;
+  
+  int nUnstartedThreads;
+  
+  boolean destroyed;
+  boolean daemon;
+  
+  boolean vmAllowSuspension;
+  
+  public ThreadGroup (String name){
+    this( Thread.currentThread().getThreadGroup(), name);
+  }
+  
+  public ThreadGroup (ThreadGroup parent, String name){
+    this.parent = parent;
+    this.name = name;
+    
+    // those are inherited from the parent, which therefore can't be null
+    this.maxPriority = parent.maxPriority;
+    this.daemon = parent.daemon;
+    
+    parent.add(this);
+  }
+  
+  //--- the container updates
+  
+  // group updates are just class internal, whereas thread updates happen from java.lang.Thread and hence need
+  // package visibility
+  
+  private synchronized void add (ThreadGroup childGroup){
+    if (destroyed){
+      throw new IllegalThreadStateException();
+    }
+    
+    if (groups == null){
+      groups = new ThreadGroup[1];
+    } else {
+      ThreadGroup[] ng = new ThreadGroup[ngroups+1];
+      System.arraycopy(groups, 0, ng, 0, ngroups);
+      groups = ng;
+    }
+    
+    groups[ngroups++] = childGroup;
+  }
+  
+  private synchronized void remove (ThreadGroup childGroup){
+    if (!destroyed){
+      for (int i=0; i<ngroups; i++){
+        if (groups[i] == childGroup){
+          if (ngroups > 1){
+            int ngroups1 = ngroups - 1;
+            ThreadGroup[] ng = new ThreadGroup[ngroups1];
+            if (i > 0) {
+              System.arraycopy(groups, 0, ng, 0, i);
+            }
+            if (i < ngroups1) {
+              System.arraycopy(groups, i + 1, ng, i, ngroups - i);
+            }
+            groups = ng;
+            ngroups = ngroups1;
+            
+          } else {
+            groups = null;
+            ngroups = 0;
+          }
+          
+          if (nthreads == 0){
+            notifyAll();
+          }
+          
+          // check if its time for a suicide in case we are a daemon group
+          if (daemon){
+            if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)){
+              destroy();
+            }
+          }
+          
+          break;
+        }
+      }
+    }
+  }
+  
+  
+  // thread add/remove happens from the native peer, to avoid additional CGs during creation/termination
+  // since the ThreadGroup is per se a shared object. We just include these methods here as a specification
+  // for ThreadInfo
+  
+  /**
+   * called from the Thread ctor, i.e. before it is started
+   * NOTE - this is implemented as a native method to avoid shared field CGs
+   */
+  synchronized void addUnstarted (){
+    if (destroyed){
+      throw new IllegalThreadStateException();
+    }
+
+    nUnstartedThreads++;
+  }
+  
+  /**
+   * this is called during Thread.start()
+   * NOTE - this is implemented as a native method to avoid shared field CGs
+   */
+  synchronized void add (Thread newThread){
+    if (destroyed){
+      throw new IllegalThreadStateException();
+    }
+
+    if (threads == null){
+      threads = new Thread[1];
+    } else {
+      Thread[] nt = new Thread[nthreads+1];
+      System.arraycopy(threads, 0, nt, 0, nthreads);
+      threads = nt;
+    }
+    
+    threads[nthreads++] = newThread;
+    nUnstartedThreads--;
+  }
+  
+  /**
+   * this is called automatically upon thread termination 
+   */
+  synchronized void threadTerminated (Thread terminatedThread){
+    if (!destroyed){
+      for (int i=0; i<nthreads; i++){
+        if (threads[i] == terminatedThread){
+          if (nthreads == 1){
+            threads = null;
+            nthreads = 0;
+            
+          } else {
+            int nthreads1 = nthreads - 1;
+            Thread[] a = new Thread[nthreads1];
+            if (i > 0) {
+              System.arraycopy(threads, 0, a, 0, i);
+            }
+            if (i < nthreads1) {
+              System.arraycopy(groups, i + 1, a, i, ngroups - i);
+            }
+            threads = a;
+            nthreads = nthreads1;
+          }
+
+          // OpenJDK does this no matter if the group was destroyed, but that looks like a bug
+          // that could lead to multiple destroys and notifyAll() signals
+          if (nthreads == 0) {
+            notifyAll();
+          }
+          if (daemon) {
+            if ((nthreads == 0) && (nUnstartedThreads == 0) && (ngroups == 0)) {
+              destroy();
+            }
+          }
+          
+          break;
+        }
+      }
+    }
+  }
+  
+  //--- public getters and setters
+  public final String getName(){
+    return name;
+  }
+  
+  public final ThreadGroup getParent(){
+    return parent;
+  }
+  
+  public final int getMaxPriority(){
+    return maxPriority;
+  }
+  
+  public final boolean isDaemon(){
+    return daemon;
+  }
+  
+  public synchronized boolean isDestroyed(){
+    return destroyed;
+  }
+  
+  public final void setDaemon (boolean daemon){
+    this.daemon = daemon;
+  }
+  
+  /**
+   * this sets the maxPriority to the min of the argument and the parents maxPriority
+   * and raises maxPriority for all our threads that have a lower one
+   */
+  public final synchronized void setMaxPriority (int newMaxPriority){
+    // do nothing if newMaxPriority is outside limits
+    if (newMaxPriority >= Thread.MIN_PRIORITY && newMaxPriority <= Thread.MAX_PRIORITY){
+      maxPriority = (parent != null) ? Math.min(parent.maxPriority, newMaxPriority) : newMaxPriority;
+      
+      for (int i=0; i<groups.length; i++){
+        groups[i].setMaxPriority(newMaxPriority);
+      }
+    }
+  }
+  
+  public final boolean parentOf (ThreadGroup tg){
+    while (true){
+      if (tg == this) return true;
+      ThreadGroup tgParent = tg.parent;
+      if (tgParent == null) return false;
+      tg = tgParent;
+    }
+  }
+  
+  public final void checkAccess(){
+    // not sure what to do here
+  }
+  
+  /**
+   * return number of threads including all sub-groups
+   */
+  public synchronized int activeCount() {
+    if (destroyed){
+      return 0;
+      
+    } else {
+      int nActive = nthreads;
+      if (ngroups > 0){
+        for (int i=0; i<ngroups; i++){
+          nActive += groups[i].activeCount();
+        }
+      }
+      
+      return nActive;
+    }
+  }
+  
+  /**
+   * copy threads into provided list
+   * kind of a misnomer
+   */
+  public int enumerate (Thread[] dst){
+    return enumerate(dst, 0, true);
+  }
+  
+  public int enumerate (Thread[] dst, boolean recurse){
+    return enumerate(dst, 0, recurse);
+  }
+  
+  private synchronized int enumerate (Thread[] dst, int idx, boolean recurse){
+    int n = 0;
+
+    int len = nthreads;
+    if ((idx + len) > dst.length){
+      len = dst.length - idx;
+    }
+    
+    for (int i = 0; i < len; i++) {
+      if (threads[i].isAlive()) {
+        dst[idx++] = threads[i];
+        n++;
+      }
+    }
+
+    if (recurse && (idx < dst.length)) {
+      for (int j = 0; j < ngroups; j++) {
+        n += groups[j].enumerate(dst, idx, true);
+      }
+    }
+    
+    return n;    
+  }
+  
+  public synchronized int activeGroupCount() {
+    if (destroyed){
+      return 0;
+      
+    } else {
+      int nActive = ngroups;
+      if (ngroups > 0){
+        for (int i=0; i<ngroups; i++){
+          nActive += groups[i].activeGroupCount();
+        }
+      }
+      
+      return nActive;
+    }
+  }
+  
+  public int enumerate (ThreadGroup[] dst){
+    return enumerate(dst, 0, true);
+  }
+  
+  public int enumerate (ThreadGroup[] dst, boolean recurse){
+    return enumerate(dst, 0, recurse);
+  }
+  private synchronized int enumerate (ThreadGroup[] dst, int idx, boolean recurse){
+    int n = 0;
+
+    int len = ngroups;
+    if ((idx + len) > dst.length){
+      len = dst.length - idx;
+    }
+    
+    for (int i = 0; i < len; i++) {
+        dst[idx++] = groups[i];
+        n++;
+    }
+
+    if (recurse && (idx < dst.length)) {
+      for (int j = 0; j < ngroups; j++) {
+        n += groups[j].enumerate(dst, idx, true);
+      }
+    }
+    
+    return n;
+  }
+
+  static final int OP_STOP = 1;
+  static final int OP_INTERRUPT = 2;
+  static final int OP_SUSPEND = 3;
+  static final int OP_RESUME = 4;
+  
+  public final void stop(){
+    if (doRecursively(OP_STOP)){
+      Thread.currentThread().stop();
+    }
+  }
+  public final void interrupt(){
+    doRecursively(OP_INTERRUPT);
+  }
+  public final void suspend(){
+    if (doRecursively(OP_SUSPEND)){
+      Thread.currentThread().suspend();      
+    }
+  }
+  public final void resume(){
+    doRecursively(OP_RESUME);
+  }
+  
+  private synchronized boolean doRecursively (int op){
+    boolean suicide = false;
+    
+    for (int i=0; i<nthreads; i++){
+      Thread t = threads[i];
+      switch (op){
+        case OP_STOP:
+          if (t == Thread.currentThread()) {
+            suicide = true; // defer
+          } else {
+            t.stop();
+          }
+          break;
+        case OP_INTERRUPT: t.interrupt(); break;
+        case OP_SUSPEND:
+          if (t == Thread.currentThread()) {
+            suicide = true; // defer
+          } else {
+            t.suspend();
+          }
+          break;
+        case OP_RESUME: t.resume(); break;
+      }
+    }
+    
+    for (int j=0; j<ngroups; j++){
+      suicide = suicide || groups[j].doRecursively(op);
+    }
+    
+    return suicide;
+  }
+  
+  
+  public synchronized final void destroy(){
+    if (destroyed || (nthreads > 0)) {
+      throw new IllegalThreadStateException();
+    }
+
+    for (int i=0; i<ngroups; i++){
+      groups[i].destroy();
+    }
+    
+    if (parent != null){ // no destroying the system group
+      nthreads = 0;
+      threads = null;
+      
+      ngroups = 0;
+      groups = null;
+      
+      destroyed = true;
+      
+      parent.remove(this);
+    }
+  }
+
+  //--- just for debugging
+  public void list(){
+    list( System.out, 0);
+  }
+  synchronized void list (PrintStream ps, int indent){
+    for (int i=0; i<indent; i++) ps.print(' ');
+    ps.println(toString());
+    
+    indent+= 4;
+    for (int j=0; j<nthreads; j++){
+      for (int i=0; i<indent; i++) ps.print(' ');
+      ps.println( threads[j]);
+    }
+    
+    for (int k=0; k<ngroups; k++){
+      groups[k].list( ps, indent);
+    }
+  }
+  
+  public boolean allowThreadSuspension (boolean allowSuspension){
+    vmAllowSuspension = allowSuspension;
+    return true;
+  }
+  
+  @Override
+  public String toString () {
+    return getClass().getName() + "[name=" + name + ",maxpri=" + maxPriority + ']';
+  }
+
+  // we handle this in ThreadInfo, but again this is a public method that could be called from anywhere
+  @Override
+  public void uncaughtException (Thread t, Throwable x) {
+    if (parent != null) { // if we have a parent, delegate
+      parent.uncaughtException(t, x);
+      
+    } else { // check if there is a default handler
+      Thread.UncaughtExceptionHandler xh = Thread.getDefaultUncaughtExceptionHandler();
+      if (xh != null) {
+        xh.uncaughtException(t, x);
+        
+      } else { // last resort - just print to Ssytem.err
+        if (!(x instanceof ThreadDeath)) {
+          System.err.print("Exception in thread \"" + t.getName() + '"');
+          x.printStackTrace(System.err);
+        }
+      }
+    }
+  }
+}
diff --git a/src/classes/java/lang/ThreadLocal.java b/src/classes/java/lang/ThreadLocal.java
new file mode 100644 (file)
index 0000000..43ea225
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import gov.nasa.jpf.annotation.NeverBreak;
+import java.lang.ref.WeakReference;
+import java.util.Objects;
+import java.util.function.Supplier;
+
+/**
+ * model of java.lang.ThreadLocal, which avoids global shared objects
+ * that can otherwise considerably contribute to the state space
+ */
+public class ThreadLocal<T> {
+
+  static class Entry<E> extends WeakReference<ThreadLocal<E>> {
+    @NeverBreak
+    E val;
+    
+    Entry (ThreadLocal<E> key, E val){
+      super(key);
+      this.val = val;
+    }
+    
+    Entry<E> getChildEntry (){
+      ThreadLocal<E> loc = get();
+      if (loc instanceof InheritableThreadLocal){
+        return new Entry<E>( loc, ((InheritableThreadLocal<E>)loc).childValue(val));
+      } else {
+        return null;
+      }
+    }
+  }
+  
+  public ThreadLocal() {
+  }
+  
+  /**
+   * override to provide initial value 
+   */
+  protected T initialValue() {
+    return null;
+  }
+    
+  private native Entry<T> getEntry();
+  private native void addEntry (Entry<T> e);
+  private native void removeEntry (Entry<T> e);
+  
+  public T get() {
+    Entry<T> e = getEntry();
+    
+    if (e == null){
+      T v = initialValue();
+      e = new Entry<T>(this, v);
+      addEntry(e);
+    }
+    
+    return e.val;
+  }
+  
+  public void set (T v){
+    Entry<T> e = getEntry();
+    
+    if (e != null){
+      e.val = v;
+      
+    } else {
+      e = new Entry<T>(this, v);
+      addEntry(e);      
+    }
+  }
+  
+  public void remove(){
+    Entry<T> e = getEntry();
+    if (e != null){
+      removeEntry(e);
+    }
+  }
+
+  
+  // Java 8 provides this as an internal type to be used from lib classes
+  // ?? why is this not done with overridden initialValue() within the concrete ThreadLocal class
+  static final class SuppliedThreadLocal<E> extends ThreadLocal<E> {
+
+    // we need to preserve the modifiers since this might introduce races (supplier could be shared)
+    private final Supplier<? extends E> sup;
+
+    SuppliedThreadLocal(Supplier<? extends E> supplier) {
+      sup = Objects.requireNonNull(supplier);
+    }
+
+    @Override
+    protected E initialValue() {
+      return sup.get();
+    }
+  }
+}
diff --git a/src/classes/java/lang/Throwable.java b/src/classes/java/lang/Throwable.java
new file mode 100644 (file)
index 0000000..ff19d97
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
+/**
+ * barebones model class for java.lang.Throwable
+ * 
+ * the main reason for having one is to defer the stacktrace creation, which
+ * we don't want if all an application does is intercepting a gazillion of
+ * exceptions like NoSuchMethodException
+ */
+public class Throwable {
+
+  int[] snapshot; // this is an internal array of { globalMethodId, pcOffset } pairs
+  
+  protected Throwable cause; // in case this is a wrapper exception (like InvocationTargetException)
+  
+  protected String detailMessage; // where we keep the context specific info
+    
+  protected StackTraceElement[] stackTrace; // only set on demand, if getStackTrace() is called
+  
+  public Throwable() {
+    try {                                            // Use Class.forName() instead of new StackTraceElement() since the latter creates garbage.
+      Class.forName("java.lang.StackTraceElement");  // Force this class to load here instead of in createStackTrace().
+    } catch (ClassNotFoundException e) {
+      throw new NoClassDefFoundError("java.lang.StackTraceElement");
+    }
+     
+    fillInStackTrace();
+  }
+
+  public Throwable (String msg) {
+    this();
+    detailMessage = msg;
+    cause = this;
+  }
+
+  public Throwable (String msg, Throwable xCause) {
+    this();
+    detailMessage = msg;
+    cause = xCause;
+  }
+
+  public Throwable (Throwable xCause) {
+    this();
+    
+    cause = xCause;
+    if (cause != null){
+      detailMessage = xCause.toString();
+    }
+  }
+
+  public String getMessage() {
+    return detailMessage;
+  }
+  
+  public String getLocalizedMessage() {
+    return detailMessage;
+  }
+
+  public Throwable getCause() {
+    if (cause == this){
+      return null;  // quirky
+    } else {
+      return cause;
+    }
+  }
+
+  // this sets 'snapshot' from the native side
+  public native Throwable fillInStackTrace();
+  
+  // and this turns 'snapshot' into an array of StackTraceElement objects
+  private native StackTraceElement[] createStackTrace();
+  
+  public StackTraceElement[] getStackTrace() {
+    if (stackTrace == null){
+      stackTrace = createStackTrace();
+    }
+    
+    return stackTrace;
+  }
+  
+  public void setStackTrace (StackTraceElement[] st) {
+    stackTrace = st;
+  }
+
+  public synchronized Throwable initCause (Throwable xCause) {
+    if (xCause == this){
+      throw new IllegalArgumentException("self-causation not permitted");
+    }
+    
+    if (cause != this){
+      throw new IllegalStateException("cannot overwrite cause");
+    }
+    
+    cause = xCause;
+    
+    return this;
+  }
+
+  @Override
+  public native String toString();
+  
+  public native void printStackTrace ();
+  
+  // <2do> and lots more of printStackTraces..
+
+  private native String getStackTraceAsString();
+  
+  public void printStackTrace (PrintStream ps){
+    String s = getStackTraceAsString();
+    ps.print(s);
+  }
+  
+  public void printStackTrace (PrintWriter pw){
+    String s = getStackTraceAsString();
+    pw.print(s);    
+  }
+  
+  int getStackTraceDepth(){
+    return (snapshot.length / 2); // snapshot stores the methodId and pc for each StackFrame 
+  }
+}
diff --git a/src/classes/java/lang/annotation/Inherited.java b/src/classes/java/lang/annotation/Inherited.java
new file mode 100644 (file)
index 0000000..45b97c4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.annotation;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Inherited {
+}
diff --git a/src/classes/java/lang/annotation/Retention.java b/src/classes/java/lang/annotation/Retention.java
new file mode 100644 (file)
index 0000000..4077cd3
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.annotation;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Retention {
+  RetentionPolicy value();
+}
diff --git a/src/classes/java/lang/ref/Reference.java b/src/classes/java/lang/ref/Reference.java
new file mode 100644 (file)
index 0000000..f69f33d
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.ref;
+
+/**
+ * MJI model class for java.lang.ref.Reference library abstraction
+ * we model this so that we can rely on our WeakRefence implementation
+ */
+public abstract class Reference<T> {
+  
+  /**
+   * the object we reference
+   * NOTE: this has to be the *first* field, or we break WeakReference handling in
+   * the garbage collection!!
+   */
+  T ref;
+
+  /** the optional queue for us */
+  ReferenceQueue<? super T> queue;
+
+  /** link to enqueue w/o additional memory requirements */
+  Reference<T> next;
+
+  Reference (T r) {
+    ref = r;
+  }
+  
+  Reference (T r, ReferenceQueue<? super T> q) {
+    ref = r;
+    queue = q;
+  }
+  
+  /** is the referenced object enqueued */
+  public boolean isEnqueued () {
+    // <2do>
+    return false;
+  }
+
+  /** clear, but do not enqueue the referenced object */
+  public void clear () {
+    ref = null;
+  }
+
+  /** add the referenced object to its queue */
+  public void enqueue () {
+  }
+
+  /** return the referenced object */
+  public T get () {
+    return ref;
+  }
+}
diff --git a/src/classes/java/lang/ref/ReferenceQueue.java b/src/classes/java/lang/ref/ReferenceQueue.java
new file mode 100644 (file)
index 0000000..6085cba
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.ref;
+
+/**
+ * MJI model class for java.lang.ref.ReferenceQueue library abstraction
+ */
+public class ReferenceQueue<T> {
+  Reference<? extends T> head;
+
+  public ReferenceQueue () {
+  }
+
+  public Reference<? extends T> poll () {
+    // <2do>
+    return null;
+  }
+
+  public Reference<? extends T> remove () {
+    // <2do>
+    return null;
+  }
+
+  public Reference<? extends T> remove (long timeout) {
+    // <2do>
+    return null;
+  }
+}
diff --git a/src/classes/java/lang/ref/WeakReference.java b/src/classes/java/lang/ref/WeakReference.java
new file mode 100644 (file)
index 0000000..8b8cd51
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.ref;
+
+/**
+ * MJI model class for java.lang.ref.WeakReference library abstraction
+ */
+public class WeakReference<T> extends Reference<T> {
+  public WeakReference (T ref) {
+    super( ref);
+  }
+
+  public WeakReference (T ref, ReferenceQueue<? super T> queue) {
+    super( ref, queue);
+  }
+}
diff --git a/src/classes/java/lang/reflect/AccessibleObject.java b/src/classes/java/lang/reflect/AccessibleObject.java
new file mode 100644 (file)
index 0000000..44e7044
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+
+public class AccessibleObject {
+  boolean isAccessible = true;
+
+  public void setAccessible (boolean flag)  throws SecurityException {
+    isAccessible = flag;
+  }
+  
+  public static void setAccessible (AccessibleObject[] accessibles, boolean flag) throws SecurityException {
+    for (int i=0; i<accessibles.length; i++) {
+      accessibles[i].isAccessible = flag;
+    }
+  }
+  
+  public boolean isAccessible() {
+    return isAccessible;
+  }
+    
+  public native <T extends Annotation> T getAnnotation (Class<T> cls); // <2do> Implement in JPF_java_lang_reflect_Constructor
+
+  public boolean isAnnotationPresent (Class<? extends Annotation> cls) {
+    return getAnnotation(cls) != null;
+  }
+
+  public native Annotation[] getAnnotations(); // <2do> Implement in JPF_java_lang_reflect_Constructor
+
+  public native Annotation[] getDeclaredAnnotations(); // <2do> Implement in JPF_java_lang_reflect_Method, JPF_java_lang_reflect_Class and JPF_java_lang_reflect_Constructor
+}
diff --git a/src/classes/java/lang/reflect/Constructor.java b/src/classes/java/lang/reflect/Constructor.java
new file mode 100644 (file)
index 0000000..0a663c0
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * (incomplete) support for consructor reflection
+ * 
+ * pretty stupid - this is almost identical to Method, but we can't derive,
+ * and the delegation happens at the peer level anyways.
+ * 
+ * NOTE: 'regIdx' and 'name' need to be like Method, or the peer delegation
+ * fails (this is the hack'ish part)
+ * 
+ * NOTE: we ditch the 'final' modifier so that we can provide our
+ * own serialization ctor objects - that's probably going away
+ * once we replace ObjectStreamClass
+ */
+public /*final*/ class Constructor <T> extends AccessibleObject implements Member {
+  
+  protected int regIdx;
+  protected String name;
+
+  @Override
+  public native String getName();
+  public native T newInstance (Object... args)
+        throws IllegalAccessException, InvocationTargetException, InstantiationException;
+  
+  @Override
+  public native int getModifiers();
+  public native Class<?> getReturnType();
+  public native Class<?>[] getParameterTypes();
+  
+  @Override
+  public native Class<T> getDeclaringClass();
+  
+  @Override
+  public native Annotation[] getAnnotations();
+  @Override
+  public native Annotation[] getDeclaredAnnotations();
+  @Override
+  public native <T extends Annotation> T getAnnotation( Class<T> annotationCls);
+  public native Annotation[][] getParameterAnnotations();
+  
+  @Override
+  public boolean isSynthetic () {
+    return false;
+  }
+  
+  @Override
+  public native String toString();
+  
+  @Override
+  public native boolean equals (Object obj);
+
+  public boolean isVarArgs (){
+    return (getModifiers() & Modifier.VARARGS) != 0;
+  }
+
+  @Override
+  public native int hashCode ();
+
+  public native String toGenericString ();
+}
diff --git a/src/classes/java/lang/reflect/Field.java b/src/classes/java/lang/reflect/Field.java
new file mode 100644 (file)
index 0000000..f53dfb9
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+
+public final class Field extends AccessibleObject implements Member {
+  int regIdx; // the link to the corresponding FieldInfo
+  String name; // deferred set by the NativePeer getName()
+
+  public String toGenericString() {
+         // TODO: return real generic string
+         return toString();
+  }
+  
+  public native boolean getBoolean (Object o) throws IllegalAccessException;
+  public native void setBoolean (Object o, boolean v) throws IllegalAccessException;
+
+  public native byte getByte (Object o) throws IllegalAccessException;
+  public native void setByte (Object o, byte v) throws IllegalAccessException;
+
+  public native short getShort (Object o) throws IllegalAccessException;
+  public native void setShort (Object o, short v) throws IllegalAccessException;
+  
+  public native char getChar (Object o) throws IllegalAccessException;
+  public native void setChar (Object o, char v) throws IllegalAccessException;
+
+  public native int getInt (Object o) throws IllegalAccessException;
+  public native void setInt (Object o, int val) throws IllegalAccessException;
+
+  public native long getLong (Object o) throws IllegalAccessException;
+  public native void setLong (Object o, long v) throws IllegalAccessException;
+
+  public native float getFloat (Object o) throws IllegalAccessException;
+  public native void setFloat (Object o, float v) throws IllegalAccessException;
+
+  public native double getDouble (Object o) throws IllegalAccessException;
+  public native void setDouble (Object o, double v) throws IllegalAccessException;
+
+  public native Class<?> getType ();
+  
+  public native Object get (Object o) throws IllegalAccessException;
+    
+  public native void set (Object o, Object v) throws IllegalArgumentException, IllegalAccessException;
+  // the member interface
+  @Override
+  public native String getName();
+  
+  @Override
+  public native int getModifiers();
+  
+  @Override
+  public native Annotation[] getAnnotations();
+
+  @Override
+  public native <T extends Annotation> T getAnnotation( Class<T> annotationCls);
+  
+  @Override
+  public native Class<?> getDeclaringClass ();
+  
+  @Override
+  public native boolean isSynthetic ();
+
+  @Override
+  public native boolean equals (Object obj);
+
+  @Override
+  public native String toString ();
+
+  public boolean isEnumConstant (){
+    return (getModifiers() & Modifier.ENUM) != 0;
+  }
+
+  @Override
+  public native int hashCode ();
+
+  @Override
+  public native Annotation[] getDeclaredAnnotations ();
+}
diff --git a/src/classes/java/lang/reflect/InvocationTargetException.java b/src/classes/java/lang/reflect/InvocationTargetException.java
new file mode 100644 (file)
index 0000000..83ec322
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.reflect;
+
+/**
+ * not really required to model, but the real thing does some funky
+ * things to override the cause, just making things a bit more complicated
+ * on our VM side (we still init Throwables explicitly from ThreadInfo)
+ */
+public class InvocationTargetException extends Exception {
+
+  public InvocationTargetException (Throwable cause){
+    super(cause);
+  }
+  
+  public Throwable getTargetException() {
+    return cause;
+  }
+}
diff --git a/src/classes/java/lang/reflect/Method.java b/src/classes/java/lang/reflect/Method.java
new file mode 100644 (file)
index 0000000..e782d44
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.lang.reflect;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * minimal Method reflection support.
+ * Note that we share peer code between Method and Constructor (which aren't
+ * really different on the JPF side), so don't change field names!
+ */
+public final class Method extends AccessibleObject implements Member {
+  int regIdx; // the link to the corresponding MethodInfo
+  String name; // deferred set by the NativePeer getName()
+
+  @Override
+  public native String getName();
+  public String toGenericString() {
+         // TODO: return real generic string
+         return toString();
+  }
+  public native Object invoke (Object object, Object... args)
+        throws IllegalAccessException, InvocationTargetException;
+
+  @Override
+  public native int getModifiers();
+  public native Class<?> getReturnType();
+  public native Class<?>[] getParameterTypes();
+  public native Type[] getGenericParameterTypes();
+  public native Class<?>[] getExceptionTypes();
+
+  @Override
+  public native Class<?> getDeclaringClass();
+
+  @Override
+  public native Annotation[] getAnnotations();
+  @Override
+  public native Annotation[] getDeclaredAnnotations();
+  @Override
+  public native <T extends Annotation> T getAnnotation( Class<T> annotationCls);
+  public native Annotation[][] getParameterAnnotations();
+
+  @Override
+  public boolean isSynthetic (){
+    return Modifier.isSynthetic(getModifiers());
+  }
+
+  @Override
+  public native String toString();
+
+  // for Annotations - return the default value of the annotation member
+  // represented by this method
+  public Object getDefaultValue() {
+    throw new UnsupportedOperationException("Method.getDefaultValue() not supported yet");
+  }
+
+  @Override
+  public native boolean equals (Object obj);
+
+  public boolean isVarArgs (){
+    return (getModifiers() & Modifier.VARARGS) != 0;
+  }
+
+  @Override
+  public native int hashCode ();
+
+  public boolean isBridge (){
+    return (getModifiers() & Modifier.BRIDGE) != 0;
+  }
+}
diff --git a/src/classes/java/net/URLClassLoader.java b/src/classes/java/net/URLClassLoader.java
new file mode 100644 (file)
index 0000000..107d79d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.net;
+
+import java.io.IOException;
+import java.security.CodeSource;
+import java.security.PermissionCollection;
+import java.security.SecureClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Vector;
+import java.util.jar.Manifest;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ *  Model class for java.net.URLClassLoader
+ */
+public class URLClassLoader extends SecureClassLoader {
+
+  private ArrayList<URL> urls = new ArrayList<URL>(0);
+
+  public URLClassLoader(URL[] urls) {
+    super();
+    addURLs(urls);
+  }
+
+  public URLClassLoader(URL[] urls, ClassLoader parent) {
+    super(parent);
+    addURLs(urls);
+  }
+
+  private void addURLs(URL[] urls) {
+    for(URL url: urls) {
+      addURL(url);
+    }
+  }
+
+  private native void addURL0(String url);
+
+  protected void addURL(URL url) {
+    addURL0(url.toString());
+    urls.add(url);
+  }
+
+  @Override
+  protected native Class<?> findClass(final String name) throws ClassNotFoundException;
+
+  private native String findResource0 (String rname);
+
+  @Override
+  public URL findResource(final String rname) {
+    URL url = null;
+    String path = findResource0(rname);
+
+    try {
+      url = new URL(path);
+    } catch (MalformedURLException x){
+      url = null;
+    }
+
+    return url;
+  }
+
+  private native String[] findResources0 (String rname);
+
+  @Override
+  public Enumeration<URL> findResources(String name) throws IOException {
+    String[] urls = findResources0(name);
+    Vector<URL> list = new Vector<URL>(0);
+    for(String url: urls) {
+      try {
+        list.add(new URL(url));
+      } catch (MalformedURLException x){
+        // process the rest
+      }
+    }
+
+    return list.elements();
+  }
+
+  public URL[] getURLs() {
+    return urls.toArray(new URL[urls.size()]);
+  }
+
+  public static URLClassLoader newInstance(URL[] urls) {
+    return (new URLClassLoader(urls));
+  }
+
+  public static URLClassLoader newInstance(URL[] urls, ClassLoader parent) {
+    return (new URLClassLoader(urls, parent));
+  }
+
+  //--- unsupported methods
+
+  public URLClassLoader(URL[] urls, ClassLoader parent, URLStreamHandlerFactory factory) {
+    throw new UnsupportedOperationException();
+  }
+
+  public void close() throws IOException {
+    throw new UnsupportedOperationException();
+  }
+
+  protected Package definePackage(String name, Manifest man, URL url) 
+      throws IllegalArgumentException {
+    throw new UnsupportedOperationException();
+  }
+
+  protected PermissionCollection getPermissions(CodeSource codesource) {
+    throw new UnsupportedOperationException();
+  }
+}
diff --git a/src/classes/java/nio/Buffer.java b/src/classes/java/nio/Buffer.java
new file mode 100644 (file)
index 0000000..0dadad7
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.nio;
+
+public abstract class Buffer {
+       protected int position;
+       protected int capacity;
+       protected int limit;
+
+       public final int capacity() {
+               return capacity;
+       }
+
+       public final Buffer position(int newPosition) {
+               if ((newPosition<0)||(newPosition>limit)) {
+                       throw new IllegalArgumentException("Illegal buffer position exception: "+newPosition);
+               }
+               this.position = newPosition;
+               return this;
+       }
+
+       public final int position() {
+               return position;
+       }
+
+       public final int limit() {
+               return this.limit;
+       }
+
+       public final Buffer limit(int newLimit) {
+               if ((newLimit<0)||(newLimit>capacity)) {
+                       throw new IllegalArgumentException("Illegal buffer limit exception: "+newLimit);
+               }
+               this.limit = newLimit;
+               return this;
+       }
+
+       public final Buffer clear(){
+               position = 0;
+               limit = capacity;
+               return this;
+       }
+
+       public final Buffer flip() {
+               limit = position;
+               position = 0;
+               return this;
+       }
+
+       public final Buffer rewind() {
+               position = 0;
+               return this;
+       }
+
+       public final int remaining() {
+               return limit-position;
+       }
+
+       public final boolean hasRemaining() {
+               return remaining()>0;
+       }
+
+       public abstract boolean hasArray();
+
+       public abstract Object array();
+}
diff --git a/src/classes/java/nio/BufferUnderflowException.java b/src/classes/java/nio/BufferUnderflowException.java
new file mode 100644 (file)
index 0000000..cd48d0f
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.nio;
+
+public class BufferUnderflowException extends RuntimeException {
+       public BufferUnderflowException() { }
+}
diff --git a/src/classes/java/nio/ByteBuffer.java b/src/classes/java/nio/ByteBuffer.java
new file mode 100644 (file)
index 0000000..e0c99a7
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.nio;
+
+public class ByteBuffer extends Buffer {
+       byte[] array;
+
+       public static ByteBuffer allocate(int i) {
+               if (i < 0) {
+                       throw new IllegalArgumentException();
+               }
+               ByteBuffer newBuffer = new ByteBuffer(i);
+               return newBuffer;
+       }
+
+       public static ByteBuffer allocateDirect(int capacity) {
+               return allocate(capacity);
+       }
+
+       protected ByteBuffer(int i) {
+               capacity = i;
+               this.clear();
+               array = new byte[i];
+       }
+
+       public ByteBuffer duplicate() {
+               ByteBuffer copy = new ByteBuffer(capacity);
+               copy.array = array;
+               return copy;
+       }
+
+       public ByteBuffer asReadOnlyBuffer() {
+               return duplicate();
+       }
+
+       public ByteBuffer slice() {
+               int remaining = limit - position;
+               ByteBuffer copy = new ByteBuffer(remaining);
+               copy.array = array;
+               return copy;
+       }
+
+       public ByteBuffer put(byte b) {
+               if (position >= limit) {
+                       throw new BufferOverflowException();
+               }
+               array[position] = b;
+               position++;
+               return this;
+       }
+
+       public ByteBuffer put(int i, byte b) {
+               if ((i < 0) || (i >= limit)) {
+                       throw new IndexOutOfBoundsException();
+               }
+               array[i] = b;
+               return this;
+       }
+
+       public ByteBuffer put(ByteBuffer src) {
+               if (src == this) {
+                       throw new IllegalArgumentException();
+               }
+
+               int srcRemaining = src.remaining();
+               if (srcRemaining > remaining()) {
+                       throw new BufferOverflowException();
+               }
+
+               System.arraycopy(src.array, src.position(), array, position, srcRemaining);
+
+               src.position(src.position() + srcRemaining);
+               position(position + srcRemaining);
+
+               return this;
+       }
+
+       public ByteBuffer put(byte[] bytes, int offset, int length) {
+               if ((offset | length | (offset + length) | (bytes.length - (offset + length))) < 0) {
+                       throw new IndexOutOfBoundsException();
+               }
+
+               if (length > remaining()) {
+                       throw new BufferOverflowException();
+               }
+
+               System.arraycopy(bytes, offset, array, position, length);
+               position(position + length);
+
+               return this;
+       }
+
+       public ByteBuffer put(byte[] bytes) {
+               return put(bytes, 0, bytes.length);
+       }
+
+       public byte get() {
+               if (position >= limit) {
+                       throw new BufferUnderflowException();
+               }
+               position++;
+               return array[position-1];
+       }
+
+       public byte get(int i) {
+               if ((i < 0) || (i >= limit)) {
+                       throw new IndexOutOfBoundsException();
+               }
+               return array[i];
+       }
+
+       public ByteBuffer get(byte[] bytes) {
+               return get(bytes, 0, bytes.length);
+       }
+
+       public ByteBuffer get(byte[] bytes, int offset, int length) {
+               if ((offset | length | (offset + length) | (bytes.length - (offset + length))) < 0) {
+                       throw new IndexOutOfBoundsException();
+               }
+
+               if (length > remaining()) {
+                       throw new BufferUnderflowException();
+               }
+
+               int end = offset + length;
+               for (int i = offset; i < end; i++) {
+                       bytes[i] = get();
+               }
+               return this;
+       }
+
+       public ByteBuffer order(ByteOrder order) {
+               return this;
+       }
+
+       /***************************************************************
+        * public char getChar()
+        * @return 16 bit (UTF-16) char of ByteBuffer at this.position 
+        * Caution: 8 or 32 bit character encodings are not supported.
+        */
+       public char getChar() {
+        char res=getChar(this.position);
+        this.position+=2;
+        return res;
+       }
+
+       /***************************************************************
+        * public char getChar(int pos)
+        * @return 16 bit (UTF-16) char of ByteBuffer at int pos 
+        * Caution: 8 or 32 bit character encodings are not supported.
+        */
+       public char getChar(int pos) {
+               if (limit - pos < 2) {
+                       throw new BufferUnderflowException();
+               }
+               int x1 = (array[pos]   & 0xff) << 8;
+               int x0 = (array[pos+1] & 0xff);
+
+               return (char) (x1 | x0);
+       }
+
+       /***************************************************************
+        * public ByteBuffer putChar(char c)
+        * @return insert 16 bit (UTF-16) char c at this.position  
+        * Caution: 8 or 32 bit character encodings are not supported.
+        */
+       public ByteBuffer putChar(char c) {
+               if (limit - position < 2) {
+                       throw new BufferOverflowException();
+               }
+               array[position]   = (byte)(c >> 8);
+               array[position+1] = (byte)(c     );
+               position += 2;
+
+               return this;
+       }
+
+       public int getInt() {
+               if (limit - position < 4) {
+                       throw new BufferUnderflowException();
+               }
+
+               int x3 = (array[position  ]       ) << 24;
+               int x2 = (array[position+1] & 0xff) << 16;
+               int x1 = (array[position+2] & 0xff) <<  8;
+               int x0 = (array[position+3] & 0xff);
+               position += 4;
+
+               return (x3 | x2 | x1 | x0);
+       }
+
+       public ByteBuffer putInt(int x) {
+               if (limit - position < 4) {
+                       throw new BufferOverflowException();
+               }
+
+               array[position  ] = (byte)(x >> 24);
+               array[position+1] = (byte)(x >> 16);
+               array[position+2] = (byte)(x >>  8);
+               array[position+3] = (byte)(x      );
+               position += 4;
+
+               return this;
+       }
+
+       public long getLong() {
+               if (limit - position < 8) {
+                       throw new BufferUnderflowException();
+               }
+
+               long x7 = ((long)(array[position  ]       ) << 56);
+               long x6 = ((long)(array[position+1] & 0xff) << 48);
+               long x5 = ((long)(array[position+2] & 0xff) << 40);
+               long x4 = ((long)(array[position+3] & 0xff) << 32);
+               long x3 = ((long)(array[position+4] & 0xff) << 24);
+               long x2 = ((long)(array[position+5] & 0xff) << 16);
+               long x1 = ((long)(array[position+6] & 0xff) <<  8);
+               long x0 = (array[position+7] & 0xff      );
+               position += 8;
+
+               return (x7 | x6 | x5 | x4 | x3 | x2 | x1 | x0);
+       }
+
+       public ByteBuffer putLong(long x) {
+               if (limit - position < 8) {
+                       throw new BufferOverflowException();
+               }
+
+               array[position  ] = (byte)((x >> 56)       );
+               array[position+1] = (byte)((x >> 48) & 0xff);
+               array[position+2] = (byte)((x >> 40) & 0xff);
+               array[position+3] = (byte)((x >> 32) & 0xff);
+               array[position+4] = (byte)((x >> 24) & 0xff);
+               array[position+5] = (byte)((x >> 16) & 0xff);
+               array[position+6] = (byte)((x >>  8) & 0xff);
+               array[position+7] = (byte)((x      ) & 0xff);
+               position += 8;
+
+               return this;
+       }
+
+       @Override
+       public byte[] array() {
+               return array;
+       }
+
+       @Override
+       public boolean hasArray() {
+               return true;
+       }
+
+       public ByteBuffer compact() {
+               int pos = position();
+               int lim = limit();
+               int cap = capacity();
+               int rem = lim - pos;
+
+               byte[] newArray = new byte[cap];
+               System.arraycopy(array, pos, newArray, 0, rem);
+               array = newArray;
+
+               position(rem);
+               limit(cap);
+               return this;
+       }
+
+       public static ByteBuffer wrap(byte[] outMess) {
+               ByteBuffer byteBuffer = new ByteBuffer(outMess.length);
+               byteBuffer.clear();
+               System.arraycopy(outMess, 0 , byteBuffer.array, 0, outMess.length);
+               return byteBuffer;
+       }
+}
diff --git a/src/classes/java/nio/ByteOrder.java b/src/classes/java/nio/ByteOrder.java
new file mode 100644 (file)
index 0000000..bb0be5d
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.nio;
+
+public final class ByteOrder {
+       private String name;
+
+       private ByteOrder(String name) {
+               this.name = name;
+       }
+
+       public static final ByteOrder BIG_ENDIAN    = new ByteOrder("BIG_ENDIAN");
+       public static final ByteOrder LITTLE_ENDIAN = new ByteOrder("LITTLE_ENDIAN");
+
+       @Override
+       public String toString() {
+               return name;
+       }
+
+       public static ByteOrder nativeOrder() { return null; } // stub for Eclipse
+}
diff --git a/src/classes/java/nio/channels/FileChannel.java b/src/classes/java/nio/channels/FileChannel.java
new file mode 100644 (file)
index 0000000..1003393
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.nio.channels;
+
+import java.io.IOException;
+import java.io.FileDescriptor;
+
+import java.nio.ByteBuffer;
+
+//This class uses the methods from FileDescriptor in order to access files
+public class FileChannel {
+
+       public int read(ByteBuffer dst) throws IOException{
+               return fd.read(dst.array(),0,dst.array().length);
+       }
+
+       public int write(ByteBuffer src) throws IOException{
+               fd.write(src.array(),0,src.array().length);
+               return src.array().length;
+       }
+
+       public void close() throws IOException{
+               fd.close();
+       }
+
+       public FileChannel(FileDescriptor fd){
+               this.fd = fd;
+       }
+
+       private FileDescriptor fd = null;
+
+       public long position() { return 0; } // Stub for Eclipse
+
+       public FileChannel position(long p) { return null; } // Stub for Eclipse
+}
diff --git a/src/classes/java/nio/package-info.java b/src/classes/java/nio/package-info.java
new file mode 100644 (file)
index 0000000..81d8ca1
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * This package contains Byte* classes and exceptions.
+ *
+ * All of them are implemented straightforwardly but
+ * only few methods are tested.
+ */
+package java.nio;
diff --git a/src/classes/java/security/AccessController.java b/src/classes/java/security/AccessController.java
new file mode 100644 (file)
index 0000000..2f9a2c9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.security;
+
+
+/*
+ * MJI model class for java.security.AccessController library abstraction
+ */
+public class AccessController {
+  public static void checkPermission (Permission p) throws AccessControlException {
+  }
+  
+  public static <T> T doPrivileged (PrivilegedAction<T> action,
+                                     AccessControlContext context) {
+    return action.run();
+  }
+  
+  public static <T> T doPrivileged (PrivilegedAction<T> action)  {
+    return action.run();
+  }
+  
+  public static <T> T doPrivileged (PrivilegedExceptionAction<T> action,
+                                     AccessControlContext context)
+    throws PrivilegedActionException {
+
+    try {
+      return action.run();
+      
+    } catch (RuntimeException rx){
+      throw rx; // we have to let unchecked exceptions pass
+    } catch (Exception x) {
+      throw new PrivilegedActionException(x);
+    }
+  }
+  
+  public static <T> T doPrivileged (PrivilegedExceptionAction<T> action)
+    throws PrivilegedActionException {
+
+    try {
+      return action.run();
+
+    } catch (RuntimeException rx){
+      throw rx; // we have to let unchecked exceptions pass
+    } catch (Exception x) {
+      throw new PrivilegedActionException(x);
+    }
+  }
+  
+  public static AccessControlContext getContext() {
+    return null;
+  }
+  
+  @SuppressWarnings("unused")
+  private static AccessControlContext getStackAccessControlContext() {
+    return null;
+  }
+
+  static AccessControlContext getInheritedAccessControlContext() {
+    return null;
+  }
+
+}
+
diff --git a/src/classes/java/security/MessageDigest.java b/src/classes/java/security/MessageDigest.java
new file mode 100644 (file)
index 0000000..4b7a249
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.security;
+
+/**
+ * forwarding implementation of MessageDigest.
+ * <2do> only partially implemented (as required by serialization)
+ */
+public class MessageDigest extends MessageDigestSpi {
+
+  String algorithm;
+  int id; // set by native peer
+  
+  private native int init0 (String algorithm);
+  
+  protected MessageDigest (String algorithm) throws NoSuchAlgorithmException {
+    if (!algorithm.equalsIgnoreCase("SHA") && !algorithm.equalsIgnoreCase("MD5")){
+      throw new NoSuchAlgorithmException("unknown algorithm: " + algorithm);
+    }
+    
+    this.algorithm = algorithm;
+    id = init0(algorithm);
+  }
+  
+  public static MessageDigest getInstance (String algorithm) throws NoSuchAlgorithmException {
+    return new MessageDigest(algorithm); // keep it simple
+  }
+  
+  public native byte[] digest (byte[] input);
+  
+  public native byte[] digest ();
+
+  public native void update(byte[] input);
+
+  @Override
+  protected native void finalize(); // to clean up
+  
+  // those are required by the compiler, but never used since we forward
+  // all public methods
+  @Override
+  protected native byte[] engineDigest ();
+
+  @Override
+  protected native void engineReset ();
+
+  @Override
+  protected native void engineUpdate (byte input);
+
+  @Override
+  protected native void engineUpdate (byte[] input, int offset, int len);
+
+}
diff --git a/src/classes/java/security/SecureClassLoader.java b/src/classes/java/security/SecureClassLoader.java
new file mode 100644 (file)
index 0000000..3abae10
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.security;
+
+/**
+ * This is just a dummy implementation of java.security.SecureClassLoader
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class SecureClassLoader extends ClassLoader {
+
+  protected SecureClassLoader(ClassLoader parent) {
+    super(parent);
+  }
+
+  protected SecureClassLoader() {
+    super();
+  }
+}
diff --git a/src/classes/java/text/DecimalFormat.java b/src/classes/java/text/DecimalFormat.java
new file mode 100644 (file)
index 0000000..2e783d0
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.text;
+
+/**
+ * <2do> this class is a Q&D version to get some support for NumberFormats -
+ * something we aren't too interested in yet. The model side has to be extended
+ * to include all the delegation, but the real work should happen on the
+ * peer side by forwarding to the real classes
+ */
+
+public class DecimalFormat extends NumberFormat implements Cloneable{
+  private static final long serialVersionUID = 1L;
+
+  /*
+   * NOTE: if we would directly intercept the ctors, we would have to
+   * explicitly call the superclass ctors from the native peer
+   * (the 'id' handle gets initialized in the java.text.Format ctor) 
+   */
+  private native void init0();
+  private native void init0(String pattern);
+  private native void init0(int style);
+  
+  public DecimalFormat () {
+    init0();
+  }
+  
+  public DecimalFormat (String pattern) {
+    init0(pattern);
+  }
+  
+  public DecimalFormat(String pattern, DecimalFormatSymbols symbols) {
+    // <2do> that's incomplete - has to delegate to the DecimalFormatSymbols
+    // object, but should do so here in the model not the peer
+    this(pattern);
+  }
+  
+  DecimalFormat (int style) {
+    init0(style);
+  }
+  // intercepted by native peer
+  
+  @Override
+  public StringBuffer format (Object obj, StringBuffer toAppendTo, FieldPosition pos) {
+    // works for use case where pos = new FieldPosition(0)
+    toAppendTo.append(obj.toString());
+    return toAppendTo;
+  }
+
+  @Override
+  public Number parse(String source, ParsePosition pos){
+    // interceptted by native peer
+    return null;
+  }
+
+  @Override
+  public void setMaximumFractionDigits (int newValue){
+    // intercepted by native peer
+  }
+  @Override
+  public void setMaximumIntegerDigits (int newValue){
+    // intercepted by native peer
+  }
+  @Override
+  public void setMinimumFractionDigits(int newValue){
+    // intercepted by native peer
+  }
+  @Override
+  public void setMinimumIntegerDigits(int newValue){
+    // intercepted by native peer
+  }
+  
+  @Override
+  public String format (long number) {
+    // intercepted by native peer
+    return null;
+  }
+  
+  @Override
+  public String format (double number) {
+    // intercepted by native peer
+    return null;
+  }
+
+  public DecimalFormatSymbols getDecimalFormatSymbols() {
+      return new DecimalFormatSymbols();
+  }
+
+  public String getPositivePrefix() {
+      return "";
+  }
+
+  public String getNegativePrefix() {
+      return "-";
+  }
+
+  public String getPositiveSuffix() {
+      return "";
+  }
+
+  public String getNegativeSuffix() {
+      return "";
+  }
+
+  @Override
+  public boolean isGroupingUsed() {
+      return false;
+  }
+
+  @Override
+  public void setGroupingUsed(boolean newValue) {
+    // intercepted by native peer
+  }
+
+  @Override
+  public boolean isParseIntegerOnly() {
+      return false;
+  }
+
+  @Override
+  public void setParseIntegerOnly(boolean value) {
+      // intercepted by native peer
+  }
+  // and probably a lot missing
+}
diff --git a/src/classes/java/text/Format.java b/src/classes/java/text/Format.java
new file mode 100644 (file)
index 0000000..09ebc8c
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.text;
+
+/**
+ * (incomplete) model class for java.text.Format
+ * the reason we model this is that we want to cut off all the inner
+ * workings by just delegating to real formatters stored in our
+ * native peer
+ */
+public abstract class Format {
+  
+  // <?> how does that work with backtracking? my initial guess is that
+  // we can safely overwrite an index because after backtracking, the
+  // formatter will never be used. It's therefore sufficient if we keep the
+  // nInstances counter in the JPF space
+  // (just a reminder - we can't use the reference value because it might
+  // change -- the ElementInfo invariance sucks!
+  static int nInstances;
+  private int id = nInstances++; // just for peer implementation purposes 
+  
+  public String format (Object o) {
+    StringBuffer sb = new StringBuffer();
+    FieldPosition pos = new FieldPosition(0);
+    return format(o, sb, pos).toString();
+  }
+  
+  public abstract StringBuffer format (Object o, StringBuffer sb, FieldPosition pos);
+
+  public abstract Object parseObject (String source, ParsePosition pos);
+
+  public Object parseObject(String source) throws ParseException {
+    ParsePosition pos = new ParsePosition(0);
+    Object result = parseObject(source, pos);
+    if (pos.index == 0) {
+      throw new ParseException("Format.parseObject(String) failed",
+        pos.errorIndex);
+    }
+    return result;
+  }
+}
diff --git a/src/classes/java/text/NumberFormat.java b/src/classes/java/text/NumberFormat.java
new file mode 100644 (file)
index 0000000..1848723
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.text;
+
+public abstract class NumberFormat extends Format {
+
+  static final int INTEGER_STYLE=0;
+  static final int NUMBER_STYLE=1;
+    
+  public static NumberFormat getIntegerInstance() {
+    return new DecimalFormat(INTEGER_STYLE);
+  }
+  
+  public static NumberFormat getNumberInstance() {
+    return new DecimalFormat(NUMBER_STYLE);
+  }
+
+  public static NumberFormat getInstance() {
+    return new DecimalFormat(NUMBER_STYLE);
+  }
+  
+  public void setMaximumFractionDigits (int newValue){
+    // intercepted by native peer
+  }
+  public void setMaximumIntegerDigits (int newValue){
+    // intercepted by native peer
+  }
+  public void setMinimumFractionDigits(int newValue){
+    // intercepted by native peer
+  }
+  public void setMinimumIntegerDigits(int newValue){
+    // intercepted by native peer
+  }
+  
+  public String format (long number) {
+    // intercepted by native peer
+    return null;
+  }
+  
+  public String format (double d) {
+    // intercepted by native peer
+    return null;
+  }
+
+  @Override
+  public final Object parseObject (String source, ParsePosition pos) {
+    return parse(source,pos);
+  }
+
+  public void setParseIntegerOnly(boolean value) {
+      // intercepted by native peer
+  }
+
+  public boolean isParseIntegerOnly() {
+      // intercepted by native peer
+      return false;
+  }
+
+  public boolean isGroupingUsed() {
+      return false;
+  }
+
+  public void setGroupingUsed(boolean newValue) {
+    // intercepted by native peer
+  }
+
+  public abstract Number parse(String source,ParsePosition pos);
+
+  // ..and probably a lot missing
+  
+}
diff --git a/src/classes/java/text/SimpleDateFormat.java b/src/classes/java/text/SimpleDateFormat.java
new file mode 100644 (file)
index 0000000..9598e36
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.text;
+
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * (incomplete) model class for java.text.SimpleDate. See Format for details
+ * about the native formatter delegation
+ */
+public class SimpleDateFormat extends DateFormat {
+
+  // see DecimalFormat comments why we use explicit init0()'s
+
+  private native void init0();
+  private native void init0(String pattern);
+  private native void init0(int timeStyle, int dateStyle);
+
+  public SimpleDateFormat () {
+    init0();
+    initializeCalendar();
+  }
+
+  public SimpleDateFormat (String pattern) {
+    if(pattern == null) {
+      throw new NullPointerException();
+    }
+    init0(pattern);
+    initializeCalendar();
+  }
+
+  public SimpleDateFormat (String pattern, Locale locale) {
+    // <2do> bluntly ignoring locale for now
+    this(pattern);
+  }
+
+  SimpleDateFormat (int timeStyle, int dateStyle, Locale locale){
+    init0(timeStyle, dateStyle);
+    initializeCalendar();
+  }
+
+  // unfortunately we can't override the DateFormat.format(String) because
+  // it is final, and hence the compiler can do a INVOKE_SPECIAL
+  native String format0 (long dateTime);
+
+  @Override
+  public StringBuffer format (Date date, StringBuffer sb, FieldPosition pos) {
+    String s = format0(date.getTime());
+    sb.append(s);
+
+    // we don't do FieldPositions yet
+
+    return sb;
+  }
+
+
+  @Override
+  public Date parse (String arg0, ParsePosition arg1) {
+    // TODO Auto-generated method stub
+    return null;
+  }
+
+  private void initializeCalendar() {
+    if (calendar == null) {
+        calendar = Calendar.getInstance(TimeZone.getDefault(), Locale.getDefault());
+    }
+  }
+
+  native public void applyPattern(String pattern);
+  
+}
diff --git a/src/classes/java/util/Random.java b/src/classes/java/util/Random.java
new file mode 100644 (file)
index 0000000..ca52926
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util;
+
+public class Random {
+
+  // the state of this object
+  private long seed;
+  
+  public Random(){
+    // intercepted by native peer to control seed initialization based on JPF configuration
+  }
+  
+  public Random(long seed) {
+    // intercepted by native peer to control seed initialization based on JPF configuration
+  }
+  
+  public synchronized void setSeed(long seed){
+    // intercepted by native peer
+  }
+  
+  protected int next(int bits){
+    return 42; // intercepted by peer
+  }
+  
+  public void nextBytes(byte[] data){
+    // intercepted by peer
+  }
+  
+  public int nextInt(){
+    return 42; // intercepted by peer    
+  }
+  
+  public int nextInt(int n) {
+    return 42; // intercepted by peer
+  }
+  
+  public long nextLong() {
+    return 42; // intercepted by peer
+  }
+  
+  public boolean nextBoolean() {
+    return true; // intercepted by peer
+  }
+  
+  public float nextFloat() {
+    return 42f; // intercepted by peer
+  }
+  
+  public double nextDouble() {
+    return 42.0; // intercepted by peer
+  }
+  
+  public synchronized double nextGaussian() {
+    return 42.0; // intercepted by peer    
+  }
+}
diff --git a/src/classes/java/util/TimeZone.java b/src/classes/java/util/TimeZone.java
new file mode 100644 (file)
index 0000000..890a78c
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util;
+
+import java.io.Serializable;
+import java.util.Date;
+
+/**
+ * a concrete TimeZone that forwards to the host VM. This is required to avoid Java version compatibility
+ * problems
+ * Note that we drop the abstract modifier
+ */
+public class TimeZone implements Serializable, Cloneable {
+
+  private static final long serialVersionUID = 1L;
+
+  private int rawOffset; 
+  private String ID;
+  
+  private static TimeZone defaultZone;
+  
+  // public styles
+  public static final int SHORT = 0;
+  public static final int LONG = 1;
+  
+  // we keep construction on the peer side
+  private static native TimeZone createDefaultZone();
+  
+  // both are always cloned
+  public static native TimeZone getTimeZone (String ID);
+  public static TimeZone getDefault() {
+    return (TimeZone) (getDefaultRef().clone());
+  }
+
+  // called internally (e.g. by java.util.Date) - no clone here
+  static TimeZone getDefaultRef(){
+    if (defaultZone == null){
+      defaultZone = createDefaultZone();
+    }
+    
+    return defaultZone;
+  }
+  
+  // clone handles CloneNotSupportedException
+  @Override
+  public Object clone() {
+    try {
+      return super.clone();
+    } catch (CloneNotSupportedException e) {
+      throw new InternalError();
+    }
+  }
+  
+  public static void setDefault (TimeZone tz) {
+    if (tz == null){ // that's a reset according to the API docs
+      defaultZone = createDefaultZone();
+    } else {
+      defaultZone = tz;
+    }
+    
+    setDefaultValues(defaultZone); // remember on the native side
+  }
+  private static native void setDefaultValues (TimeZone tz); 
+  
+  public static native String[] getAvailableIDs();
+  public static native String[] getAvailableIDs(int rawOffset);
+  
+  // the public TimeZone() constructor of the original class can only be called from the
+  // concrete derived classes we want to skip anyways
+  
+  public TimeZone (String ID){
+    setID(ID);
+  }
+  // this will set ID and rawOffset
+  public native void setID (String ID);
+  
+  public String getID (){
+    return ID;
+  }
+  
+  public native int getOffset (int era, int year, int month, int day, int dayOfWeek, int milliseconds);
+
+  public native int getOffset (long date);
+  
+  // this is not public in Java 1.7
+  native int getOffsets (long date, int[] offsets);
+  
+  public int getRawOffset (){
+    return rawOffset;
+  }
+  public void setRawOffset (int offsetMillis){
+    rawOffset = offsetMillis;
+  }
+  
+  public boolean inDaylightTime (Date date) {
+    return inDaylightTime(date.getTime());
+  }
+  private native boolean inDaylightTime (long time); 
+
+
+  public native boolean useDaylightTime();
+
+  public native boolean observesDaylightTime();
+  
+  public native int getDSTSavings();
+  
+  public String getDisplayName(){
+    // <2do> should use Locale.Category.DISPLAY in Java 1.7
+    return getDisplayName( false, LONG, Locale.getDefault());
+  }
+  public String getDisplayName (Locale locale) {
+    return getDisplayName( false, LONG, locale);    
+  }
+  public String getDisplayName (boolean daylight, int style){
+    return getDisplayName( daylight, style, Locale.getDefault());    
+  }
+  public native String getDisplayName (boolean daylight, int style, Locale locale);
+}
diff --git a/src/classes/java/util/concurrent/BrokenBarrierException.java b/src/classes/java/util/concurrent/BrokenBarrierException.java
new file mode 100644 (file)
index 0000000..49ab036
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent;
+
+@SuppressWarnings("serial")
+public class BrokenBarrierException extends Exception {
+     
+     public BrokenBarrierException () {
+     }
+     
+     public BrokenBarrierException (String details) {
+          super(details);
+     }
+}
diff --git a/src/classes/java/util/concurrent/CyclicBarrier.java b/src/classes/java/util/concurrent/CyclicBarrier.java
new file mode 100644 (file)
index 0000000..1995db0
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent;
+
+/**
+ * a simplistic CyclicBarrier implementation, required because the real one
+ * relies heavily on Sun infrastructure (including native methods)
+ */
+public class CyclicBarrier {
+
+  private Runnable action;
+  private int parties;
+  private int count;
+  private boolean isBroken;
+
+  // make sure nobody from the outside can interfere with our locking
+  private final Object lock = new Object();
+
+  
+  public CyclicBarrier (int parties) {
+    this(parties, null);
+  }
+
+  public CyclicBarrier (int parties, Runnable action) {
+    this.parties = parties;
+    count = parties;
+    
+    this.action = action;
+  }
+  
+  public int await () throws InterruptedException, BrokenBarrierException {
+    synchronized (lock) {
+      int arrival = parties - count;
+
+      if (--count == 0) {
+        if (action != null) {
+          action.run();
+        }
+        count = parties; // reset barrier
+        lock.notifyAll();
+      } else {
+        try {
+          lock.wait();
+          if (isBroken) {
+            throw new BrokenBarrierException();
+          }
+        } catch (InterruptedException ix) {
+          if (count > 0) {
+            isBroken = true;
+            lock.notifyAll();
+          }
+
+          throw ix;
+        }
+      }
+
+      return arrival;
+    }
+  }
+
+  public int getParties () {
+    return parties;
+  }
+
+  public void reset () {
+    synchronized (lock) {
+      if ((count != parties) && (count != 0)) {
+        // there are waiters
+        isBroken = true;
+        lock.notifyAll();
+      } else {
+        count = parties;
+        isBroken = false;
+      }
+    }
+  }
+
+  public boolean isBroken () {
+    // true if one of the parties got out of an await by being
+    // interrupted
+    return isBroken;
+  }
+
+  public int getNumberWaiting () {
+    synchronized (lock) {
+      return (parties - count);
+    }
+  }
+}
diff --git a/src/classes/java/util/concurrent/Exchanger.java b/src/classes/java/util/concurrent/Exchanger.java
new file mode 100644 (file)
index 0000000..03801d2
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent;
+
+/**
+ * model class for java.util.concurrent.Exchanger
+ * We model because the original class goes to great lengths implementing
+ * memory based synchronization, using execution time and spins
+ * 
+ * Exchangers are also per se shared objects, so we want to minimize field
+ * access from bytecode
+ */
+public class Exchanger<V> {
+  
+  // created on native side and pinned down until transaction is complete
+  static class Exchange<T> {
+    Thread waiterThread;
+    boolean waiterTimedOut;
+    
+    T waiterData;
+    T responderData;
+  }
+  
+  //-- only accessed from native methods
+  private Exchange<V> exchange;
+  
+  
+  public native V exchange(V value) throws InterruptedException;
+
+  private native V exchange0 (V value, long timeoutMillis) throws InterruptedException, TimeoutException;
+  
+  // unfortunately we can't directly go native here without duplicating the TimeUnit conversion in the peer
+  public V exchange(V value, long timeout, TimeUnit unit) throws InterruptedException, TimeoutException {
+    long to = unit.convert(timeout,TimeUnit.MILLISECONDS);
+    return exchange0( value, to);
+  }
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicIntegerArray.java b/src/classes/java/util/concurrent/atomic/AtomicIntegerArray.java
new file mode 100644 (file)
index 0000000..fcc2125
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * model class for AtomicIntegerArray
+ */
+public class AtomicIntegerArray implements Serializable {
+  private static final long serialVersionUID = 2862133569453604235L;
+
+  private final int[] array;
+
+  public AtomicIntegerArray(int length) {
+    array = new int[length];
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public AtomicIntegerArray(int[] array) {
+    if (array == null)
+      throw new NullPointerException();
+
+    int length = array.length;
+    this.array = new int[length];
+
+    for (int i = 0; i < length; ++i)
+      this.array[i] = array[i];
+
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public final int length() {
+    return(array.length);
+  }
+
+  public final int get(int i) {
+    checkIndex(i);
+    return(getNative(i));
+  }
+
+  private final native int getNative(int i);
+
+  public final boolean compareAndSet(int i, int expect, int update) {
+    checkIndex(i);
+    return(compareAndSetNative(i, expect, update));
+  }
+
+  private final native boolean compareAndSetNative(int i, int expect, int update);
+
+  public final boolean weakCompareAndSet(int i, int expect, int update) {
+    return(compareAndSet(i, expect, update));
+  }
+
+  public final int getAndSet(int i, int newValue) {
+    while (true) {
+      int current = get(i);
+      if (compareAndSet(i, current, newValue))
+        return(current);
+    }
+  }
+
+  public final void set(int i, int newValue) {
+    getAndSet(i, newValue);
+  }
+
+  public final void lazySet(int i, int newValue) {
+    set(i, newValue);
+  }
+
+  public final int getAndIncrement(int i) {
+    return(getAndAdd(i, 1));
+  }
+
+  public final int getAndDecrement(int i) {
+    return(getAndAdd(i, -1));
+  }
+
+  public final int getAndAdd(int i, int delta) {
+    while (true) {
+      int current = get(i);
+      int next = current + delta;
+      if (compareAndSet(i, current, next))
+        return(current);
+    }
+  }
+
+  public final int incrementAndGet(int i) {
+    return(getAndIncrement(i) + 1);
+  }
+
+  public final int decrementAndGet(int i) {
+    return(getAndDecrement(i) - 1);
+  }
+
+  public final int addAndGet(int i, int delta) {
+    return(getAndAdd(i, delta) + delta);
+  }
+
+  @Override
+  public String toString() {
+    // <2do> need a volatile read in order to conform to JMM  // Does this really matter in JPF?
+    return(Arrays.toString(array));
+  }
+
+  private void checkIndex(int i) {
+    if (i < 0 || i >= array.length)
+      throw new IndexOutOfBoundsException("index " + i);
+  }
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java b/src/classes/java/util/concurrent/atomic/AtomicIntegerFieldUpdater.java
new file mode 100644 (file)
index 0000000..4c5725e
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+/**
+ * model class for the AtomicIntegerFieldUpdater
+ * in reality it's an abstract class, but this here is merely a stub anyways
+ */
+public class AtomicIntegerFieldUpdater<T> {
+
+  int fieldId;
+
+  public static <O> AtomicIntegerFieldUpdater<O> newUpdater (Class<O> objClass, String fieldName) {
+    return new AtomicIntegerFieldUpdater<O>(objClass, fieldName);
+  }
+
+  protected AtomicIntegerFieldUpdater(Class<T> objClass, String fieldName){
+    // direct Object subclass, so we can directly intercept the ctor
+    // w/o having to call a parent ctor
+  }
+
+  public native boolean compareAndSet(T obj, int expect, int update);
+  public native int     get(T obj);
+  public native int     getAndAdd(T obj, int delta);
+  public native int     getAndSet(T obj, int newValue);
+  public native void    lazySet(T obj, int newValue);
+  public native void    set(T obj, int newValue);
+  public native boolean weakCompareAndSet(T obj, int expect, int update);
+
+  public        int     addAndGet(T obj, int delta)  {return(getAndAdd(obj, delta) + delta);}
+  public        int     decrementAndGet(T obj)       {return(addAndGet(obj, -1));}
+  public        int     getAndDecrement(T obj)       {return(getAndAdd(obj, -1));}
+  public        int     getAndIncrement(T obj)       {return(getAndAdd(obj, 1));}
+  public        int     incrementAndGet(T obj)       {return(addAndGet(obj, 1));}
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicLongArray.java b/src/classes/java/util/concurrent/atomic/AtomicLongArray.java
new file mode 100644 (file)
index 0000000..1df393d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * model class for AtomicLongArray
+ */
+public class AtomicLongArray implements Serializable {
+  private static final long serialVersionUID = -2308431214976778248L;
+
+  private final long[] array;
+
+  public AtomicLongArray(int length) {
+    array = new long[length];
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public AtomicLongArray(long[] array) {
+    if (array == null)
+      throw new NullPointerException();
+
+    int length = array.length;
+    this.array = new long[length];
+
+    for (int i = 0; i < length; ++i)
+      this.array[i] = array[i];
+
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public final int length() {
+    return(array.length);
+  }
+
+  public final long get(int i) {
+    checkIndex(i);
+    return(getNative(i));
+  }
+
+  private final native long getNative(int i);
+
+  public final boolean compareAndSet(int i, long expect, long update) {
+    checkIndex(i);
+    return(compareAndSetNative(i, expect, update));
+  }
+
+  private final native boolean compareAndSetNative(int i, long expect, long update);
+
+  public final boolean weakCompareAndSet(int i, long expect, long update) {
+    return(compareAndSet(i, expect, update));
+  }
+
+  public final long getAndSet(int i, long newValue) {
+    while (true) {
+      long current = get(i);
+      if (compareAndSet(i, current, newValue))
+        return(current);
+    }
+  }
+
+  public final void set(int i, long newValue) {
+    getAndSet(i, newValue);
+  }
+
+  public final void lazySet(int i, long newValue) {
+    set(i, newValue);
+  }
+
+  public final long getAndIncrement(int i) {
+    return(getAndAdd(i, 1));
+  }
+
+  public final long getAndDecrement(int i) {
+    return(getAndAdd(i, -1));
+  }
+
+  public final long getAndAdd(int i, long delta) {
+    while (true) {
+      long current = get(i);
+      long next = current + delta;
+      if (compareAndSet(i, current, next))
+        return(current);
+    }
+  }
+
+  public final long incrementAndGet(int i) {
+    return(getAndIncrement(i) + 1);
+  }
+
+  public final long decrementAndGet(int i) {
+    return(getAndDecrement(i) - 1);
+  }
+
+  public final long addAndGet(int i, long delta) {
+    return(getAndAdd(i, delta) + delta);
+  }
+
+  @Override
+  public String toString() {
+    // <2do> need a volatile read in order to conform to JMM  // Does this really matter in JPF?
+    return(Arrays.toString(array));
+  }
+
+  private void checkIndex(int i) {
+    if (i < 0 || i >= array.length)
+      throw new IndexOutOfBoundsException("index " + i);
+  }
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java b/src/classes/java/util/concurrent/atomic/AtomicLongFieldUpdater.java
new file mode 100644 (file)
index 0000000..aa2534d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+/**
+ * model class for the AtomicLongFieldUpdater
+ * in reality it's an abstract class, but this here is merely a stub anyways
+ */
+public class AtomicLongFieldUpdater<T> {
+
+  int fieldId;
+
+  public static <O> AtomicLongFieldUpdater<O> newUpdater (Class<O> objClass, String fieldName) {
+    return new AtomicLongFieldUpdater<O>(objClass, fieldName);
+  }
+
+  protected AtomicLongFieldUpdater(Class<T> objClass, String fieldName){
+    // direct Object subclass, so we can directly intercept the ctor
+    // w/o having to call a parent ctor
+  }
+
+  public native boolean compareAndSet(T obj, long expect, long update);
+  public native long    get(T obj);
+  public native long    getAndAdd(T obj, long delta);
+  public native long    getAndSet(T obj, long newValue);
+  public native void    lazySet(T obj, long newValue);
+  public native void    set(T obj, long newValue);
+  public native boolean weakCompareAndSet(T obj, long expect, long update);
+
+  public        long    addAndGet(T obj, long delta) {return(getAndAdd(obj, delta) + delta);}
+  public        long    decrementAndGet(T obj)       {return(addAndGet(obj, -1));}
+  public        long    getAndDecrement(T obj)       {return(getAndAdd(obj, -1));}
+  public        long    getAndIncrement(T obj)       {return(getAndAdd(obj, 1));}
+  public        long    incrementAndGet(T obj)       {return(addAndGet(obj, 1));}
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicReferenceArray.java b/src/classes/java/util/concurrent/atomic/AtomicReferenceArray.java
new file mode 100644 (file)
index 0000000..6ae4b07
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+import java.io.Serializable;
+import java.util.Arrays;
+
+/**
+ * model class for AtomicReferenceArray
+ */
+public class AtomicReferenceArray<E> implements Serializable {
+  private static final long serialVersionUID = -6209656149925076980L;
+
+  private final Object[] array;
+
+  public AtomicReferenceArray(int length) {
+    array = new Object[length];
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public AtomicReferenceArray(E[] array) {
+    if (array == null)
+      throw new NullPointerException();
+
+    int length = array.length;
+    this.array = new Object[length];
+
+    for (int i = 0; i < length; ++i)
+      this.array[i] = array[i];
+
+    // <2do> need a volatile write in order to conform to JMM  // Does this really matter in JPF?
+  }
+
+  public final int length() {
+    return(array.length);
+  }
+
+  public final E get(int i) {
+    checkIndex(i);
+    return(getNative(i));
+  }
+
+  private final native E getNative(int i);
+
+  public final boolean compareAndSet(int i, E expect, E update) {
+    checkIndex(i);
+    return(compareAndSetNative(i, expect, update));
+  }
+
+  private final native boolean compareAndSetNative(int i, E expect, E update);
+
+  public final boolean weakCompareAndSet(int i, E expect, E update) {
+    return(compareAndSet(i, expect, update));
+  }
+
+  public final E getAndSet(int i, E newValue) {
+    while (true) {
+      E current = get(i);
+      if (compareAndSet(i, current, newValue))
+        return(current);
+    }
+  }
+
+  public final void set(int i, E newValue) {
+    getAndSet(i, newValue);
+  }
+
+  public final void lazySet(int i, E newValue) {
+    set(i, newValue);
+  }
+
+  @Override
+  public String toString() {
+    // <2do> need a volatile read in order to conform to JMM  // Does this really matter in JPF?
+    return(Arrays.toString(array));
+  }
+
+  private void checkIndex(int i) {
+    if (i < 0 || i >= array.length)
+      throw new IndexOutOfBoundsException("index " + i);
+  }
+}
diff --git a/src/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java b/src/classes/java/util/concurrent/atomic/AtomicReferenceFieldUpdater.java
new file mode 100644 (file)
index 0000000..845ea46
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.concurrent.atomic;
+
+/**
+ * model class for the AtomicReferenceFieldUpdater
+ * in reality it's an abstract class, but this here is merely a stub anyways
+ */
+public class AtomicReferenceFieldUpdater<T,V> {
+
+  int fieldId;
+
+  public static <O,F> AtomicReferenceFieldUpdater<O,F> newUpdater (Class<O> objClass, Class<F> fieldClass,
+                                                                   String fieldName) {
+    return new AtomicReferenceFieldUpdater<O,F>(objClass, fieldClass, fieldName);
+  }
+
+  protected AtomicReferenceFieldUpdater(Class<T> objClass, Class<V> fieldClass, String fieldName){
+    // direct Object subclass, so we can directly intercept the ctor
+    // w/o having to call a parent ctor
+  }
+
+  public native boolean compareAndSet(T obj, V expect, V update);
+  public native V       get(T obj);
+  public native V       getAndSet(T obj, V newValue);
+  public native void    lazySet(T obj, V newValue);
+  public native void    set(T obj, V newValue);
+  public native boolean weakCompareAndSet(T obj, V expect, V update);
+}
diff --git a/src/classes/java/util/function/Supplier.java b/src/classes/java/util/function/Supplier.java
new file mode 100644 (file)
index 0000000..e885b3e
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.function;
+
+/**
+ * Java7 doesn't have it, but we need it to model ThreadLocal
+ */
+public interface Supplier<T> {
+  T get();
+}
diff --git a/src/classes/java/util/logging/FileHandler.java b/src/classes/java/util/logging/FileHandler.java
new file mode 100644 (file)
index 0000000..7f60e80
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.logging;
+
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * simple stub to avoid execptions when using basic logging
+ */
+public class FileHandler extends StreamHandler {
+  public FileHandler() throws IOException {
+    this("log.log");
+  }
+
+  public FileHandler(String pattern) throws IOException {
+    super();
+    setOutputStream(new FileOutputStream(pattern));
+  }
+}
diff --git a/src/classes/java/util/regex/Matcher.java b/src/classes/java/util/regex/Matcher.java
new file mode 100644 (file)
index 0000000..8b1aac4
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.regex;
+
+/**
+ * model of a regular expression matcher, to save memory and execution time
+ */
+public class Matcher {
+
+  // this is the same trick like java.text.Format - avoiding a native
+  // memory leak by means of overwriting a JPF state tracked index value
+  // (well, it's still a leak since it never gets recycled unless we add a
+  // finalizer, but it should be much less serious)
+  static int nInstances;
+  private int id = nInstances++; // just for peer implementation purposes 
+  
+  Pattern pattern;
+  String input;    // that's an approximation (don't use CharSequence on the native side)
+  
+  Matcher() {
+  }
+  
+  Matcher (Pattern pattern, CharSequence inp){
+    this.pattern = pattern;
+    this.input = inp.toString();
+    
+    register();
+  }
+  
+  public Pattern pattern() {
+    return pattern;
+  }
+  
+  native void register();
+  
+  public native Matcher reset();
+  
+  public String group() {
+    return group(0);
+  }
+  
+  public native String group(int group);
+  
+  public native int groupCount();
+
+  public Matcher reset(CharSequence inp) {
+    this.input = inp.toString();
+    return reset();
+  }
+
+  public native boolean matches();
+  
+  public native boolean find();
+  
+  public native boolean lookingAt();
+  
+  public int start() {
+    return start(0);
+  }
+  
+  public native int start(int group);
+  
+  public int end() {
+    return end(0);
+  }
+  
+  public native int end(int group);
+
+  public native boolean hasTransparentBounds();
+
+  public native Matcher useTransparentBounds(boolean b);
+
+  public native boolean hasAnchoringBounds();
+
+  public native Matcher useAnchoringBounds(boolean b);
+
+  public native int regionStart();
+
+  public native int regionEnd();
+
+  public native Matcher region(int start, int end);
+
+  public static native String quoteReplacement(String abc);
+
+  public native String replaceAll(String replacement);
+
+  public native String replaceFirst(String replacement);
+
+  @Override
+  public native String toString();
+
+  public native boolean hitEnd();
+
+  public native boolean requireEnd();
+
+  // TODO public native MatchResult toMatchResult();
+  // TODO public native StringBuffer appendTail(StringBuffer sb);
+  // TODO public native Matcher appendReplacement(StringBuffer sb, String replacement);
+}
diff --git a/src/classes/java/util/regex/Pattern.java b/src/classes/java/util/regex/Pattern.java
new file mode 100644 (file)
index 0000000..7f00c44
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java.util.regex;
+
+/**
+ * simplified model of java.util.refex.Pattern, which otherwise
+ * is very expensive in terms of state memory and execution costs
+ */
+public class Pattern {
+
+  String regex;
+  int flags;
+  
+  public static Pattern compile (String regex) {
+    return new Pattern(regex, 0);
+  }
+  
+  public static Pattern compile (String regex, int flags){
+    return new Pattern(regex, flags);
+  }
+  
+  private Pattern (String regex, int flags){
+    this.regex = regex;
+    this.flags = flags;
+  }
+  
+  public Matcher matcher (CharSequence input){
+    return new Matcher(this, input);
+  }
+  
+  public String pattern() {
+    return regex;
+  }
+  
+  public String[] split (CharSequence input){
+    return split(input,0);
+  }
+
+  public String[] split (CharSequence input, int limit){
+    return split0(input.toString(), limit); // just to avoid the CharSequence charAt() hassle on the native side
+  }
+
+  private native String[] split0(String input, int limit);
+  
+  @Override
+  public String toString() {
+    return regex;
+  }
+}
diff --git a/src/classes/org/junit/After.java b/src/classes/org/junit/After.java
new file mode 100644 (file)
index 0000000..11b8e18
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * same as org.junit.Test - we don't want to pull the whole of JUnit
+ * into our JPF classpath just because we run TestJPF derived classes
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface After {
+  // nothing in here
+}
diff --git a/src/classes/org/junit/AfterClass.java b/src/classes/org/junit/AfterClass.java
new file mode 100644 (file)
index 0000000..bd2fee0
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * same as org.junit.Test - we don't want to pull the whole of JUnit
+ * into our JPF classpath just because we run TestJPF derived classes
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface AfterClass {
+  // nothing in here
+}
diff --git a/src/classes/org/junit/Before.java b/src/classes/org/junit/Before.java
new file mode 100644 (file)
index 0000000..de2678b
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * same as org.junit.Test - we don't want to pull the whole of JUnit
+ * into our JPF classpath just because we run TestJPF derived classes
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Before {
+  // nothing in here
+}
diff --git a/src/classes/org/junit/BeforeClass.java b/src/classes/org/junit/BeforeClass.java
new file mode 100644 (file)
index 0000000..c689a10
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * same as org.junit.Test - we don't want to pull the whole of JUnit
+ * into our JPF classpath just because we run TestJPF derived classes
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface BeforeClass {
+  // nothing in here
+}
\ No newline at end of file
diff --git a/src/classes/org/junit/Ignore.java b/src/classes/org/junit/Ignore.java
new file mode 100644 (file)
index 0000000..c89ebda
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * same as org.junit.Test - we don't want to pull the whole of JUnit
+ * into our JPF classpath just because we run TestJPF derived classes
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Ignore {
+  String value() default "";
+}
diff --git a/src/classes/org/junit/Test.java b/src/classes/org/junit/Test.java
new file mode 100644 (file)
index 0000000..5275269
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * this annotation type is shamelessly lifted from JUnit.
+ *
+ * The only reason it is here is that we don't want to pull the whole of JUnit into
+ * our 'classpath' when executing TestJPF classes under JPF, just because some of
+ * the test SUT methods have a @Test annotation.
+ *
+ * Lifting/masking classes in general is not a good practice, but it seems to
+ * be the lesser of two evils in this case, given that we really don't need
+ * anything but just the simple annotation type.
+ *
+ * NOTE - this might be different if we execute junit under JPF, which is the
+ * reason why we include the @Test features here. However - this assumes that @Test
+ * is such a basic JUnit type that it won't change
+ */
+
+package org.junit;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Test {
+
+  static class None extends Throwable {
+    private static final long serialVersionUID= 1L;
+    private None() {}
+  }
+
+  Class<? extends Throwable> expected() default None.class;
+
+  long timeout() default 0L;
+}
diff --git a/src/classes/sun/misc/AtomicLong.java b/src/classes/sun/misc/AtomicLong.java
new file mode 100644 (file)
index 0000000..cf3e430
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+package sun.misc;
+
+/**
+ * MJI model class for sun.misc.AtomicLong library abstraction
+ */
+public class AtomicLong {
+  long value;
+
+  public AtomicLong () {
+  }
+
+  private AtomicLong (long val) {
+    value = val;
+  }
+
+  public static AtomicLong newAtomicLong (long val) {
+    return new AtomicLong(val);
+  }
+
+  public boolean attemptAdd (long l) {
+    value += l;
+
+    return true;
+  }
+
+  public boolean attemptIncrememt () {
+    value++;
+
+    return true;
+  }
+
+  public boolean attemptSet (long val) {
+    value = val;
+
+    return true;
+  }
+
+  public boolean attemptUpdate (long fallback, long newval) {
+    value = newval;
+
+    return true;
+  }
+
+  public long get () {
+    return value;
+  }
+
+  @SuppressWarnings("unused")
+  private static boolean VMSupportsCS8 () {
+    // whatever it means
+    return false;
+  }
+}
diff --git a/src/classes/sun/misc/JavaAWTAccess.java b/src/classes/sun/misc/JavaAWTAccess.java
new file mode 100644 (file)
index 0000000..84a44ee
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+/**
+ * same as the others - we need to model it in case somebody uses Java 5,
+ * or an earlier Java 6 version on OS X
+ */
+public interface JavaAWTAccess {
+  
+  // don't know about these yet
+  public Object get();
+  public void put(Object k, Object v);
+  public void remove(Object k);
+  
+  public boolean isDisposed();
+  public boolean isMainAppContext();
+
+}
diff --git a/src/classes/sun/misc/JavaIOAccess.java b/src/classes/sun/misc/JavaIOAccess.java
new file mode 100644 (file)
index 0000000..89c7fec
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+//import java.io.Console;
+import java.nio.charset.Charset;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+public interface JavaIOAccess {
+    //public Console console(); // not in Java 1.5, so we skip for now
+    public Runnable consoleRestoreHook();
+    public Charset charset();
+}
diff --git a/src/classes/sun/misc/JavaIODeleteOnExitAccess.java b/src/classes/sun/misc/JavaIODeleteOnExitAccess.java
new file mode 100644 (file)
index 0000000..931ac84
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+public interface JavaIODeleteOnExitAccess extends Runnable {
+  @Override
+  public void run();
+}
diff --git a/src/classes/sun/misc/JavaIOFileDescriptorAccess.java b/src/classes/sun/misc/JavaIOFileDescriptorAccess.java
new file mode 100644 (file)
index 0000000..f1ee8c4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+import java.io.FileDescriptor;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+
+public interface JavaIOFileDescriptorAccess {
+    public void set(FileDescriptor fdObj, int fd);
+    public int get(FileDescriptor fdObj);
+    public void setHandle(FileDescriptor fdObj, long h);
+    public long getHandle(FileDescriptor fdObj);
+}
diff --git a/src/classes/sun/misc/JavaLangAccess.java b/src/classes/sun/misc/JavaLangAccess.java
new file mode 100644 (file)
index 0000000..8ec78e5
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+import sun.nio.ch.Interruptible;
+import sun.reflect.ConstantPool;
+import sun.reflect.annotation.AnnotationType;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.misc.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+
+public interface JavaLangAccess {
+
+    ConstantPool getConstantPool(Class<?> klass);
+
+    void setAnnotationType(Class<?> klass, AnnotationType annotationType);
+
+    AnnotationType getAnnotationType(Class<?> klass);
+
+    <E extends Enum<E>> E[] getEnumConstantsShared(Class<E> klass);
+
+    void blockedOn(Thread t, Interruptible b);
+
+    void registerShutdownHook(int slot, Runnable r);
+    
+    int getStackTraceDepth(Throwable t);
+    
+    StackTraceElement getStackTraceElement(Throwable t, int i);
+}
diff --git a/src/classes/sun/misc/JavaNetAccess.java b/src/classes/sun/misc/JavaNetAccess.java
new file mode 100644 (file)
index 0000000..7cb92cd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.misc;
+
+import java.net.URLClassLoader;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+public interface JavaNetAccess {
+    URLClassPath getURLClassPath (URLClassLoader ucl);
+}
diff --git a/src/classes/sun/misc/JavaNioAccess.java b/src/classes/sun/misc/JavaNioAccess.java
new file mode 100644 (file)
index 0000000..67e4ecd
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+package sun.misc;
+
+/**
+ * <2do> this is not supported yet
+ */
+public interface JavaNioAccess {
+  
+  interface BufferPool {
+    String getName();
+    long getCount();
+    long getTotalCapacity();
+    long getMemoryUsed();
+  }
+
+  BufferPool getDirectBufferPool();
+}
diff --git a/src/classes/sun/misc/SharedSecrets.java b/src/classes/sun/misc/SharedSecrets.java
new file mode 100644 (file)
index 0000000..c74ff4a
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+package sun.misc;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.ObjectInputStream;
+import java.util.jar.JarFile;
+
+/**
+ * This is a backdoor mechanism in Java 6 to allow (some sort of)
+ * controlled access to internals between packages, using
+ * sun.misc.* interfaces (e.g. JavaLangAccess) that are anonymously
+ * instantiated within the exporting package (e.g. java.lang), and
+ * obtained via SharedSecrets, which in turn obtains the
+ * instances from sun.misc.Unsafe. For most packages these interface
+ * objects are created on demand by static init of some key classes of
+ * these packages that call the SharedSecrets setters
+ * (except for JavaLangAccess and JavaNetAccess)
+ *
+ * Since this is used from within the standard libraries of Java 6, we need
+ * some sort of support, but we don't want to break Java 1.5 yet by introducing
+ * lots of Java 6 dependencies, which would force us to duplicate their code
+ * even though it might be pure Java (like java.io.Console).
+ *
+ * This is a can of worms, which we only open partially to support
+ * EnumSets for both Java 1.5 and 6. We make the cut at java.* packages -
+ * if the backdoor interfaces/types require anything outside sun.* packages,
+ * we leave it out.
+ *
+ * All of this is hopefully going away when we drop Java 1.5 support, and is
+ * to be replaced by some native peers providing the required native calls
+ */
+public class SharedSecrets {
+  private static final Unsafe unsafe = Unsafe.getUnsafe();
+
+  private static JavaUtilJarAccess javaUtilJarAccess;
+  private static JavaLangAccess javaLangAccess;
+  private static JavaIOAccess javaIOAccess;
+  private static JavaIODeleteOnExitAccess javaIODeleteOnExitAccess;
+  private static JavaNetAccess javaNetAccess;
+  private static JavaIOFileDescriptorAccess javaIOFileDescriptorAccess;
+  private static JavaNioAccess javaNioAccess;
+  private static JavaAWTAccess javaAWTAccess;
+  private static JavaOISAccess javaOISAccess;
+  private static JavaObjectInputStreamAccess javaObjectInputStreamAccess;
+
+  // (required for EnumSet ops)
+  public static JavaLangAccess getJavaLangAccess() {
+    return javaLangAccess;
+  }
+  // automatically called by java.lang.System clinit
+  public static void setJavaLangAccess(JavaLangAccess jla) {
+    javaLangAccess = jla;
+  }
+
+
+  public static void setJavaNetAccess(JavaNetAccess jna) {
+    javaNetAccess = jna;
+  }
+  // automatically called by java.net.URLClassLoader clinit
+  public static JavaNetAccess getJavaNetAccess() {
+    return javaNetAccess;
+  }
+
+
+  public static JavaUtilJarAccess javaUtilJarAccess() {
+    if (javaUtilJarAccess == null) {
+      unsafe.ensureClassInitialized(JarFile.class);
+    }
+    return javaUtilJarAccess;
+  }
+  public static void setJavaUtilJarAccess(JavaUtilJarAccess access) {
+    javaUtilJarAccess = access;
+  }
+
+
+  public static void setJavaIOAccess(JavaIOAccess jia) {
+    javaIOAccess = jia;
+  }
+  // this is normally done by java.io.Console, which is not in Java 1.5
+  // since this is a rather big beast with lost of bytecode, we don't add
+  // this for now
+  public static JavaIOAccess getJavaIOAccess() {
+    if (javaIOAccess == null) {
+      //unsafe.ensureClassInitialized(Console.class);
+      throw new UnsupportedOperationException("sun.misc.SharedSecrets.getJavaIOAccess() not supported yet");
+    }
+    return javaIOAccess;
+  }
+
+  
+  public static void setJavaNioAccess(JavaNioAccess a) {
+    javaNioAccess = a;
+  }
+  public static JavaNioAccess getJavaNioAccess() {
+    if (javaNioAccess == null) {
+      throw new UnsupportedOperationException("sun.misc.SharedSecrets.getJavaNioAccess() not supported yet");
+    }
+    return javaNioAccess;
+  }
+
+  
+  public static void setJavaIODeleteOnExitAccess(JavaIODeleteOnExitAccess jida) {
+    javaIODeleteOnExitAccess = jida;
+  }
+
+  public static JavaIODeleteOnExitAccess getJavaIODeleteOnExitAccess() {
+    if (javaIODeleteOnExitAccess == null) {
+      unsafe.ensureClassInitialized(File.class);
+    }
+    return javaIODeleteOnExitAccess;
+  }
+
+  public static void setJavaIOFileDescriptorAccess(JavaIOFileDescriptorAccess jiofda) {
+    javaIOFileDescriptorAccess = jiofda;
+  }
+  public static JavaIOFileDescriptorAccess getJavaIOFileDescriptorAccess() {
+    if (javaIOFileDescriptorAccess == null) {
+      unsafe.ensureClassInitialized(FileDescriptor.class);
+      throw new UnsupportedOperationException("sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess() not supported yet");
+    }
+
+    return javaIOFileDescriptorAccess;
+  }
+
+  public static JavaObjectInputStreamAccess getJavaObjectInputStreamAccess() {
+    if (javaObjectInputStreamAccess == null) {
+      unsafe.ensureClassInitialized(ObjectInputStream.class);
+      throw new UnsupportedOperationException("sun.misc.SharedSecrets.getJavaObjectInputStreamAccess() not supported yet");
+    }
+    
+    return javaObjectInputStreamAccess;
+  }
+
+  public static void setJavaObjectInputStreamAccess(JavaObjectInputStreamAccess access) {
+    javaObjectInputStreamAccess = access;
+  }
+  
+  public static void setJavaAWTAccess (JavaAWTAccess jaa){
+    javaAWTAccess = jaa;
+  }
+  public static JavaAWTAccess getJavaAWTAccess(){
+    return javaAWTAccess;
+  }
+
+  public static void setJavaOISAccess(JavaOISAccess access) {
+    javaOISAccess = access;
+  }
+
+  public static JavaOISAccess getJavaOISAccess() {
+    if (javaOISAccess == null) {
+      unsafe.ensureClassInitialized(ObjectInputStream.class);
+      throw new UnsupportedOperationException("sun.misc.SharedSecrets.getJavaOISAccess() not supported yet");
+    }
+
+    return javaOISAccess;
+  }
+}
diff --git a/src/classes/sun/misc/Unsafe.java b/src/classes/sun/misc/Unsafe.java
new file mode 100644 (file)
index 0000000..e024ab7
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+package sun.misc;
+
+import java.lang.reflect.Field;
+
+/**
+ * Unsafe = unwanted. See comments in the native peer. We only have it because
+ * it is required by java.util.concurrent.
+ *
+ * Note that in the real world, this class is only callable from the system library,
+ * not application code
+ */
+
+public class Unsafe {
+  private static final Unsafe theUnsafe = new Unsafe();
+
+  public static final int INVALID_FIELD_OFFSET = -1;
+
+  public static Unsafe getUnsafe() {
+    return theUnsafe;
+    //return new Unsafe();
+  }
+
+  // field offsets are completely useless between VMs, we just return
+  // a numeric id for the corresponding FieldInfo here
+  public native int fieldOffset (Field f);
+  public native long objectFieldOffset (Field f);
+
+  // those do the usual CAS magic
+  public native boolean compareAndSwapObject (Object oThis, long offset, Object expect, Object update);
+  public native boolean compareAndSwapInt (Object oThis, long offset, int expect, int update);
+  public native boolean compareAndSwapLong (Object oThis, long offset, long expect, long update);
+
+  // that looks like some atomic conditional wait
+  public native void park (boolean isAbsolute, long timeout);
+  public native void unpark (Object thread);
+
+  // various accessors
+  public native int getInt(Object obj, long l);
+  public native int getIntVolatile(Object obj, long l);
+
+  @Deprecated
+  public int getInt(Object obj, int offset) {
+    return getInt(obj, (long) offset);
+  }
+
+  public native void putInt(Object obj, long l, int i);
+  public native void putIntVolatile(Object obj, long l, int i);
+
+  @Deprecated
+  public void putInt(Object obj, int offset, int i) {
+    putInt(obj, (long) offset, i);
+  }
+
+  public native void putOrderedInt(Object obj, long l, int i);
+
+  public native Object getObject(Object obj, long l);
+  public native Object getObjectVolatile(Object obj, long l);
+
+  @Deprecated
+  public Object getObject(Object obj, int offset) {
+    return getObject(obj, (long) offset);
+  }
+
+  public native void putObject(Object obj, long l, Object obj1);
+  public native void putObjectVolatile(Object obj, long l, Object obj1);
+  
+
+  @Deprecated
+  public void putObject(Object obj, int offset, Object obj1) {
+    putObject(obj, (long) offset, obj1);
+  }
+
+  public native void putOrderedObject(Object obj, long l, Object obj1);
+
+  public native boolean getBoolean(Object obj, long l);
+  public native boolean getBooleanVolatile(Object obj, long l);
+
+  @Deprecated
+  public boolean getBoolean(Object obj, int offset) {
+    return getBoolean(obj, (long) offset);
+  }
+
+  public native void putBoolean(Object obj, long l, boolean flag);
+  public native void putBooleanVolatile(Object obj, long l, boolean flag);
+
+  @Deprecated
+  public void putBoolean(Object obj, int offset, boolean flag) {
+    putBoolean(obj, (long) offset, flag);
+  }
+
+  public native byte getByte(Object obj, long l);
+  public native byte getByteVolatile(Object obj, long l);
+
+  @Deprecated
+  public byte getByte(Object obj, int offset) {
+    return getByte(obj, (long) offset);
+  }
+
+  public native void putByte(Object obj, long l, byte byte0);
+  public native void putByteVolatile(Object obj, long l, byte byte0);
+
+  @Deprecated
+  public void putByte(Object obj, int offset, byte byte0) {
+    putByte(obj, (long) offset, byte0);
+  }
+
+  public native short getShort(Object obj, long l);
+  public native short getShortVolatile(Object obj, long l);
+
+  @Deprecated
+  public short getShort(Object obj, int offset) {
+    return getShort(obj, (long) offset);
+  }
+
+  public native void putShort(Object obj, long l, short word0);
+  public native void putShortVolatile(Object obj, long l, short word0);
+
+  @Deprecated
+  public void putShort(Object obj, int offset, short word0) {
+    putShort(obj, (long) offset, word0);
+  }
+
+  public native char getChar(Object obj, long l);
+  public native char getCharVolatile(Object obj, long l);
+
+  @Deprecated
+  public char getChar(Object obj, int offset) {
+    return getChar(obj, (long) offset);
+  }
+
+  public native void putChar(Object obj, long l, char c);
+  public native void putCharVolatile(Object obj, long l, char c);
+
+  @Deprecated
+  public void putChar(Object obj, int offset, char c) {
+    putChar(obj, (long) offset, c);
+  }
+
+  public native long getLong(Object obj, long l);
+  public native long getLongVolatile(Object obj, long l);
+
+  @Deprecated
+  public long getLong(Object obj, int offset) {
+    return getLong(obj, (long) offset);
+  }
+
+  public native void putLong(Object obj, long l, long l1);
+  public native void putLongVolatile(Object obj, long l, long l1);
+
+  public native void putOrderedLong(Object obj, long l, long l1);
+
+  @Deprecated
+  public void putLong(Object obj, int offset, long l1) {
+    putLong(obj, (long) offset, l1);
+  }
+
+  public native float getFloat(Object obj, long l);
+  public native float getFloatVolatile(Object obj, long l);
+
+  @Deprecated
+  public float getFloat(Object obj, int offset) {
+    return getFloat(obj, (long) offset);
+  }
+
+  public native void putFloat(Object obj, long l, float f);
+  public native void putFloatVolatile(Object obj, long l, float f);
+
+  @Deprecated
+  public void putFloat(Object obj, int offset, float f) {
+    putFloat(obj, (long) offset, f);
+  }
+
+  public native double getDouble(Object obj, long l);
+  public native double getDoubleVolatile(Object obj, long l);
+
+  @Deprecated
+  public double getDouble(Object obj, int offset) {
+    return getDouble(obj, (long) offset);
+  }
+
+  public native void putDouble(Object obj, long l, double d);
+  public native void putDoubleVolatile(Object obj, long l, double d);
+
+  @Deprecated
+  public void putDouble(Object obj, int offset, double d) {
+    putDouble(obj, (long) offset, d);
+  }
+
+  public native void ensureClassInitialized(Class<?> cls);
+
+  public native int arrayBaseOffset(Class<?> clazz);
+
+  public native int arrayIndexScale(Class<?> clazz);
+  
+  
+  //--- java.nio finally breaks object boundaries  - hello, evil pointer arithmetic
+  
+  /**
+   * this is really a byte[] allocation (used by java.nio.Bits). Note that
+   * object has to be explicitly freed with freeMemory() (yikes!)
+   */
+  public native long allocateMemory (long bytes);
+  
+  /**
+   * to be used to free allocateMemory() allocated array objects
+   */
+  public native void freeMemory (long byteArrayRef);
+    
+  /**
+   * byte access of allocateMemory() objects. Note that 'address' has
+   * to point into such an object
+   */
+  public native byte getByte (long address);
+  public native void putByte (long address, byte val);
+  
+  public native char getChar (long address);
+  public native void putChar (long address, char val);
+  
+  public native int getInt (long address);
+  public native void putInt (long address, int val);
+  
+  public native long getLong (long address);
+  public native void putLong (long address, long val);
+
+  public native float getFloat (long address);
+  public native void putFloat (long address, float val);
+
+  public native double getDouble (long address);
+  public native void putDouble (long address, double val);
+  
+}
diff --git a/src/classes/sun/net/www/protocol/http/Handler.java b/src/classes/sun/net/www/protocol/http/Handler.java
new file mode 100644 (file)
index 0000000..3b717be
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+package sun.net.www.protocol.http;
+
+import gov.nasa.jpf.CachedROHttpConnection;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.Proxy;
+import java.net.URL;
+
+/**
+ * replaced handler to support configured URLConnection classes
+ */
+public class Handler extends java.net.URLStreamHandler {
+
+  protected String proxy;
+  protected int proxyPort;
+
+  public Handler() {
+    proxy = null;
+    proxyPort = -1;
+  }
+
+  public Handler(String proxy, int port) {
+    this.proxy = proxy;
+    this.proxyPort = port;
+  }
+
+  @Override
+  protected int getDefaultPort() {
+    return 80;
+  }
+
+
+  static Class<?>[] argTypes = { URL.class, Proxy.class };
+  private native Class<? extends java.net.URLConnection> getConnectionClass(String url);
+
+  
+  @Override
+  protected java.net.URLConnection openConnection (URL u, Proxy p) throws IOException {
+
+    Class<? extends java.net.URLConnection> clazz = getConnectionClass(u.toString());
+
+    if (clazz != null){
+      try {
+        Constructor<? extends java.net.URLConnection> ctor = clazz.getConstructor(argTypes);
+        return ctor.newInstance(u, p);
+
+      } catch (NoSuchMethodException nmx){
+        throw new IOException("connection class has no suitabe ctor: " + clazz.getName());
+      } catch (IllegalAccessException iax){
+        throw new IOException("connection class has no public ctor: " + clazz.getName());
+      } catch (InvocationTargetException itx){
+        throw new IOException("exception initializing URLConnection", itx);
+      } catch (InstantiationException ix){
+        throw new IOException("connection class cannot be instantiated: " + clazz.getName());
+      }
+
+    } else {
+      // we just go with the standard thing, hoping that we have a modeled Socket layer
+      return new CachedROHttpConnection(u, p, this);
+    }
+  }
+
+  @Override
+  protected java.net.URLConnection openConnection(URL u) throws IOException {
+    return openConnection(u, null);
+  }
+
+  //... and a lot of methods still missing
+}
diff --git a/src/classes/sun/nio/ch/Interruptible.java b/src/classes/sun/nio/ch/Interruptible.java
new file mode 100644 (file)
index 0000000..a253c27
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.nio.ch;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+
+public interface Interruptible {
+  public void interrupt();
+}
diff --git a/src/classes/sun/reflect/ConstantPool.java b/src/classes/sun/reflect/ConstantPool.java
new file mode 100644 (file)
index 0000000..e871f69
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.reflect;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Member;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * see sun.msic.SharedSecrets for details
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ * (to be replaced with a native peer for the native methods of the lib class)
+ */
+public class ConstantPool {
+
+  public int getSize() {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Class<?> getClassAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Class<?> getClassAtIfLoaded(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Member getMethodAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Member getMethodAtIfLoaded(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Field getFieldAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public Field getFieldAtIfLoaded(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public String[] getMemberRefInfoAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public int getIntAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public long getLongAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public float getFloatAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public double getDoubleAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public String getStringAt(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+  public String getUTF8At(int index) {
+    throw new UnsupportedOperationException("sun.reflect.ConstantPool not supported yet");
+  }
+
+}
diff --git a/src/classes/sun/reflect/annotation/AnnotationType.java b/src/classes/sun/reflect/annotation/AnnotationType.java
new file mode 100644 (file)
index 0000000..77a2630
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+package sun.reflect.annotation;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import sun.misc.SharedSecrets;
+
+/**
+ * this is a placeholder for a Java 6 class, which we only have here to
+ * support both Java 1.5 and 6 with the same set of env/ classes
+ *
+ * this is Java only, so it's a drag we have to add this, but since it is outside
+ * java.* and doesn't refer to Java 6 stuff outside the sun.misc.SharedSecrets
+ * we bite the bullet and add it (for now)
+ *
+ * <2do> THIS IS GOING AWAY AS SOON AS WE OFFICIALLY SWITCH TO JAVA 6
+ */
+public class AnnotationType {
+
+  private RetentionPolicy retention = RetentionPolicy.RUNTIME;
+  private boolean inherited = false;
+
+  // caches
+  private final Map<String, Class<?>> memberTypes = new HashMap<String, Class<?>>();
+  private final Map<String, Object> memberDefaults = new HashMap<String, Object>();
+  private final Map<String, Method> members = new HashMap<String, Method>();
+
+
+  public static synchronized AnnotationType getInstance (Class<?> annotationClass) {
+    AnnotationType at = SharedSecrets.getJavaLangAccess().getAnnotationType(annotationClass);
+    if (at == null) {
+      at = new AnnotationType(annotationClass);
+    }
+    return at;
+  }
+
+  private AnnotationType(final Class<?> annoCls) {
+    if (!annoCls.isAnnotation()) {
+      throw new IllegalArgumentException("Not an annotation type");
+    }
+
+    Method[] methods = annoCls.getDeclaredMethods();
+
+    for (Method m : methods) {
+      if (m.getParameterTypes().length == 0) {
+        // cache name -> method assoc
+        String mname = m.getName();
+        members.put(mname, m);
+
+        // cache member type
+        Class<?> type = m.getReturnType();
+        memberTypes.put(mname, invocationHandlerReturnType(type));
+
+        // cache member default val (if any)
+        Object val = m.getDefaultValue();
+        if (val != null) {
+          memberDefaults.put(mname, val);
+        }
+      } else {
+        // probably an exception
+      }
+    }
+
+    if ((annoCls != Retention.class) && (annoCls != Inherited.class)) { // don't get recursive
+      inherited = annoCls.isAnnotationPresent(Inherited.class);
+
+      Retention r = annoCls.getAnnotation(Retention.class);
+      if (r == null) {
+        retention = RetentionPolicy.CLASS;
+      } else {
+        retention = r.value();
+      }
+    }
+
+    SharedSecrets.getJavaLangAccess().setAnnotationType(annoCls, this);
+  }
+
+  public static Class<?> invocationHandlerReturnType (Class<?> type) {
+    // return box types for builtins
+    if (type == boolean.class) {
+      return Boolean.class;
+    } else if (type == byte.class) {
+      return Byte.class;
+    } else if (type == char.class) {
+      return Character.class;
+    } else if (type == short.class) {
+      return Short.class;
+    } else if (type == int.class) {
+      return Integer.class;
+    } else if (type == long.class) {
+      return Long.class;
+    } else if (type == float.class) {
+      return Float.class;
+    } else if (type == double.class) {
+      return Double.class;
+    } else {
+      return type;
+    }
+  }
+
+  public Map<String, Class<?>> memberTypes() {
+    return memberTypes;
+  }
+
+  public Map<String, Method> members() {
+    return members;
+  }
+
+  public Map<String, Object> memberDefaults() {
+    return memberDefaults;
+  }
+
+  public RetentionPolicy retention() {
+    return retention;
+  }
+
+  public boolean isInherited() {
+    return inherited;
+  }
+
+}
diff --git a/src/examples/BoundedBuffer.java b/src/examples/BoundedBuffer.java
new file mode 100644 (file)
index 0000000..985fbc1
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * concurrency example with deadlock
+ * adapted from "Concurrency: State Models & Java Programs", Jeff Magee & Jeff Kramer
+ * 
+ * Note there need to be at least 2x buffer_size instances of either producers or
+ * consumers in order to produce the deadlock, which basically depends on 
+ * a notification choice between a consumer and a producer in a context where
+ * only threads of the notifier type are still runnable
+ * 
+ * This is a good benchmark for state management/matching in a small heap context.
+ * Higher numbers of buffer size and producers/consumers result in a nice
+ * state explosion
+ */
+public class BoundedBuffer {
+  
+  static int BUFFER_SIZE = 1;
+  static int N_PRODUCERS = 4;
+  static int N_CONSUMERS = 4;
+  
+  static Object DATA = "fortytwo";
+      
+  //--- the bounded buffer implementation
+  protected Object[] buf;
+  protected int in = 0;
+  protected int out= 0;
+  protected int count= 0;
+  protected int size;
+  
+  public BoundedBuffer(int size) {
+    this.size = size;
+    buf = new Object[size];
+  }
+
+  public synchronized void put(Object o) throws InterruptedException {
+    while (count == size) {
+      wait();
+    }
+    buf[in] = o;
+    //System.out.println("PUT from " + Thread.currentThread().getName());
+    ++count;
+    in = (in + 1) % size;
+    notify(); // if this is not a notifyAll() we might notify the wrong waiter
+  }
+
+  public synchronized Object get() throws InterruptedException {
+    while (count == 0) {
+      wait();
+    }
+    Object o = buf[out];
+    buf[out] = null;
+    //System.out.println("GET from " + Thread.currentThread().getName());
+    --count;
+    out = (out + 1) % size;
+    notify(); // if this is not a notifyAll() we might notify the wrong waiter
+    return (o);
+  }
+  
+  //--- the producer
+  static class Producer extends Thread {
+    static int nProducers = 1;
+    BoundedBuffer buf;
+
+    Producer(BoundedBuffer b) {
+      buf = b;
+      setName("P" + nProducers++);
+    }
+
+    @Override
+       public void run() {
+      try {
+        while(true) {
+          // to ease state matching, we don't put different objects in the buffer
+          buf.put(DATA);
+        }
+      } catch (InterruptedException e){}
+    }
+  }
+  
+  //--- the consumer
+  static class Consumer extends Thread {
+    static int nConsumers = 1;
+    BoundedBuffer buf;
+
+    Consumer(BoundedBuffer b) {
+      buf = b;
+      setName( "C" + nConsumers++);
+    }
+
+    @Override
+       public void run() {
+      try {
+        while(true) {
+            Object tmp = buf.get();
+        }
+      } catch(InterruptedException e ){}
+    }
+  }
+
+  //--- the test driver
+  public static void main(String [] args) {
+    readArguments( args);
+    //System.out.printf("running BoundedBuffer with buffer-size %d, %d producers and %d consumers\n", BUFFER_SIZE, N_PRODUCERS, N_CONSUMERS);
+
+    BoundedBuffer buf = new BoundedBuffer(BUFFER_SIZE);
+   
+    for (int i=0; i<N_PRODUCERS; i++) {
+      new Producer(buf).start();
+    }
+    for (int i=0; i<N_CONSUMERS; i++) {
+      new Consumer(buf).start();
+    }
+  }
+  
+  static void readArguments (String[] args){
+    if (args.length > 0){
+      BUFFER_SIZE = Integer.parseInt(args[0]);
+    }
+    if (args.length > 1){
+      N_PRODUCERS = Integer.parseInt(args[1]);      
+    }
+    if (args.length > 2){
+      N_CONSUMERS = Integer.parseInt(args[2]);      
+    }
+  }
+}
diff --git a/src/examples/BoundedBuffer.jpf b/src/examples/BoundedBuffer.jpf
new file mode 100644 (file)
index 0000000..bdd86dc
--- /dev/null
@@ -0,0 +1,4 @@
+target = BoundedBuffer
+
+# this should produce a deadlock
+target.args = 2,4,1
\ No newline at end of file
diff --git a/src/examples/Crossing.java b/src/examples/Crossing.java
new file mode 100644 (file)
index 0000000..14e279d
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * This is the famous Bridge-crossing puzzle. The aim is to see what the minimum
+ * amount of time is for 4 people to cross with flash-light (torch): 10.5.2.1.
+ * The answer is 17 for the given configuration.
+ *
+ * Purpose of this example is to show how to use the 'Verify' API in test
+ * applications (usually drivers)
+ *
+ * One can find this solution with JPF in BFS search mode - DFS will not
+ * work since this is an infinite-state program (time keeps increasing).
+ *
+ * If one uses the ignoreIf(cond) call the branches that will lead to
+ * solutions worst than the current will be cut-off and this will allow
+ * DFS also to complete - and BFS to terminate faster.
+ *
+ * When setting the native flag in main one can also save information
+ * from one run to the next using JPF's native peer methods -
+ * see JPF_Crossing.java for the code of getTotal() and setTotal(int).
+ */
+
+import gov.nasa.jpf.vm.Verify;
+
+enum Side { LEFT, RIGHT }
+
+class Bridge {
+
+  Side torchSide;
+  Person[] onBridge = new Person[2];
+  int numOnBridge = 0;
+
+  public Bridge (Side torchSide){
+    this.torchSide = torchSide;
+  }
+
+  void moveTorch(){
+    if (torchSide == Side.LEFT){
+      torchSide = Side.RIGHT;
+    } else {
+      torchSide = Side.LEFT;
+    }
+  }
+  
+  public int cross() {
+    int time = 0;
+    moveTorch();
+
+    if (numOnBridge == 1) {
+      onBridge[0].side = torchSide;
+      time = onBridge[0].timeToCross;
+
+    //System.out.println("Person " + onBridge[0] +
+    //                     " moved to " + Torch.side +
+    //               " in time " + time);
+    } else {
+      onBridge[0].side = torchSide;
+      onBridge[1].side = torchSide;
+
+      if (onBridge[0].timeToCross > onBridge[1].timeToCross) {
+        time = onBridge[0].timeToCross;
+      } else {
+        time = onBridge[1].timeToCross;
+      }
+
+    //System.out.println("Person " + onBridge[0] +
+    //                 " and Person " + onBridge[1] +
+    //                 " moved to " + Torch.side +
+    //                 " in time " + time);
+    }
+
+    return time;
+  }
+
+  public void clearBridge() {
+    numOnBridge = 0;
+    onBridge[0] = null;
+    onBridge[1] = null;
+  }
+  
+  boolean isFull(){
+    return numOnBridge == 2;
+  }
+  
+  boolean arePersonsOnBridge(){
+    return numOnBridge > 0;
+  }
+  
+  boolean isPersonOnBridge (Person p){
+    return (p == onBridge[0] || p == onBridge[1]);
+  }
+
+  void putPersonOnBridge (Person p){
+    onBridge[numOnBridge++] = p;
+  }  
+}
+
+class Person {
+  String name;
+  Side side;
+  int timeToCross;
+
+  public Person(String name, int timeToCross) {
+    this.timeToCross = timeToCross;
+    this.name = name;
+  }
+
+  public void tryToMove (Bridge bridge) {
+    if (side == bridge.torchSide) {
+      if (!Verify.getBoolean()) {
+        if (! (bridge.isFull() || bridge.isPersonOnBridge(this))){
+          bridge.putPersonOnBridge(this);
+        }
+      }
+    }
+  }
+  
+  @Override
+  public String toString(){
+    return name;
+  }
+}
+
+public class Crossing {
+
+  Bridge bridge;
+  Person[] persons;
+  int elapsedTime;
+  Side initialSide;
+  
+  public Crossing (Person[] persons, Side initialSide){
+    this.persons = persons;
+    for (Person p : persons){
+      p.side = initialSide;
+    }
+    
+    this.bridge = new Bridge( initialSide);
+    this.initialSide = initialSide;
+  }
+  
+  boolean haveAllPersonsCrossed (){
+    for (Person p : persons){
+      if (p.side == initialSide){
+        return false;
+      }
+    }
+    
+    return true;
+  }
+  
+  void solve (){
+    printPersons();
+    System.out.println();
+    printCrossingState();
+    //Verify.setCounter(0, Integer.MAX_VALUE);
+      
+    while (!haveAllPersonsCrossed()){
+      for (Person p : persons){
+        p.tryToMove(bridge);
+      }
+
+      if (bridge.arePersonsOnBridge()) {
+        elapsedTime += bridge.cross();
+
+        //Verify.ignoreIf(elapsedTime >= Verify.getCounter(0));
+        //Verify.ignoreIf(elapsedTime > 17); //with this DFS will also find error
+
+        bridge.clearBridge();
+      }
+      
+      printCrossingState();      
+    }
+    
+    
+    System.out.println("total time = " + elapsedTime);
+    
+    //Verify.setCounter(0, elapsedTime); // new minimum
+    Verify.printPathOutput("done");
+    Verify.storeTraceAndTerminateIf(elapsedTime == 17, null, null);
+    //assert (elapsedTime > 17);
+  }
+  
+  String personsOnSide (Side side){
+    int n=0;
+    StringBuilder sb = new StringBuilder();
+    for (Person p : persons){
+      if (p.side == side){
+        if (n++ > 0){
+          sb.append(',');
+        }
+        sb.append( p);
+      }
+    }
+    return sb.toString();
+  }
+  
+  String torchSymbol (Side side){
+    if (bridge.torchSide == side){
+      return "*";
+    } else {
+      return " ";
+    }
+  }
+  
+  void printPersons(){
+    for (Person p : persons){
+      System.out.printf("%10s needs %2d min to cross\n", p.name, p.timeToCross);
+    }
+  }
+  
+  void printCrossingState (){
+    System.out.printf("%20s %s====%s %-20s : elapsed time=%d\n",
+            personsOnSide(Side.LEFT), torchSymbol(Side.LEFT),
+            torchSymbol(Side.RIGHT), personsOnSide(Side.RIGHT), elapsedTime);
+  }
+  
+  public static void main(String[] args) {
+    Person[] persons = {
+      new Person("Bill", 1),
+      new Person("Xoe", 2),
+      new Person("Sue", 5),
+      new Person("Joe", 10)
+    };
+
+    Crossing crossing = new Crossing( persons, Side.LEFT);
+    crossing.solve();
+  }
+}
diff --git a/src/examples/Crossing.jpf b/src/examples/Crossing.jpf
new file mode 100644 (file)
index 0000000..fb4d186
--- /dev/null
@@ -0,0 +1,5 @@
+target = Crossing
+
+vm.tree_output = false 
+vm.path_output = true
+search.class = .search.heuristic.BFSHeuristic
diff --git a/src/examples/DiningPhil.java b/src/examples/DiningPhil.java
new file mode 100644 (file)
index 0000000..7295a5a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+public class DiningPhil {
+
+  static class Fork {
+  }
+
+  static class Philosopher extends Thread {
+
+    Fork left;
+    Fork right;
+
+    public Philosopher(Fork left, Fork right) {
+      this.left = left;
+      this.right = right;
+      //start();
+    }
+
+    @Override
+       public void run() {
+      // think!
+      synchronized (left) {
+        synchronized (right) {
+          // eat!
+        }
+      }
+    }
+  }
+  
+  static int nPhilosophers = 6;
+
+  public static void main(String[] args) {
+    if (args.length > 0){
+      nPhilosophers = Integer.parseInt(args[0]);
+    }
+    
+    //Verify.beginAtomic();
+    Fork[] forks = new Fork[nPhilosophers];
+    for (int i = 0; i < nPhilosophers; i++) {
+      forks[i] = new Fork();
+    }
+    for (int i = 0; i < nPhilosophers; i++) {
+      Philosopher p = new Philosopher(forks[i], forks[(i + 1) % nPhilosophers]);
+      p.start();
+    }
+    //Verify.endAtomic();
+  }
+}
\ No newline at end of file
diff --git a/src/examples/DiningPhil.jpf b/src/examples/DiningPhil.jpf
new file mode 100644 (file)
index 0000000..901b818
--- /dev/null
@@ -0,0 +1,3 @@
+target = DiningPhil
+
+search.class = .search.heuristic.BFSHeuristic
diff --git a/src/examples/HelloWorld.java b/src/examples/HelloWorld.java
new file mode 100644 (file)
index 0000000..1be027e
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * you guess what that one does - just like a normal VM
+ */
+public class HelloWorld {
+  public static void main(String[] args){
+    System.out.println("I won't say it!");
+  }
+}
diff --git a/src/examples/HelloWorld.jpf b/src/examples/HelloWorld.jpf
new file mode 100644 (file)
index 0000000..8545924
--- /dev/null
@@ -0,0 +1 @@
+target=HelloWorld
\ No newline at end of file
diff --git a/src/examples/NumericValueCheck.java b/src/examples/NumericValueCheck.java
new file mode 100644 (file)
index 0000000..d99f092
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * example to catch numeric value rnge violations with the NumericValueChecker listener
+ */
+public class NumericValueCheck {
+  
+  public static void main (String[] args){
+    double someVariable;
+    
+    someVariable = 42;  // what else
+    
+    someVariable = 12345; // ..That's the number only an idiot would have on his luggage
+  }
+}
diff --git a/src/examples/NumericValueCheck.jpf b/src/examples/NumericValueCheck.jpf
new file mode 100644 (file)
index 0000000..6fa8a95
--- /dev/null
@@ -0,0 +1,10 @@
+target = NumericValueCheck
+
+listener = .listener.NumericValueChecker
+
+# NumericValueChecker configuration
+range.vars = 1
+range.1.var = NumericValueCheck.main(java.lang.String[]):someVariable
+range.1.min = 0
+range.1.max = 42
diff --git a/src/examples/Racer.java b/src/examples/Racer.java
new file mode 100644 (file)
index 0000000..227cfab
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+public class Racer implements Runnable {\r
+\r
+     int d = 42;\r
+\r
+     @Override
+       public void run () {\r
+          doSomething(1001);                   // (1)\r
+          d = 0;                               // (2)\r
+     }\r
+\r
+     public static void main (String[] args){\r
+          Racer racer = new Racer();\r
+          Thread t = new Thread(racer);\r
+          t.start();\r
+\r
+          doSomething(1000);                   // (3)\r
+          int c = 420 / racer.d;               // (4)\r
+          System.out.println(c);\r
+     }\r
+     \r
+     static void doSomething (int n) {\r
+          // not very interesting..\r
+          try { Thread.sleep(n); } catch (InterruptedException ix) {}\r
+     }\r
+}\r
diff --git a/src/examples/Racer.jpf b/src/examples/Racer.jpf
new file mode 100644 (file)
index 0000000..5cfdfd2
--- /dev/null
@@ -0,0 +1,5 @@
+target = Racer
+
+listener=gov.nasa.jpf.listener.PreciseRaceDetector
+
+report.console.property_violation=error,trace
diff --git a/src/examples/Rand.java b/src/examples/Rand.java
new file mode 100644 (file)
index 0000000..4f80754
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+import java.util.Random;\r
+\r
+public class Rand {\r
+  public static void main (String[] args) {\r
+    System.out.println("computing c = a/(b+a - 2)..");\r
+    Random random = new Random(42);      // (1)\r
+\r
+    int a = random.nextInt(2);           // (2)\r
+    System.out.printf("a=%d\n", a);\r
+\r
+    //... lots of code here\r
+\r
+    int b = random.nextInt(3);           // (3)\r
+    System.out.printf("  b=%d       ,a=%d\n", b, a);\r
+\r
+    int c = a/(b+a -2);                  // (4)\r
+    System.out.printf("=>  c=%d     , b=%d, a=%d\n", c, b, a);         \r
+  }\r
+}\r
diff --git a/src/examples/Rand.jpf b/src/examples/Rand.jpf
new file mode 100644 (file)
index 0000000..904f74b
--- /dev/null
@@ -0,0 +1,4 @@
+target = Rand
+
+cg.enumerate_random = true
+report.console.property_violation=error,trace
\ No newline at end of file
diff --git a/src/examples/RobotManager-replay-nt.jpf b/src/examples/RobotManager-replay-nt.jpf
new file mode 100644 (file)
index 0000000..8664914
--- /dev/null
@@ -0,0 +1,7 @@
+target = RobotManager
+
+listener=.listener.ChoiceSelector,.listener.NullTracker
+
+choice.use_trace=trace
+
+cg.enumerate_random=true
diff --git a/src/examples/RobotManager-replay-ot.jpf b/src/examples/RobotManager-replay-ot.jpf
new file mode 100644 (file)
index 0000000..67aa617
--- /dev/null
@@ -0,0 +1,10 @@
+target = RobotManager
+
+listener=.listener.ChoiceSelector,.listener.ObjectTracker
+
+choice.use_trace=trace
+
+ot.refs = 0x160,0x185
+ot.log_fields = false
+
+cg.enumerate_random=true
diff --git a/src/examples/RobotManager.java b/src/examples/RobotManager.java
new file mode 100644 (file)
index 0000000..2bf025b
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is licensed under the
+ * Apache License, Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the License at
+ * 
+ *        http://www.apache.org/licenses/LICENSE-2.0. 
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and 
+ * limitations under the License.
+ */
+
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * a simplified version of the AWT RobotManager example
+ */
+public class RobotManager {
+
+  static class Robot {
+    String id;
+    
+    Robot (String id){
+      this.id = id;
+    }
+    
+    String getId(){
+      return id;
+    }
+    
+    String processSequence(String sequence){
+      System.out.printf("robot %s processing sequence: %s\n", id, sequence);
+      return "Ok";
+    }
+  }
+  
+  class StatusAcquisitionThread extends Thread {
+    boolean done;
+
+    public StatusAcquisitionThread () {
+      setDaemon(true);
+    }
+
+    @Override
+       public void run () {
+      int n = robotList.size();
+      Random random = new Random(0);
+
+      while (!done) {
+        int idx = random.nextInt(n);
+        Robot robot = robotList.get(idx);
+        setRobotOnline(robot, !isRobotOnline(robot.id));
+
+        try {
+          Thread.sleep(3000);
+        } catch (InterruptedException ix) {
+        }
+      }
+    }
+
+    public void terminate () {
+      done = true;
+    }
+  }
+  
+  List<Robot> robotList = new ArrayList<Robot>();
+  HashMap<String,Robot> onlineRobots = new HashMap<String,Robot>();
+  StatusAcquisitionThread acquisitionThread;
+
+  public RobotManager() {
+    robotList.add( new Robot("RATS-1"));
+    robotList.add( new Robot("RATS-2"));    
+    robotList.add( new Robot("RCAT-1"));
+    robotList.add( new Robot("POGO-1"));
+    
+    for (Robot r : robotList) {
+      setRobotOnline(r, true);
+    }
+  }
+  
+  public void setRobotOnline (Robot robot, boolean isOnline) {
+    if (isOnline) {
+      onlineRobots.put(robot.getId(), robot);
+    } else {
+      onlineRobots.remove(robot.getId());
+    }
+  }
+  
+  public boolean isRobotOnline (String robotName) {
+    return onlineRobots.containsKey(robotName);
+  }
+  
+  public Robot getOnlineRobot (String robotName) {
+    return onlineRobots.get(robotName);
+  }
+  
+  public String sendSequence(Robot robot, String sequence) {    
+    return robot.processSequence(sequence); 
+  }
+  
+  void startStatusAcquisitionThread (){
+    acquisitionThread = new StatusAcquisitionThread();
+    acquisitionThread.start();
+  }
+  
+  void stopStatusAcquisitionThread(){
+    acquisitionThread.terminate();
+  }
+  
+  void processInput (){
+    String robotName = "POGO-1";
+    String sequence = "left; go";
+    
+    if (isRobotOnline(robotName)){
+      Robot robot = getOnlineRobot( robotName);
+      String result = robot.processSequence(sequence);
+      System.out.printf("sent sequence \"%s\" to robot %s => %s\n", sequence, robotName, result);
+    } else {
+      System.out.print("robot not online: ");
+      System.out.println(robotName);
+    }
+  }
+  
+  public static void main (String[] args){
+    RobotManager robotManager = new RobotManager();
+    robotManager.startStatusAcquisitionThread();
+    robotManager.processInput();
+    //robotManager.stopStatusAcquisitionThread();
+  }
+}
diff --git a/src/examples/RobotManager.jpf b/src/examples/RobotManager.jpf
new file mode 100644 (file)
index 0000000..481e64b
--- /dev/null
@@ -0,0 +1,5 @@
+target = RobotManager
+
+listener=.listener.TraceStorer
+
+cg.enumerate_random=true
diff --git a/src/examples/StopWatch.java b/src/examples/StopWatch.java
new file mode 100644 (file)
index 0000000..8da5066
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * example to show how to explore off-nominal paths
+ */
+public class StopWatch {
+  
+  public static void main(String[] args){
+    long tStart = System.currentTimeMillis();
+    System.out.println("some lengthy computation..");
+    long tEnd = System.currentTimeMillis();
+    
+    if (tEnd - tStart > 1000){
+      throw new RuntimeException("it took too long..");
+    }
+    
+    System.out.println("all fine, finished in time");
+  }
+}
diff --git a/src/examples/StopWatch.jpf b/src/examples/StopWatch.jpf
new file mode 100644 (file)
index 0000000..2cabef9
--- /dev/null
@@ -0,0 +1,3 @@
+target = StopWatch
+
+listener = .listener.StopWatchFuzzer
diff --git a/src/examples/TestExample-coverage.jpf b/src/examples/TestExample-coverage.jpf
new file mode 100644 (file)
index 0000000..75d9ed4
--- /dev/null
@@ -0,0 +1,7 @@
+target = TestExample
+
+listener=.listener.CoverageAnalyzer
+
+coverage.include = T1,T2
+coverage.show_methods = true
+#coverage.show_bodies = true
\ No newline at end of file
diff --git a/src/examples/TestExample.java b/src/examples/TestExample.java
new file mode 100644 (file)
index 0000000..48aebdc
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+class T1 {
+  int d = 42;
+
+  public int func1(int a, int b) {
+    if (a > b) {
+      return 1;
+    } else if (a == b) {
+      return 0;
+    } else {
+      return -1;
+    }
+  }
+
+  public boolean func2(boolean cond) {
+    if (cond && (d > 40)) {
+      d--;
+    } else {
+      d++;
+    }
+    return cond;
+  }
+}
+
+class T2 {
+
+  public int computeSomething (int a, int b){
+    try {
+      return a / b;
+    } catch (ArithmeticException ax){
+      return -1; // pretty lame error handling
+    }
+  }
+
+  public void doSomething() {
+    System.out.println("something");
+  }
+}
+
+public class TestExample {
+
+  public static void main(String[] args) {
+    T1 t1 = new T1();
+
+    assert t1.func1(1, 0) > 0;
+    assert t1.func1(0, 1) < 0;
+
+    assert t1.func2(true) == true;
+    assert t1.func2(false) == false;
+
+
+    T2 t2 = new T2();
+
+    assert t2.computeSomething(42, 42) == 1.0;
+  }
+}
+
diff --git a/src/examples/oldclassic-da.jpf b/src/examples/oldclassic-da.jpf
new file mode 100644 (file)
index 0000000..b50256e
--- /dev/null
@@ -0,0 +1,10 @@
+# JPF properties to run the oldclassic example with DeadlockAnalyzer report
+
+target=oldclassic
+
+# turn off instruction trace
+report.console.property_violation=error,snapshot
+
+# deadlock analyzer is a property-specific trace report generator
+listener=.listener.DeadlockAnalyzer
+deadlock.format=essential
\ No newline at end of file
diff --git a/src/examples/oldclassic.java b/src/examples/oldclassic.java
new file mode 100644 (file)
index 0000000..7c10d6c
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+
+/**
+ * This example shows a deadlock that occurs as a result of a missed signal,
+ * i.e. a wait() that happens after the corresponding notify().
+ * 
+ * The defect is caused by a violated monitor encapsulation, i.e. directly
+ * accessing monitor internal data ('Event.count') from concurrent clients
+ * ('FirstTask', 'SecondTask'), without synchronization with the
+ * corresponding monitor operations ('wait_for-Event()' and 'signalEvent()').
+ * 
+ * The resulting race is typical for unsafe optimizations that try to 
+ * avoid expensive blocking calls by means of local caches
+ * 
+ * This example was inspired by a defect found in the "Remote Agent" 
+ * spacecraft controller that flew on board of "Deep Space 1", as described
+ * in: 
+ * 
+ *   Model Checking Programs
+ *   W. Visser, K. Havelund, G. Brat, S. Park and F. Lerda
+ *   Automated Software Engineering Journal
+ *   Volume 10, Number 2, April 2003
+ *  
+ * @author wvisser
+ */
+
+//------- the test driver
+public class oldclassic {
+  public static void main (String[] args) {
+    Event      new_event1 = new Event();
+    Event      new_event2 = new Event();
+
+    FirstTask  task1 = new FirstTask(new_event1, new_event2);
+    SecondTask task2 = new SecondTask(new_event1, new_event2);
+
+    task1.start();
+    task2.start();
+  }
+}
+
+//------- shared objects implemented as monitors
+class Event {
+  int count = 0;
+
+  public synchronized void signal_event () {
+    
+    // NOTE: this abstraction is not strictly required - even if the state space would
+    // be unbound, JPF could still find the error at a reasonable search depth,
+    // unless it's left-most branch in the search tree is unbound. If it is,
+    // there are two ways to work around: (1) use a different search strategy
+    // (e.g. HeuristicSearch with BFSHeuristic), or (2) set a random choice
+    // enumeration order ("+cg.randomize_choices=true"). In this example, (2)
+    // works just fine
+    count = (count + 1) % 3;
+    //count++;  // requires "+cg.randomize_choices=true" for DFSearch policy
+    
+    notifyAll();
+  }
+
+  public synchronized void wait_for_event () {
+    try {
+      wait();
+    } catch (InterruptedException e) {
+    }
+  }
+}
+
+//------- the two concurrent threads using the monitors
+class FirstTask extends Thread {
+  Event event1;
+  Event event2;
+  int   count = 0;  // bad optimization - local cache of event1 internals
+
+  public FirstTask (Event e1, Event e2) {
+    this.event1 = e1;
+    this.event2 = e2;
+  }
+
+  @Override
+  public void run () {
+    count = event1.count;          // <race> violates event1 monitor encapsulation
+
+    while (true) {
+      System.out.println("1");
+
+      if (count == event1.count) { // <race> ditto
+        event1.wait_for_event();
+      }
+
+      count = event1.count;        // <race> ditto
+      event2.signal_event();       // updates event2.count
+    }
+  }
+}
+
+class SecondTask extends Thread {
+  Event event1;
+  Event event2;
+  int   count = 0;  // bad optimization - local cache of event2 internals
+
+  public SecondTask (Event e1, Event e2) {
+    this.event1 = e1;
+    this.event2 = e2;
+  }
+
+  @Override
+  public void run () {
+    count = event2.count;          // <race> violates event2 monitor encapsulation
+
+    while (true) {
+      System.out.println("  2");
+      event1.signal_event();       // updates event1.count
+
+      if (count == event2.count) { // <race> ditto
+        event2.wait_for_event();
+      }
+
+      count = event2.count;        // <race> ditto
+    }
+  }
+}
diff --git a/src/examples/oldclassic.jpf b/src/examples/oldclassic.jpf
new file mode 100644 (file)
index 0000000..ea4b55a
--- /dev/null
@@ -0,0 +1,5 @@
+# JPF properties to run the oldclassic example with DeadlockAnalyzer report
+
+target=oldclassic
+
+report.console.property_violation=error,snapshot,trace
diff --git a/src/main/gov/nasa/jpf/$coreTag.java b/src/main/gov/nasa/jpf/$coreTag.java
new file mode 100644 (file)
index 0000000..2b9aca9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * if this class can be loaded, the JPF core is in the CLASSPATH.
+ * 
+ * The reason to use an empty, non-instantiable tag class for this is to make
+ * sure we don't get conflicts between CLs - this class should not be used
+ * for anything but to check if it can be found
+ *
+ * NOTE - this class has to be reachable through the same CP entry like
+ * gov.nasa.jpf.JPF and gov.nasa.jpf.Config
+ */
+public final class $coreTag {
+  private $coreTag() {
+    // nobody can call this
+  }
+}
diff --git a/src/main/gov/nasa/jpf/Config.java b/src/main/gov/nasa/jpf/Config.java
new file mode 100644 (file)
index 0000000..947ff87
--- /dev/null
@@ -0,0 +1,2468 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.JPFSiteUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.logging.Logger;
+import java.util.regex.Pattern;
+
+
+/**
+ * class that encapsulates property-based JPF configuration. This is mainly an
+ * associative array with various typed accessors, and a structured
+ * initialization process. This implementation has the design constraint that it
+ * does not promote symbolic information to concrete types, which means that
+ * frequently accessed data should be promoted and cached in client classes.
+ * This in turn means we assume the data is not going to change at runtime.
+ * Major motivation for this mechanism is to avoid 'Option' classes that have
+ * concrete type fields, and hence are structural bottlenecks, i.e. every
+ * parameterized user extension (Heuristics, Scheduler etc.) require to update
+ * this single class. Note that Config is also not thread safe with respect to
+ * retrieving exceptions that occurred during instantiation
+ *
+ * Another important caveat for both implementation and usage of Config is that
+ * it is supposed to be our master configuration mechanism, i.e. it is also used
+ * to configure other core services like logging. This means that Config
+ * initialization should not depend on these services. Initialization has to
+ * return at all times, recording potential problems for later handling. This is
+ * why we have to keep the Config data model and initialization fairly simple
+ * and robust.
+ *
+ * Except of JPF and Config itself, all JPF classes are loaded by a
+ * Classloader that is constucted by Config (e.g. by collecting jars from
+ * known/configured locations), i.e. we SHOULD NOT rely on any 3rd party
+ * libraries within Config. The class should be as autarkical as possible.
+ *
+ *
+ * PROPERTY SOURCES
+ * ----------------
+ *
+ * (1) one site.properties - this file specifies the location of the jpf-core and
+ * installed extensions, like:
+ *
+ *     jpf-core = /Users/pcmehlitz/projects/jpf/jpf-core
+ *     ...
+ *     jpf-numeric = /Users/pcmehlitz/projects/jpf/jpf-numeric
+ *     ...
+ *     extensions = ${jpf-core}
+ *
+ * Each key/directory that is in site.properties is used to locate a corresponding
+ * project property (jpf.properties) file
+ *
+ * (2) any number of jpf.properties project properties files - each directory
+ * entry in the 'extensions' list is checked for a jpf.properties file, which 
+ * is automatically loaded if found. Project properties mostly contain path 
+ * settings that are used to initialize class loading by the host VM and JPF
+ *
+ * (3) one *.jpf application properties - this specifies all the settings for a
+ * specific JPF run, esp. listener and target/target.args.
+ * app properties can be specified as the sole JPF argument, i.e. instead of
+ * a SUT classname
+ *     ..
+ *     target = x.Y.MySystemUnderTest
+ *     target.args = one,two
+ *     ..
+ *     listener = z.MyListener
+ *
+ * (4) commandline properties - all start with '+', they can override all other props
+ *
+ *
+ * LOOKUP ORDER
+ * ------------
+ *                       property lookup
+ *   property type   :      spec             :  default
+ *   ----------------:-----------------------:----------
+ * |  site           :   +site               : "${user.home}/[.]jpf/site.properties"
+ * |                 :                       :
+ * |  project        :  'extensions' value   : set in site.properties
+ * |                 :                       :
+ * |  app            :   +app                : -
+ * |                 :                       :
+ * v  cmdline        :   +<key>=<val>        : -
+ *
+ * * if there is an explicit spec and the pathname does not exist, throw a
+ * JPFConfigException
+ *
+ * * if the system properties cannot be found, throw a JPFConfigException
+ *
+ *
+ * <2do> need to make NumberFormatException handling consistent - should always
+ * throw an JPFConfigException, not silently returning the default value
+ *
+ */
+
+
+@SuppressWarnings("serial")
+public class Config extends Properties {
+
+  static final char   KEY_PREFIX = '@';
+  public static final String REQUIRES_KEY = "@requires";
+  public static final String INCLUDE_KEY = "@include";
+  public static final String INCLUDE_UNLESS_KEY = "@include_unless";
+  public static final String INCLUDE_IF_KEY = "@include_if";
+  public static final String USING_KEY = "@using";
+
+  static final String[] EMPTY_STRING_ARRAY = new String[0];
+
+  public static final String LIST_SEPARATOR = ",";
+  public static final String PATH_SEPARATOR = ","; // the default for automatic appends
+
+  public static final Class<?>[] CONFIG_ARGTYPES = { Config.class };  
+  public static final Class<?>[] NO_ARGTYPES = new Class<?>[0];
+  public static final Object[] NO_ARGS = new Object[0];
+
+  public static final String TRUE = "true";
+  public static final String FALSE = "false";
+  
+  static final String MAX = "MAX";
+
+  static final String IGNORE_VALUE = "-";
+
+  // maximum number of processes for distributed applications
+  public static int MAX_NUM_PRC = 16;
+
+  // do we want to log the config init
+  public static boolean log = false;
+
+  // bad - a control exception
+  static class MissingRequiredKeyException extends RuntimeException {
+    MissingRequiredKeyException(String details){
+      super(details);
+    }
+  }
+
+  // it seems bad design to keep ClassLoader management in a glorified Properties object,
+  // but a lot of what Config does is to resolve configured types, for which we need
+  // control over the loader that is used for resolution
+  ClassLoader loader = Config.class.getClassLoader();
+    
+  // where did we initialize from
+  ArrayList<Object> sources = new ArrayList<Object>();
+  
+  ArrayList<ConfigChangeListener> changeListeners;
+  
+  // Properties are simple Hashmaps, but we want to maintain the order of entries
+  LinkedList<String> entrySequence = new LinkedList<String>();
+
+  // an [optional] hashmap to keep objects we want to be singletons
+  HashMap<String,Object> singletons;
+  
+  public final Object[] CONFIG_ARGS = { this };
+
+  // the original command line args that were passed into the constructor
+  String[] args;
+  
+  // non-property/option command line args (starting from the first arg that is not prepened by '-','+')
+  String[] freeArgs;
+
+  /**
+   * the standard Config constructor that processes the whole properties stack
+   */
+  public Config (String[] cmdLineArgs)  {
+    args = cmdLineArgs;
+    String[] a = cmdLineArgs.clone(); // we might nullify some of them
+
+    // we need the app properties (*.jpf) pathname upfront because it might define 'site'
+    String appProperties = getAppPropertiesLocation(a);
+
+    //--- the site properties
+    String siteProperties = getSitePropertiesLocation( a, appProperties);
+    if (siteProperties != null){
+      loadProperties( siteProperties);
+    }
+
+    //--- get the project properties from current dir + site configured extensions
+    loadProjectProperties();
+
+    //--- the application properties
+    if (appProperties != null){
+      loadProperties( appProperties);
+    }
+
+    //--- at last, the (rest of the) command line properties
+    loadArgs(a);
+
+    // note that global path collection now happens from initClassLoader(), to
+    // accommodate for deferred project initialization when explicitly setting Config entries
+
+    //printEntries();
+  }
+
+  private Config() {
+    // just interal, for reloading
+  }
+  
+  /**
+   * single source Config constructor (does not process stack)
+   * @param fileName - single properties filename to initialize from 
+   */
+  public Config (String fileName){
+    loadProperties(fileName);
+  }
+
+  public Config (Reader in){
+    try {
+      load(in);
+    } catch (IOException iox){
+      exception("error reading data: " + iox);
+    }
+  }
+  
+  public static void enableLogging (boolean enableLogging){
+    log = enableLogging;
+  }
+
+  public void log (String msg){
+    if (log){ // very simplisitc, but we might do more in the future
+      System.out.println(msg);
+    }
+  }
+
+
+  String getAppPropertiesLocation(String[] args){
+    String path = null;
+
+    path = getPathArg(args, "app");
+    if (path == null){
+      // see if the first free arg is a *.jpf
+      path = getAppArg(args);
+    }
+    
+    put("jpf.app", path);
+
+    return path;
+  }
+
+  String getSitePropertiesLocation(String[] args, String appPropPath){
+    String path = getPathArg(args, "site");
+
+    if (path == null){
+      // look into the app properties
+      // NOTE: we might want to drop this in the future because it constitutes
+      // a cyclic properties file dependency
+      if (appPropPath != null){
+        path = JPFSiteUtils.getMatchFromFile(appPropPath,"site");
+      }
+
+      if (path == null) {
+        File siteProps = JPFSiteUtils.getStandardSiteProperties();
+        if (siteProps != null){
+          path = siteProps.getAbsolutePath();
+        }
+      }
+    }
+    
+    put("jpf.site", path);
+
+    return path;
+  }
+
+
+  // watch out - this does not reset the computed paths!
+  public Config reload() {
+    log("reloading config");
+
+    // just reload all our sources
+    Config newConfig = new Config();
+    for (Object src : sources){
+      if (src instanceof File) {
+        newConfig.loadProperties(((File)src).getPath());
+      } else if (src instanceof URL) {
+        newConfig.loadProperties((URL)src);
+      } else {
+        log("don't know how to reload: " + src);
+      }
+    }
+
+    // now reload command line args on top of that
+    newConfig.loadArgs(args);
+    newConfig.args = args;
+    
+    return newConfig;
+  }
+
+  public String[] getArgs() {
+    return args;
+  }
+
+  /*
+   * note that matching args are expanded and stored here, to avoid any
+   * discrepancy with value expansions (which are order-dependent)
+   */
+  protected String getPathArg (String[] args, String key){
+    int keyLen = key.length();
+
+    for (int i=0; i<args.length; i++){
+      String a = args[i];
+      if (a != null){
+        int len = a.length();
+        if (len > keyLen + 2){
+          if (a.charAt(0) == '+' && a.charAt(keyLen+1) == '='){
+            if (a.substring(1, keyLen+1).equals(key)){
+              String val = expandString(key, a.substring(keyLen+2));
+              args[i] = null; // processed
+              return val;
+            }
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+  /*
+   * if the first freeArg is a JPF application property filename, use this
+   * as targetArg and set the "jpf.app" property accordingly
+   */
+  protected String getAppArg (String[] args){
+
+    for (int i=0; i<args.length; i++){
+      String a = args[i];
+      if (a != null && a.length() > 0){
+        switch (a.charAt(0)) {
+          case '+': continue;
+          case '-': continue;
+          default:
+            if (a.endsWith(".jpf")){
+              String val = expandString("jpf.app", a);
+              args[i] = null; // processed
+              return val;
+            }
+        }
+      }
+    }
+
+    return null;
+  }
+
+
+  protected void loadProperties (URL url){
+    log("loading defaults from: " + url);
+
+    InputStream is = null;
+    try {
+      is = url.openStream();
+      load(is);
+      sources.add(url);
+    } catch (IOException iox){
+      log("error in input stream for: " + url + " : " + iox.getMessage());
+    } finally {
+      if (is != null){
+        try {
+          is.close();
+        } catch (IOException iox1){
+          log("error closing input stream for: " + url + " : " + iox1.getMessage());
+        }
+      }
+    }
+  }
+
+  protected void setConfigPathProperties (String fileName){
+    put("config", fileName);
+    int i = fileName.lastIndexOf(File.separatorChar);
+    if (i>=0){
+      put("config_path", fileName.substring(0,i));
+    } else {
+      put("config_path", ".");
+    }
+  }
+
+
+  protected boolean loadProperties (String fileName) {
+    if (fileName != null && fileName.length() > 0) {
+      FileInputStream is = null;
+      try {
+        File f = new File(fileName);
+        if (f.isFile()) {
+          log("loading property file: " + fileName);
+
+          setConfigPathProperties(f.getAbsolutePath());
+          sources.add(f);
+          is = new FileInputStream(f);
+          load(is);
+          return true;
+        } else {
+          throw exception("property file does not exist: " + f.getAbsolutePath());
+        }
+      } catch (MissingRequiredKeyException rkx){
+        // Hmpff - control exception
+        log("missing required key: " + rkx.getMessage() + ", skipping: " + fileName);
+      } catch (IOException iex) {
+        throw exception("error reading properties: " + fileName);
+      } finally {
+        if (is != null){
+          try {
+            is.close();
+          } catch (IOException iox1){
+            log("error closing input stream for file: " + fileName);
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+
+  /**
+   * this holds the policy defining in which order we process directories
+   * containing JPF projects (i.e. jpf.properties files)
+   */
+  protected void loadProjectProperties () {
+    // this is the list of directories holding jpf.properties files that
+    // have to be processed in order of entry (increasing priority)
+    LinkedList<File> jpfDirs = new LinkedList<File>();
+
+    // deduce the JPF projects in use (at least jpf-core) from the CL which
+    // defined this class
+    addJPFdirsFromClasspath(jpfDirs);
+
+    // add all the site configured extension dirs (but NOT jpf-core)
+    addJPFdirsFromSiteExtensions(jpfDirs);
+
+    // add the current dir, which has highest priority (this might bump up
+    // a previous entry by reodering it - which includes jpf-core)
+    addCurrentJPFdir(jpfDirs);
+
+    // now load all the jpf.property files we found in these dirs
+    // (later loads can override previous settings)
+    for (File dir : jpfDirs){
+      loadProperties(new File(dir,"jpf.properties").getAbsolutePath());
+    }
+  }
+
+  protected void appendPath (String pathKey, String key, String configPath){
+    String[] paths = getStringArray(key);
+    if (paths != null){
+      for (String e : paths) {
+        if (!e.startsWith("${") || !e.startsWith(File.separator)) {
+          e = configPath + File.separatorChar + e;
+        }
+        append(pathKey, e, PATH_SEPARATOR);
+      }
+    }
+  }
+
+  protected void addJPFdirs (List<File> jpfDirs, File dir){
+    while (dir != null) {
+      File jpfProp = new File(dir, "jpf.properties");
+      if (jpfProp.isFile()) {
+        registerJPFdir(jpfDirs, dir);
+        return;       // we probably don't want recursion here
+      }
+      dir = getParentFile(dir);
+    }
+  }
+
+  /**
+   * add the current dir to the list of JPF components.
+   * Note: this includes the core, so that we maintain the general
+   * principle that the enclosing project takes precedence (imagine the opposite:
+   * if we want to test a certain feature that is overridden by another extension
+   * we don't know about)
+   */
+  protected void addCurrentJPFdir(List<File> jpfDirs){
+    File dir = new File(System.getProperty("user.dir"));
+    while (dir != null) {
+      File jpfProp = new File(dir, "jpf.properties");
+      if (jpfProp.isFile()) {
+        registerJPFdir(jpfDirs, dir);
+        return;
+      }
+      dir = getParentFile(dir);
+    }
+  }
+
+  protected void addJPFdirsFromClasspath(List<File> jpfDirs) {
+    String cp = System.getProperty("java.class.path");
+    String[] cpEntries = cp.split(File.pathSeparator);
+
+    for (String p : cpEntries) {
+      File f = new File(p);
+      File dir = f.isFile() ? getParentFile(f) : f;
+
+      addJPFdirs(jpfDirs, dir);
+    }
+  }
+
+  protected void addJPFdirsFromSiteExtensions (List<File> jpfDirs){
+    String[] extensions = getCompactStringArray("extensions");
+    if (extensions != null){
+      for (String pn : extensions){
+        addJPFdirs( jpfDirs, new File(pn));
+      }
+    }
+  }
+
+  /**
+   * the obvious part is that it only adds to the list if the file is absent
+   * the not-so-obvious part is that it re-orders already present files
+   * to maintain the priority
+   */
+  protected boolean registerJPFdir(List<File> list, File dir){
+    try {
+      dir = dir.getCanonicalFile();
+
+      for (File e : list) {
+        if (e.equals(dir)) {
+          list.remove(e);
+          list.add(e);
+          return false;
+        }
+      }
+    } catch (IOException iox) {
+      throw new JPFConfigException("illegal path spec: " + dir);
+    }
+    
+    list.add(dir);
+    return true;
+  }
+
+  static File root = new File(File.separator);
+
+  protected File getParentFile(File f){
+    if (f == root){
+      return null;
+    } else {
+      File parent = f.getParentFile();
+      if (parent == null){
+        parent = new File(f.getAbsolutePath());
+
+        if (parent.getName().equals(root.getName())) {
+          return root;
+        } else {
+          return parent;
+        }
+      } else {
+        return parent;
+      }
+    }
+  }
+
+
+  /*
+   * argument syntax:
+   *          {'+'<key>['='<val>'] | '-'<driver-arg>} {<free-arg>}
+   *
+   * (1) null cmdLineArgs are ignored
+   * (2) all config cmdLineArgs start with '+'
+   * (3) if '=' is ommitted, a 'true' value is assumed
+   * (4) if <val> is ommitted, a 'null' value is assumed
+   * (5) no spaces around '='
+   * (6) all '-' driver-cmdLineArgs are ignored
+   */
+
+  protected void loadArgs (String[] cmdLineArgs) {
+
+    for (int i=0; i<cmdLineArgs.length; i++){
+      String a = cmdLineArgs[i];
+
+      if (a != null && a.length() > 0){
+        switch (a.charAt(0)){
+          case '+': // Config arg
+            processArg(a.substring(1));
+            break;
+
+          case '-': // driver arg, ignore
+            continue;
+
+          default:  // free (non property/option) cmdLineArgs to follow
+
+            int n = cmdLineArgs.length - i;
+            freeArgs = new String[n];
+            System.arraycopy(cmdLineArgs, i, freeArgs, 0, n);
+
+            return;
+        }
+      }
+    }
+  }
+
+
+  /*
+   * this does not include the '+' prefix, just the 
+   *     <key>[=[<value>]]
+   */
+  protected void processArg (String a) {
+
+    int idx = a.indexOf("=");
+
+    if (idx == 0){
+      throw new JPFConfigException("illegal option: " + a);
+    }
+
+    if (idx > 0) {
+      String key = a.substring(0, idx).trim();
+      String val = a.substring(idx + 1).trim();
+
+      if (val.length() == 0){
+        val = null;
+      }
+
+      setProperty(key, val);
+
+    } else {
+      setProperty(a.trim(), "true");
+    }
+
+  }
+
+
+  /**
+   * replace string constants with global static objects
+   */
+  protected String normalize (String v) {
+    if (v == null){
+      return null; // ? maybe TRUE - check default loading of "key" or "key="
+    }
+
+    // trim leading and trailing blanks (at least Java 1.4.2 does not take care of trailing blanks)
+    v = v.trim();
+    
+    // true/false
+    if ("true".equalsIgnoreCase(v)
+        || "yes".equalsIgnoreCase(v)
+        || "on".equalsIgnoreCase(v)) {
+      v = TRUE;
+    } else if ("false".equalsIgnoreCase(v)
+        || "no".equalsIgnoreCase(v)
+        || "off".equalsIgnoreCase(v)) {
+      v = FALSE;
+    }
+
+    // nil/null
+    if ("nil".equalsIgnoreCase(v) || "null".equalsIgnoreCase(v)){
+      v = null;
+    }
+    
+    return v;
+  }
+
+  
+  // our internal expander
+  // Note that we need to know the key this came from, to handle recursive expansion
+  protected String expandString (String key, String s) {
+    int i, j = 0;
+    if (s == null || s.length() == 0) {
+      return s;
+    }
+
+    while ((i = s.indexOf("${", j)) >= 0) {
+      if ((j = s.indexOf('}', i)) > 0) {
+        String k = s.substring(i + 2, j);
+        String v;
+        
+        if ((key != null) && key.equals(k)) {
+          // that's expanding itself -> use what is there
+          v = getProperty(key);
+        } else {
+          // refers to another key, which is already expanded, so this
+          // can't get recursive (we expand during entry storage)
+          v = getProperty(k);
+        }
+        
+        if (v == null) { // if we don't have it, fall back to system properties
+          v = System.getProperty(k);
+        }
+        
+        if (v != null) {
+          s = s.substring(0, i) + v + s.substring(j + 1, s.length());
+          j = i + v.length();
+        } else {
+          s = s.substring(0, i) + s.substring(j + 1, s.length());
+          j = i;
+        }
+      }
+    }
+
+    return s;    
+  }
+
+
+  boolean loadPropertiesRecursive (String fileName){
+    // save the current values of automatic properties
+    String curConfig = (String)get("config");
+    String curConfigPath = (String)get("config_path");
+
+    File propFile = new File(fileName);
+    if (!propFile.isAbsolute()){
+      propFile = new File(curConfigPath, fileName);
+    }
+    String absPath = propFile.getAbsolutePath();
+
+    if (!propFile.isFile()){
+      throw exception("property file does not exist: " + absPath);
+    }
+
+    boolean ret = loadProperties(absPath);
+
+    // restore the automatic properties
+    super.put("config", curConfig);
+    super.put("config_path", curConfigPath);
+
+    return ret;
+  }
+
+  void includePropertyFile(String key, String value){
+    value = expandString(key, value);
+    if (value != null && value.length() > 0){
+      loadPropertiesRecursive(value);
+    } else {
+      throw exception("@include pathname argument missing");
+    }
+  }
+
+  void includeCondPropertyFile(String key, String value, boolean keyPresent){
+    value = expandString(key, value);
+    if (value != null && value.length() > 0){
+      // check if it's a conditional "@include_unless/if = ?key?pathName"
+      if (value.charAt(0) == '?'){
+        int idx = value.indexOf('?', 1);
+        if (idx > 1){
+          String k = value.substring(1, idx);
+          if (containsKey(k) == keyPresent){
+            String v = value.substring(idx+1);
+            if (v.length() > 0){
+              loadPropertiesRecursive(v);
+            } else {
+              throw exception("@include_unless pathname argument missing (?<key>?<pathName>)");
+            }
+          }
+
+        } else {
+          throw exception("malformed @include_unless argument (?<key>?<pathName>), found: " + value);
+        }
+      } else {
+        throw exception("malformed @include_unless argument (?<key>?<pathName>), found: " + value);
+      }
+    } else {
+      throw exception("@include_unless missing ?<key>?<pathName> argument");
+    }
+  }
+
+
+  void includeProjectPropertyFile (String projectId){
+    String projectPath = getString(projectId);
+    if (projectPath != null){
+      File projectProps = new File(projectPath, "jpf.properties");
+      if (projectProps.isFile()){
+        loadPropertiesRecursive(projectProps.getAbsolutePath());
+
+      } else {
+        throw exception("project properties not found: " + projectProps.getAbsolutePath());
+      }
+
+    } else {
+      throw exception("unknown project id (check site.properties): " + projectId);
+    }
+  }
+
+  // we override this so that we can handle expansion for both key and value
+  // (value expansion can be recursive, i.e. refer to itself)
+  @Override
+  public Object put (Object keyObject, Object valueObject){
+
+    if (keyObject == null){
+      throw exception("no null keys allowed");
+    } else if (!(keyObject instanceof String)){
+      throw exception("only String keys allowed, got: " + keyObject);
+    }
+    if (valueObject != null && !(valueObject instanceof String)){
+      throw exception("only String or null values allowed, got: " + valueObject);
+    }
+
+    String key = (String)keyObject;
+    String value = (String)valueObject;
+
+    if (key.length() == 0){
+      throw exception("no empty keys allowed");
+    }
+
+    if (key.charAt(0) == KEY_PREFIX){
+      processPseudoProperty( key, value);
+      return null; // no value it replaces
+
+    } else {
+      // finally, a real key/value pair to add (or remove) - expand and store
+      String k = expandString(null, key);
+
+      if (!(value == null)) { // add or overwrite entry
+        String v = value;
+
+        if (k.charAt(k.length() - 1) == '+') { // the append hack
+          k = k.substring(0, k.length() - 1);
+          return append(k, v, null);
+
+        } else if (k.charAt(0) == '+') { // the prepend hack
+          k = k.substring(1);
+          return prepend(k, v, null);
+
+        } else { // normal value set
+          v = normalize(expandString(k, v));
+          if (v != null){
+            return setKey(k, v);
+          } else {
+            return removeKey(k);
+          }
+        }
+
+      } else { // setting a null value removes the entry
+        return removeKey(k);
+      }
+    }
+  }
+  
+  protected void processPseudoProperty( String key, String value){
+    if (REQUIRES_KEY.equals(key)) {
+      // shortcircuit loading of property files - used to enforce order
+      // of properties, e.g. to model dependencies
+      for (String reqKey : split(value)) {
+        if (!containsKey(reqKey)) {
+          throw new MissingRequiredKeyException(reqKey);
+        }
+      }
+
+    } else if (INCLUDE_KEY.equals(key)) {
+      includePropertyFile(key, value);
+      
+    } else if (INCLUDE_UNLESS_KEY.equals(key)) {
+      includeCondPropertyFile(key, value, false);
+      
+    } else if (INCLUDE_IF_KEY.equals(key)) {
+      includeCondPropertyFile(key, value, true);
+      
+    } else if (USING_KEY.equals(key)) {
+      // check if corresponding jpf.properties has already been loaded. If yes, skip
+      if (!haveSeenProjectProperty(value)){
+        includeProjectPropertyFile(value);
+      }
+      
+    } else {
+      throw exception("unknown keyword: " + key);
+    }
+  }
+
+  protected boolean haveSeenProjectProperty (String key){
+    String pn = getString(key);
+    if (pn == null){
+      return false;
+    } else {
+      return sources.contains( new File( pn, "jpf.properties"));
+    }
+  }
+  
+  private Object setKey (String k, String v){
+    Object oldValue = put0(k, v);
+    notifyPropertyChangeListeners(k, (String) oldValue, v);
+    return oldValue;
+  }
+
+  private Object removeKey (String k){
+    Object oldValue = super.get(k);
+    remove0(k);
+    notifyPropertyChangeListeners(k, (String) oldValue, null);
+    return oldValue;
+  }
+
+  private Object put0 (String k, Object v){
+    entrySequence.add(k);
+    return super.put(k, v);
+  }
+
+  private Object remove0 (String k){
+    entrySequence.add(k);
+    return super.remove(k);
+  }
+
+  public String prepend (String key, String value, String separator) {
+    String oldValue = getProperty(key);
+    value = normalize( expandString(key, value));
+
+    append0(key, oldValue, value, oldValue, separator);
+
+    return oldValue;
+  }
+
+  public String append (String key, String value, String separator) {
+    String oldValue = getProperty(key);
+    value = normalize( expandString(key, value));
+
+    append0(key, oldValue, oldValue, value, separator);
+
+    return oldValue;
+  }
+
+
+  private void append0 (String key, String oldValue, String a, String b, String separator){
+    String newValue;
+
+    if (a != null){
+      if (b != null) {
+        StringBuilder sb = new StringBuilder(a);
+        if (separator != null) {
+          sb.append(separator);
+        }
+        sb.append(b);
+        newValue = sb.toString();
+
+      } else { // b==null : nothing to append
+        if (oldValue == a){ // using reference compare is intentional here
+          return; // no change
+        } else {
+          newValue = a;
+        }
+      }
+
+    } else { // a==null : nothing to append to
+      if (oldValue == b || b == null){  // using reference compare is intentional here
+        return; // no change
+      } else {
+        newValue = b;
+      }
+    }
+
+    // if we get here, we have a newValue that differs from oldValue
+    put0(key, newValue);
+    notifyPropertyChangeListeners(key, oldValue, newValue);
+  }
+
+  protected String append (String key, String value) {
+    return append(key, value, LIST_SEPARATOR); // append with our standard list separator
+  }
+
+  /**
+   * check if we have a key.index entry. If not, check the non-indexed key. If no
+   * key found return null
+   * This simplifies clients that can have process id indexed properties
+   */
+  public String getIndexableKey (String key, int index){
+    String k = key + '.' + index;
+    if (containsKey(k)){
+      return k;
+    } else {
+      if (containsKey(key)){
+        return key;
+      }
+    }
+    
+    return null; // neither indexed nor non-indexed key in dictionary
+  }
+
+  public void setClassLoader (ClassLoader newLoader){
+    loader = newLoader;
+  }
+
+  public ClassLoader getClassLoader (){
+    return loader;
+  }
+
+  public boolean hasSetClassLoader (){
+    return Config.class.getClassLoader() != loader;
+  }
+
+  public JPFClassLoader initClassLoader (ClassLoader parent) {
+    ArrayList<String> list = new ArrayList<String>();
+
+    // we prefer to call this here automatically instead of allowing
+    // explicit collectGlobalPath() calls because (a) this could not preserve
+    // initial path settings, and (b) setting it *after* the JPFClassLoader got
+    // installed won't work (would have to add URLs explicitly, or would have
+    // to create a new JPFClassLoader, which then conflicts with classes already
+    // defined by the previous one)
+    collectGlobalPaths();
+    if (log){
+      log("collected native_classpath=" + get("native_classpath"));
+      log("collected native_libraries=" + get("native_libraries"));
+    }
+
+
+    String[] cp = getCompactStringArray("native_classpath");
+    cp = FileUtils.expandWildcards(cp);
+    for (String e : cp) {
+      list.add(e);
+    }
+    URL[] urls = FileUtils.getURLs(list);
+
+    String[] nativeLibs = getCompactStringArray("native_libraries");
+
+    JPFClassLoader cl;
+    if (parent instanceof JPFClassLoader){ // no need to create a new one, just initialize
+      cl = (JPFClassLoader)parent;
+      for (URL url : urls){
+        cl.addURL(url);
+      }
+      cl.setNativeLibs(nativeLibs);
+      
+    } else {    
+      cl = new JPFClassLoader( urls, nativeLibs, parent);
+    }
+    
+    loader = cl;
+    return cl;
+  }
+
+  /**
+   * has to be called if 'native_classpath' gets explicitly changed
+   * USE WITH CARE - if this is messed up, it is hard to debug
+   */
+  public void updateClassLoader (){
+    if (loader != null && loader instanceof JPFClassLoader){
+      JPFClassLoader jpfCl = (JPFClassLoader)loader;
+            
+      ArrayList<String> list = new ArrayList<String>();
+      String[] cp = getCompactStringArray("native_classpath");
+      cp = FileUtils.expandWildcards(cp);
+      for (String e : cp) {
+        URL url = FileUtils.getURL(e);
+        jpfCl.addURL(url); // this does not add if already present
+      }
+
+      String[] nativeLibs = getCompactStringArray("native_libraries");
+      jpfCl.setNativeLibs(nativeLibs);
+    }
+  }
+  
+
+  //------------------------------ public methods - the Config API
+
+
+  public String[] getEntrySequence () {
+    // whoever gets this might add/append/remove items, so we have to
+    // avoid ConcurrentModificationExceptions
+    return entrySequence.toArray(new String[entrySequence.size()]);
+  }
+
+  public void addChangeListener (ConfigChangeListener l) {
+    if (changeListeners == null) {
+      changeListeners = new ArrayList<ConfigChangeListener>();
+      changeListeners.add(l);
+    } else {
+      if (!changeListeners.contains(l)) {
+        changeListeners.add(l);
+      }
+    }
+  }
+  
+  public void removeChangeListener (ConfigChangeListener l) {
+    if (changeListeners != null) {
+      changeListeners.remove(l);
+      
+      if (changeListeners.size() == 0) {
+        changeListeners = null;
+      }
+    }
+  }
+  
+  // this shouldn't really be public but only accessible to JPF
+  public void jpfRunTerminated() {
+    if (changeListeners != null) {
+      // note we can't use the standard list iterator here because the sole purpose
+      // of having this notification is to remove the listener from the list during its enumeration
+      // which would give us ConcurrentModificationExceptions
+      ArrayList<ConfigChangeListener> list = (ArrayList<ConfigChangeListener>)changeListeners.clone();
+      for (ConfigChangeListener l : list) {
+        l.jpfRunTerminated(this);
+      }
+    }
+  }
+  
+  public JPFException exception (String msg) {
+    String context = getString("config");
+    if (context != null){
+      msg = "error in " + context + " : " + msg;
+    }
+
+    return new JPFConfigException(msg);
+  }
+
+  public void throwException(String msg) {
+    throw new JPFConfigException(msg);
+  }
+
+  /**
+   * return any command line args that are not options or properties
+   * (this usually contains the application class and arguments)
+   */
+  public String[] getFreeArgs(){
+    return freeArgs;
+  } 
+
+  //--- special keys
+  
+  /*
+   * target and its associated keys (target.args, target.entry) are now
+   * just ordinary key/value pairs and only here as convenience methods
+   * for JPF drivers/shells so that you don't have to remember the key names
+   * 
+   * NOTE - this does only work for a SingleProcessVM, and only has the
+   * desired effect before the JPF object is created
+   */
+  
+  public void setTarget (String clsName) {
+    put("target", clsName);
+  }
+  public String getTarget(){
+    return getString("target");
+  }
+  
+  public void setTargetArgs (String[] args) {
+    StringBuilder sb = new StringBuilder();
+    int i=0;
+    for (String a : args){
+      if (i++ > 0){
+        sb.append(',');
+      }
+      sb.append(a);
+    }
+    put("target.args", sb.toString());
+  }
+  public String[] getTargetArgs(){
+    String[] a = getStringArray("target.args");
+    if (a == null){
+      return new String[0];
+    } else {
+      return a;
+    }
+  }
+  
+  public void setTargetEntry (String mthName) {
+    put("target.entry", mthName);
+  }
+  public String getTargetEntry(){
+    return getString("target.entry");
+  }
+  
+  
+  //----------------------- type specific accessors
+
+  public boolean getBoolean(String key) {
+    String v = getProperty(key);
+    return (v == TRUE);
+  }
+
+  public boolean getBoolean(String key, boolean def) {
+    String v = getProperty(key);
+    if (v != null) {
+      return (v == TRUE);
+    } else {
+      return def;
+    }
+  }
+
+  /**
+   * for a given <baseKey>, check if there are corresponding
+   * values for keys <baseKey>.0 ... <baseKey>.<maxSize>
+   * If a value is found, store it in an array at the respective index
+   *
+   * @param baseKey String with base key without trailing '.'
+   * @param maxSize maximum size of returned value array
+   * @return trimmed array with String values found in dictionary
+   */
+  public String[] getStringEnumeration (String baseKey, int maxSize) {
+    String[] arr = new String[maxSize];
+    int max=-1;
+
+    StringBuilder sb = new StringBuilder(baseKey);
+    sb.append('.');
+    int len = baseKey.length()+1;
+
+    for (int i=0; i<maxSize; i++) {
+      sb.setLength(len);
+      sb.append(i);
+
+      String v = getString(sb.toString());
+      if (v != null) {
+        arr[i] = v;
+        max = i;
+      }
+    }
+
+    if (max >= 0) {
+      max++;
+      if (max < maxSize) {
+        String[] a = new String[max];
+        System.arraycopy(arr,0,a,0,max);
+        return a;
+      } else {
+        return arr;
+      }
+    } else {
+      return null;
+    }
+  }
+
+  public String[] getKeysStartingWith (String prefix){
+    ArrayList<String> list = new ArrayList<String>();
+
+    for (Enumeration e = keys(); e.hasMoreElements(); ){
+      String k = e.nextElement().toString();
+      if (k.startsWith(prefix)){
+        list.add(k);
+      }
+    }
+
+    return list.toArray(new String[list.size()]);
+  }
+
+  public String[] getKeyComponents (String key){
+    return key.split("\\.");
+  }
+
+  public int[] getIntArray (String key) throws JPFConfigException {
+    String v = getProperty(key);
+
+    if (v != null) {
+      String[] sa = split(v);
+      int[] a = new int[sa.length];
+      int i = 0;
+      try {
+        for (; i<sa.length; i++) {
+          String s = sa[i];
+          int val;
+          if (s.startsWith("0x")){
+            val = Integer.parseInt(s.substring(2),16); 
+          } else {
+            val = Integer.parseInt(s);
+          }
+          a[i] = val;
+        }
+        return a;
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal int[] element in '" + key + "' = \"" + sa[i] + '"');
+      }
+    } else {
+      return null;
+    }
+  }
+  public int[] getIntArray (String key, int... defaultValues){
+    int[] val = getIntArray(key);
+    if (val == null){
+      return defaultValues;
+    } else {
+      return val;
+    }
+  }
+
+  public long getDuration (String key, long defValue) {
+    String v = getProperty(key);
+    if (v != null) {
+      long d = 0;
+
+      if (v.indexOf(':') > 0){
+        String[] a = v.split(":");
+        if (a.length > 3){
+          //log.severe("illegal duration: " + key + "=" + v);
+          return defValue;
+        }
+        int m = 1000;
+        for (int i=a.length-1; i>=0; i--, m*=60){
+          try {
+            int n = Integer.parseInt(a[i]);
+            d += m*n;
+          } catch (NumberFormatException nfx) {
+            throw new JPFConfigException("illegal duration element in '" + key + "' = \"" + v + '"');
+          }
+        }
+
+      } else {
+        try {
+          d = Long.parseLong(v);
+        } catch (NumberFormatException nfx) {
+          throw new JPFConfigException("illegal duration element in '" + key + "' = \"" + v + '"');
+        }
+      }
+
+      return d;
+    }
+
+    return defValue;
+  }
+
+  public int getInt(String key) {
+    return getInt(key, 0);
+  }
+
+  public int getInt(String key, int defValue) {
+    String v = getProperty(key);
+    if (v != null) {
+      if (MAX.equals(v)){
+        return Integer.MAX_VALUE;
+      } else {
+        try {
+          return Integer.parseInt(v);
+        } catch (NumberFormatException nfx) {
+          throw new JPFConfigException("illegal int element in '" + key + "' = \"" + v + '"');
+        }
+      }
+    }
+
+    return defValue;
+  }
+
+  public long getLong(String key) {
+    return getLong(key, 0L);
+  }
+
+  public long getLong(String key, long defValue) {
+    String v = getProperty(key);
+    if (v != null) {
+      if (MAX.equals(v)){
+        return Long.MAX_VALUE;
+      } else {
+        try {
+          return Long.parseLong(v);
+        } catch (NumberFormatException nfx) {
+          throw new JPFConfigException("illegal long element in '" + key + "' = \"" + v + '"');
+        }
+      }
+    }
+
+    return defValue;
+  }
+
+  public long[] getLongArray (String key) throws JPFConfigException {
+    String v = getProperty(key);
+
+    if (v != null) {
+      String[] sa = split(v);
+      long[] a = new long[sa.length];
+      int i = 0;
+      try {
+        for (; i<sa.length; i++) {
+          a[i] = Long.parseLong(sa[i]);
+        }
+        return a;
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal long[] element in " + key + " = " + sa[i]);
+      }
+    } else {
+      return null;
+    }
+  }
+
+  public long[] getLongArray (String key, long... defaultValues){
+    long[] val = getLongArray(key);
+    if (val != null){
+      return val;
+    } else {
+      return defaultValues;
+    } 
+  }
+
+  public float getFloat (String key) {
+    return getFloat(key, 0.0f);
+  }
+
+  public float getFloat (String key, float defValue) {
+    String v = getProperty(key);
+    if (v != null) {
+      try {
+        return Float.parseFloat(v);
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal float element in '" + key + "' = \"" + v + '"');
+      }
+    }
+
+    return defValue;
+  }
+  
+  public float[] getFloatArray (String key) throws JPFConfigException {
+    String v = getProperty(key);
+
+    if (v != null) {
+      String[] sa = split(v);
+      float[] a = new float[sa.length];
+      int i = 0;
+      try {
+        for (; i<sa.length; i++) {
+          a[i] = Float.parseFloat(sa[i]);
+        }
+        return a;
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal float[] element in " + key + " = " + sa[i]);
+      }
+    } else {
+      return null;
+    }
+  }
+  public float[] getFloatArray (String key, float... defaultValues){
+    float[] v = getFloatArray( key);
+    if (v != null){
+      return v;
+    } else {
+      return defaultValues;
+    }
+  }
+  
+  
+  public double getDouble (String key) {
+    return getDouble(key, 0.0);
+  }
+
+  public double getDouble (String key, double defValue) {
+    String v = getProperty(key);
+    if (v != null) {
+      try {
+        return Double.parseDouble(v);
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal double element in '" + key + "' = \"" + v + '"');
+      }
+    }
+
+    return defValue;
+  }
+
+  public double[] getDoubleArray (String key) throws JPFConfigException {
+    String v = getProperty(key);
+
+    if (v != null) {
+      String[] sa = split(v);
+      double[] a = new double[sa.length];
+      int i = 0;
+      try {
+        for (; i<sa.length; i++) {
+          a[i] = Double.parseDouble(sa[i]);
+        }
+        return a;
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal double[] element in " + key + " = " + sa[i]);
+      }
+    } else {
+      return null;
+    }
+  }
+  public double[] getDoubleArray (String key, double... defaultValues){
+    double[] v = getDoubleArray( key);
+    if (v != null){
+      return v;
+    } else {
+      return defaultValues;
+    }
+  }
+
+  public <T extends Enum<T>> T getEnum( String key, T[] values, T defValue){
+    String v = getProperty(key);
+
+    if (v != null){
+      for (T t : values){
+        if (v.equalsIgnoreCase(t.name())){
+          return t;
+        }
+      }
+      
+      throw new JPFConfigException("unknown enum value for " + key + " = " + v);
+      
+    } else {
+      return defValue;
+    }
+  }
+
+  public String getString(String key) {
+    return getProperty(key);
+  }
+
+  public String getString(String key, String defValue) {
+    String s = getProperty(key);
+    if (s != null) {
+      return s;
+    } else {
+      return defValue;
+    }
+  }
+
+  /**
+   * return memory size in bytes, or 'defValue' if not in dictionary. Encoding
+   * can have a 'M' or 'k' postfix, values have to be positive integers (decimal
+   * notation)
+   */
+  public long getMemorySize(String key, long defValue) {
+    String v = getProperty(key);
+    long sz = defValue;
+
+    if (v != null) {
+      int n = v.length() - 1;
+      try {
+        char c = v.charAt(n);
+
+        if ((c == 'M') || (c == 'm')) {
+          sz = Long.parseLong(v.substring(0, n)) << 20;
+        } else if ((c == 'K') || (c == 'k')) {
+          sz = Long.parseLong(v.substring(0, n)) << 10;
+        } else {
+          sz = Long.parseLong(v);
+        }
+
+      } catch (NumberFormatException nfx) {
+        throw new JPFConfigException("illegal memory size element in '" + key + "' = \"" + v + '"');
+      }
+    }
+
+    return sz;
+  }
+
+  public HashSet<String> getStringSet(String key){
+    String v = getProperty(key);
+    if (v != null && (v.length() > 0)) {
+      HashSet<String> hs = new HashSet<String>();
+      for (String s : split(v)) {
+        hs.add(s);
+      }
+      return hs;
+    }
+
+    return null;
+    
+  }
+  
+  public HashSet<String> getNonEmptyStringSet(String key){
+    HashSet<String> hs = getStringSet(key);
+    if (hs != null && hs.isEmpty()) {
+      return null;
+    } else {
+      return hs;
+    }
+  }
+    
+  public String[] getStringArray(String key) {
+    String v = getProperty(key);
+    if (v != null && (v.length() > 0)) {
+      return split(v);
+    }
+
+    return null;
+  }
+
+  public String[] getStringArray(String key, char[] delims) {
+    String v = getProperty(key);
+    if (v != null && (v.length() > 0)) {
+      return split(v,delims);
+    }
+
+    return null;
+  }
+
+  public String[] getCompactTrimmedStringArray (String key){
+    String[] a = getStringArray(key);
+
+    if (a != null) {
+      for (int i = 0; i < a.length; i++) {
+        String s = a[i];
+        if (s != null && s.length() > 0) {
+          a[i] = s.trim();
+        }
+      }
+
+      return removeEmptyStrings(a);
+
+    } else {
+      return EMPTY_STRING_ARRAY;
+    }
+  }
+
+  public String[] getCompactStringArray(String key){
+    return removeEmptyStrings(getStringArray(key));
+  }
+
+  
+  public String[] getStringArray(String key, String[] def){
+    String v = getProperty(key);
+    if (v != null && (v.length() > 0)) {
+      return split(v);
+    } else {
+      return def;
+    }
+  }
+
+  public static String[] removeEmptyStrings (String[] a){
+    if (a != null) {
+      int n = 0;
+      for (int i=0; i<a.length; i++){
+        if (a[i].length() > 0){
+          n++;
+        }
+      }
+
+      if (n < a.length){ // we have empty strings in the split
+        String[] r = new String[n];
+        for (int i=0, j=0; i<a.length; i++){
+          if (a[i].length() > 0){
+            r[j++] = a[i];
+            if (j == n){
+              break;
+            }
+          }
+        }
+        return r;
+
+      } else {
+        return a;
+      }
+    }
+
+    return null;
+  }
+
+
+  /**
+   * return an [optional] id part of a property value (all that follows the first '@')
+   */
+  String getIdPart (String key) {
+    String v = getProperty(key);
+    if ((v != null) && (v.length() > 0)) {
+      int i = v.indexOf('@');
+      if (i >= 0){
+        return v.substring(i+1);
+      }
+    }
+
+    return null;
+  }
+
+  public Class<?> asClass (String v) throws JPFConfigException {
+    if ((v != null) && (v.length() > 0)) {
+      v = stripId(v);
+      v = expandClassName(v);
+      try {
+        return loader.loadClass(v);
+      } catch (ClassNotFoundException cfx) {
+        throw new JPFConfigException("class not found " + v + " by classloader: " + loader);
+      } catch (ExceptionInInitializerError ix) {
+        throw new JPFConfigException("class initialization of " + v + " failed: " + ix,
+            ix);
+      }
+    }
+
+    return null;    
+  }
+      
+  public <T> Class<? extends T> getClass(String key, Class<T> type) throws JPFConfigException {
+    Class<?> cls = asClass( getProperty(key));
+    if (cls != null) {
+      if (type.isAssignableFrom(cls)) {
+        return cls.asSubclass(type);
+      } else {
+        throw new JPFConfigException("classname entry for: \"" + key + "\" not of type: " + type.getName());
+      }
+    }
+    return null;
+  }
+  
+    
+  public Class<?> getClass(String key) throws JPFConfigException {
+    return asClass( getProperty(key));
+  }
+  
+  public Class<?> getEssentialClass(String key) throws JPFConfigException {
+    Class<?> cls = getClass(key);
+    if (cls == null) {
+      throw new JPFConfigException("no classname entry for: \"" + key + "\"");
+    }
+
+    return cls;
+  }
+  
+  String stripId (String v) {
+    int i = v.indexOf('@');
+    if (i >= 0) {
+      return v.substring(0,i);
+    } else {
+      return v;
+    }
+  }
+
+  String getId (String v){
+    int i = v.indexOf('@');
+    if (i >= 0) {
+      return v.substring(i+1);
+    } else {
+      return null;
+    }
+  }
+
+  String expandClassName (String clsName) {
+    if (clsName != null && clsName.length() > 0 && clsName.charAt(0) == '.') {
+      return "gov.nasa.jpf" + clsName;
+    } else {
+      return clsName;
+    }
+  }
+
+  
+  public Class<?>[] getClasses(String key) throws JPFConfigException {
+    String[] v = getStringArray(key);
+    if (v != null) {
+      int n = v.length;
+      Class<?>[] a = new Class[n];
+      for (int i = 0; i < n; i++) {
+        String clsName = expandClassName(v[i]);
+        if (clsName != null && clsName.length() > 0){
+          try {
+            clsName = stripId(clsName);
+            a[i] = loader.loadClass(clsName);
+          } catch (ClassNotFoundException cnfx) {
+            throw new JPFConfigException("class not found " + v[i]);
+          } catch (ExceptionInInitializerError ix) {
+            throw new JPFConfigException("class initialization of " + v[i] + " failed: " + ix, ix);
+          }
+        }
+      }
+
+      return a;
+    }
+
+    return null;
+  }
+  
+  /**
+   * this one is used to instantiate objects from a list of keys that share
+   * the same prefix, e.g.
+   * 
+   *  shell.panels = config,site
+   *  shell.panels.site = .shell.panels.SitePanel
+   *  shell.panels.config = .shell.panels.ConfigPanel
+   *  ...
+   * 
+   * note that we specify default class names, not classes, so that the classes
+   * get loaded through our own loader at call time (they might not be visible
+   * to our caller)
+   */
+  public <T> T[] getGroupInstances (String keyPrefix, String keyPostfix, Class<T> type, 
+          String... defaultClsNames) throws JPFConfigException {
+    
+    String[] ids = getCompactTrimmedStringArray(keyPrefix);
+    
+    if (ids.length > 0){
+      keyPrefix = keyPrefix + '.';
+      T[] arr = (T[]) Array.newInstance(type, ids.length);
+      
+      for(int i = 0; i < ids.length; i++){
+        String key = keyPrefix + ids[i];
+        if (keyPostfix != null){
+          key = key + keyPostfix;
+        }
+        arr[i] = getEssentialInstance(key, type);
+      }
+      
+      return arr;
+      
+    } else {
+      T[] arr = (T[]) Array.newInstance(type, defaultClsNames.length);
+              
+      for (int i=0; i<arr.length; i++){
+        arr[i] = getInstance((String)null, defaultClsNames[i], type);
+        if (arr[i] == null){
+          exception("cannot instantiate default type " + defaultClsNames[i]);
+        }
+      }
+      
+      return arr;
+    }
+  }
+  
+  // <2do> - that's kind of kludged together, not very efficient
+  String[] getIds (String key) {
+    String v = getProperty(key);
+
+    if (v != null) {
+      int i = v.indexOf('@');
+      if (i >= 0) { // Ok, we have ids
+        String[] a = split(v);
+        String[] ids = new String[a.length];
+        for (i = 0; i<a.length; i++) {
+          ids[i] = getId(a[i]);
+        }
+        return ids;
+      }
+    }
+
+    return null;
+  }
+
+  public <T> ArrayList<T> getInstances(String key, Class<T> type) throws JPFConfigException {
+
+    Class<?>[] argTypes = { Config.class };
+    Object[] args = { this };
+
+    return getInstances(key,type,argTypes,args);
+  }
+  
+  public <T> ArrayList<T> getInstances(String key, Class<T> type, Class<?>[]argTypes, Object[] args)
+                                                      throws JPFConfigException {
+    Class<?>[] c = getClasses(key);
+
+    if (c != null) {
+      String[] ids = getIds(key);
+
+      ArrayList<T> a = new ArrayList<T>(c.length);
+
+      for (int i = 0; i < c.length; i++) {
+        String id = (ids != null) ? ids[i] : null;
+        T listener = getInstance(key, c[i], type, argTypes, args, id);
+        if (listener != null) {
+          a.add( listener);
+        } else {
+          // should report here
+        }
+      }
+
+      return a;
+      
+    } else {
+      // should report here
+    }
+
+    return null;
+  }
+  
+  public <T> T getInstance(String key, Class<T> type, String defClsName) throws JPFConfigException {
+    Class<?>[] argTypes = CONFIG_ARGTYPES;
+    Object[] args = CONFIG_ARGS;
+
+    Class<?> cls = getClass(key);
+    String id = getIdPart(key);
+
+    if (cls == null) {
+      try {
+        cls = loader.loadClass(defClsName);
+      } catch (ClassNotFoundException cfx) {
+        throw new JPFConfigException("class not found " + defClsName);
+      } catch (ExceptionInInitializerError ix) {
+        throw new JPFConfigException("class initialization of " + defClsName + " failed: " + ix, ix);
+      }
+    }
+    
+    return getInstance(key, cls, type, argTypes, args, id);
+  }
+
+  public <T> T getInstance(String key, Class<T> type) throws JPFConfigException {
+    Class<?>[] argTypes = CONFIG_ARGTYPES;
+    Object[] args = CONFIG_ARGS;
+
+    return getInstance(key, type, argTypes, args);
+  }
+    
+  public <T> T getInstance(String key, Class<T> type, Class<?>[] argTypes,
+                            Object[] args) throws JPFConfigException {
+    Class<?> cls = getClass(key);
+    String id = getIdPart(key);
+
+    if (cls != null) {
+      return getInstance(key, cls, type, argTypes, args, id);
+    } else {
+      return null;
+    }
+  }
+  
+  public <T> T getInstance(String key, Class<T> type, Object arg1, Object arg2)  throws JPFConfigException {
+    Class<?>[] argTypes = new Class<?>[2];
+    argTypes[0] = arg1.getClass();
+    argTypes[1] = arg2.getClass();
+
+    Object[] args = new Object[2];
+    args[0] = arg1;
+    args[1] = arg2;
+
+    return getInstance(key, type, argTypes, args);
+  }
+
+
+  public <T> T getEssentialInstance(String key, Class<T> type) throws JPFConfigException {
+    Class<?>[] argTypes = { Config.class };
+    Object[] args = { this };
+    return getEssentialInstance(key, type, argTypes, args);
+  }
+
+  /**
+   * just a convenience method for ctor calls that take two arguments
+   */
+  public <T> T getEssentialInstance(String key, Class<T> type, Object arg1, Object arg2)  throws JPFConfigException {
+    Class<?>[] argTypes = new Class<?>[2];
+    argTypes[0] = arg1.getClass();
+    argTypes[1] = arg2.getClass();
+
+    Object[] args = new Object[2];
+    args[0] = arg1;
+    args[1] = arg2;
+
+    return getEssentialInstance(key, type, argTypes, args);
+  }
+
+  public <T> T getEssentialInstance(String key, Class<T> type, Class<?>[] argTypes, Object[] args) throws JPFConfigException {
+    Class<?> cls = getEssentialClass(key);
+    String id = getIdPart(key);
+
+    return getInstance(key, cls, type, argTypes, args, id);
+  }
+
+  public <T> T getInstance (String id, String clsName, Class<T> type, Class<?>[] argTypes, Object[] args) throws JPFConfigException {
+    Class<?> cls = asClass(clsName);
+    
+    if (cls != null) {
+      return getInstance(id, cls, type, argTypes, args, id);
+    } else {
+      return null;
+    }
+  }
+  
+  public <T> T getInstance (String id, String clsName, Class<T> type) throws JPFConfigException {
+    Class<?>[] argTypes = CONFIG_ARGTYPES;
+    Object[] args = CONFIG_ARGS;
+
+    Class<?> cls = asClass(clsName);
+    
+    if (cls != null) {
+      return getInstance(id, cls, type, argTypes, args, id);
+    } else {
+      return null;
+    }
+  }
+  
+  /**
+   * this is our private instantiation workhorse - try to instantiate an object of
+   * class 'cls' by using the following ordered set of ctors 1. <cls>(
+   * <argTypes>) 2. <cls>(Config) 3. <cls>() if all of that fails, or there was
+   * a 'type' provided the instantiated object does not comply with, return null
+   */
+  <T> T getInstance(String key, Class<?> cls, Class<T> type, Class<?>[] argTypes,
+                     Object[] args, String id) throws JPFConfigException {
+    Object o = null;
+    Constructor<?> ctor = null;
+
+    if (cls == null) {
+      return null;
+    }
+
+    if (id != null) { // check first if we already have this one instantiated as a singleton
+      if (singletons == null) {
+        singletons = new HashMap<String,Object>();
+      } else {
+        o = type.cast(singletons.get(id));
+      }
+    }
+
+    while (o == null) {
+      try {
+        ctor = cls.getConstructor(argTypes);
+        o = ctor.newInstance(args);
+      } catch (NoSuchMethodException nmx) {
+         
+        if ((argTypes.length > 1) || ((argTypes.length == 1) && (argTypes[0] != Config.class))) {
+          // fallback 1: try a single Config param
+          argTypes = CONFIG_ARGTYPES;
+          args = CONFIG_ARGS;
+
+        } else if (argTypes.length > 0) {
+          // fallback 2: try the default ctor
+          argTypes = NO_ARGTYPES;
+          args = NO_ARGS;
+
+        } else {
+          // Ok, there is no suitable ctor, bail out
+          throw new JPFConfigException(key, cls, "no suitable ctor found");
+        }
+      } catch (IllegalAccessException iacc) {
+        throw new JPFConfigException(key, cls, "\n> ctor not accessible: "
+            + getMethodSignature(ctor));
+      } catch (IllegalArgumentException iarg) {
+        throw new JPFConfigException(key, cls, "\n> illegal constructor arguments: "
+            + getMethodSignature(ctor));
+      } catch (InvocationTargetException ix) {
+        Throwable tx = ix.getTargetException();
+        if (tx instanceof JPFConfigException) {
+          throw new JPFConfigException(tx.getMessage() + "\n> used within \"" + key
+              + "\" instantiation of " + cls);
+        } else {
+          throw new JPFConfigException(key, cls, "\n> exception in "
+              + getMethodSignature(ctor) + ":\n>> " + tx, tx);
+        }
+      } catch (InstantiationException ivt) {
+        throw new JPFConfigException(key, cls,
+            "\n> abstract class cannot be instantiated");
+      } catch (ExceptionInInitializerError eie) {
+        throw new JPFConfigException(key, cls, "\n> static initialization failed:\n>> "
+            + eie.getException(), eie.getException());
+      }
+    }
+
+    // check type
+    if (!type.isInstance(o)) {
+      throw new JPFConfigException(key, cls, "\n> instance not of type: "
+          + type.getName());
+    }
+
+    if (id != null) { // add to singletons (in case it's not already in there)
+      singletons.put(id, o);
+    }
+
+    return type.cast(o); // safe according to above
+  }
+
+  public String getMethodSignature(Constructor<?> ctor) {
+    StringBuilder sb = new StringBuilder(ctor.getName());
+    sb.append('(');
+    Class<?>[] argTypes = ctor.getParameterTypes();
+    for (int i = 0; i < argTypes.length; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      sb.append(argTypes[i].getName());
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+
+  public boolean hasValue(String key) {
+    String v = getProperty(key);
+    return ((v != null) && (v.length() > 0));
+  }
+
+  public boolean hasValueIgnoreCase(String key, String value) {
+    String v = getProperty(key);
+    if (v != null) {
+      return v.equalsIgnoreCase(value);
+    }
+
+    return false;
+  }
+
+  public int getChoiceIndexIgnoreCase(String key, String[] choices) {
+    String v = getProperty(key);
+
+    if ((v != null) && (choices != null)) {
+      for (int i = 0; i < choices.length; i++) {
+        if (v.equalsIgnoreCase(choices[i])) {
+          return i;
+        }
+      }
+    }
+
+    return -1;
+  }
+
+  public URL getURL (String key){
+    String v = getProperty(key);
+    if (v != null) {
+      try {
+        return FileUtils.getURL(v);
+      } catch (Throwable x){
+        throw exception("malformed URL: " + v);
+      }
+    } else {
+      return null;
+    }
+  }
+
+  public File[] getPathArray (String key) {    
+    String v = getProperty(key);
+    if (v != null) {
+      String[] pe = removeEmptyStrings( pathSplit(v));
+      
+      if (pe != null && pe.length > 0) {
+        File[] files = new File[pe.length];
+        for (int i=0; i<files.length; i++) {
+          String path = FileUtils.asPlatformPath(pe[i]);
+          files[i] = new File(path);
+        }
+        return files;
+      }      
+    }
+
+    return new File[0];
+  }
+
+  public File getPath (String key) {
+    String v = getProperty(key);
+    if (v != null) {
+      return new File(FileUtils.asPlatformPath(v));
+    }
+    
+    return null;
+  }
+
+  static final char[] UNIX_PATH_SEPARATORS = {',', ';', ':' };
+  static final char[] WINDOWS_PATH_SEPARATORS = {',', ';' };
+
+  protected String[] pathSplit (String input){
+    if (File.pathSeparatorChar == ':'){
+      return split( input, UNIX_PATH_SEPARATORS);
+    } else {
+      return split( input, WINDOWS_PATH_SEPARATORS);
+    }
+  }
+
+  static final char[] DELIMS = { ',', ';' };
+
+  /**
+   * our own version of split, which handles "`" quoting, and breaks on non-quoted
+   * ',' and ';' chars. We need this so that we can use ';' separated lists in
+   * JPF property files, but still can use quoted ';' if we absolutely have to
+   * specify Java signatures. On the other hand, we can't quote with '\' because
+   * that would make Windows paths even more terrible.
+   * regexes are bad at quoting, and this is more efficient anyways
+   */
+  protected String[] split (String input){
+    return split(input, DELIMS);
+  }
+
+  private boolean isDelim(char[] delim, char c){
+    for (int i=0; i<delim.length; i++){
+      if (c == delim[i]){
+        return true;
+      }
+    }
+    return false;
+  }
+
+  protected String[] split (String input, char[] delim){
+    int n = input.length();
+    ArrayList<String> elements = new ArrayList<String>();
+    boolean quote = false;
+
+    char[] buf = new char[128];
+    int k=0;
+
+    for (int i=0; i<n; i++){
+      char c = input.charAt(i);
+
+      if (!quote) {
+        if (isDelim(delim,c)){ // element separator
+          elements.add( new String(buf, 0, k));
+          k = 0;
+          continue;
+        } else if (c=='`') {
+          quote = true;
+          continue;
+        }
+      }
+
+      if (k >= buf.length){
+        char[] newBuf = new char[buf.length+128];
+        System.arraycopy(buf, 0, newBuf, 0, k);
+        buf = newBuf;
+      }
+      buf[k++] = c;
+      quote = false;
+    }
+
+    if (k>0){
+      elements.add( new String(buf, 0, k));
+    }
+
+    return elements.toArray(new String[elements.size()]);
+  }
+
+  static final String UNINITIALIZED = "uninitialized";
+  // this is where we store the initial values in case we have to recollect
+  String initialNativeClasspath = UNINITIALIZED, 
+          initialClasspath = UNINITIALIZED, 
+          initialSourcepath = UNINITIALIZED, 
+          initialPeerPackages = UNINITIALIZED,
+          initialNativeLibraries = UNINITIALIZED;
+  
+
+  /**
+   * this resets to what was explicitly set in the config files
+   */
+  public void resetGlobalPaths() {
+    if (initialNativeClasspath == UNINITIALIZED){
+      initialNativeClasspath = getString("native_classpath");
+    } else {
+      put0( "native_classpath", initialNativeClasspath);
+    }
+
+    if (initialClasspath == UNINITIALIZED){
+      initialClasspath = getString("classpath");
+    } else {
+      put0( "classpath", initialClasspath);
+    }
+    
+    if (initialSourcepath == UNINITIALIZED){
+      initialSourcepath = getString("sourcepath");
+    } else {
+      put0( "sourcepath", initialSourcepath);
+    }
+
+    if (initialPeerPackages == UNINITIALIZED){
+      initialPeerPackages = getString("peer_packages");
+    } else {
+      put0( "peer_packages", initialPeerPackages);
+    }
+
+    if (initialNativeLibraries == UNINITIALIZED){
+      initialNativeLibraries = getString("native_libraries");
+    } else {
+      put0( "native_libraries", initialNativeLibraries);
+    }
+  }
+
+  /**
+   * collect all the <project>.{native_classpath,classpath,sourcepath,peer_packages,native_libraries}
+   * and append them to the global settings
+   *
+   * NOTE - this is now called from within initClassLoader, which should only happen once and
+   * is the first time we really need the global paths.
+   *
+   * <2do> this is Ok for native_classpath and native_libraries, but we should probably do
+   * classpath, sourcepath and peer_packages separately (they can be collected later)
+   */
+  public void collectGlobalPaths() {
+        
+    // note - this is in the order of entry, i.e. reflects priorities
+    // we have to process this in reverse order so that later entries are prioritized
+    String[] keys = getEntrySequence();
+
+    String nativeLibKey = "." + System.getProperty("os.name") +
+            '.' + System.getProperty("os.arch") + ".native_libraries";
+
+    for (int i = keys.length-1; i>=0; i--){
+      String k = keys[i];
+      if (k.endsWith(".native_classpath")){
+        appendPath("native_classpath", k);
+        
+      } else if (k.endsWith(".classpath")){
+        appendPath("classpath", k);
+        
+      } else if (k.endsWith(".sourcepath")){        
+        appendPath("sourcepath", k);
+        
+      } else if (k.endsWith("peer_packages")){
+        append("peer_packages", getString(k), ",");
+        
+      } else if (k.endsWith(nativeLibKey)){
+        appendPath("native_libraries", k);
+      }
+    }
+  }
+
+  
+  static Pattern absPath = Pattern.compile("(?:[a-zA-Z]:)?[/\\\\].*");
+
+  void appendPath (String pathKey, String key){
+    String projName = key.substring(0, key.indexOf('.'));
+    String pathPrefix = null;
+
+    if (projName.isEmpty()){
+      pathPrefix = new File(".").getAbsolutePath();
+    } else {
+      pathPrefix = getString(projName);
+    }
+
+    if (pathPrefix != null){
+      pathPrefix += '/';
+
+      String[] elements = getCompactStringArray(key);
+      if (elements != null){
+        for (String e : elements) {
+          if (e != null && e.length()>0){
+
+            // if this entry is not an absolute path, or doesn't start with
+            // the project path, prepend the project path
+            if (!(absPath.matcher(e).matches()) && !e.startsWith(pathPrefix)) {
+              e = pathPrefix + e;
+            }
+
+            append(pathKey, e);
+          }
+        }
+      }
+
+    } else {
+      //throw new JPFConfigException("no project path for " + key);
+    }
+  }
+
+
+  //--- our modification interface
+
+  /**
+   * iterate over all keys, if a key starts with the provided keyPrefix, add
+   * this value under the corresponding key suffix. For example:
+   *
+   *  test.report.console.finished = result
+   *
+   *    -> prompotePropertyCategory("test.") ->
+   *
+   *  report.console.finished = result
+   *
+   * if a matching key has an IGNORE_VALUE value ("-"), the entry is *not* promoted
+   * (we need this to override promoted keys)
+   */
+  public void promotePropertyCategory (String keyPrefix){
+    int prefixLen = keyPrefix.length();
+    
+    // HashTable does not support adding elements while iterating over the entrySet 
+    ArrayList<Map.Entry<Object,Object>> promoted = null;
+    
+    for (Map.Entry<Object,Object> e : entrySet()){
+      Object k = e.getKey();
+      if (k instanceof String){
+        String key = (String)k;
+        if (key.startsWith(keyPrefix)){
+          Object v = e.getValue();
+          if (! IGNORE_VALUE.equals(v)){
+            if (promoted == null){
+              promoted = new ArrayList<Map.Entry<Object,Object>>();
+            }
+            promoted.add(e);
+          }
+        }
+      }
+    }
+    
+    if (promoted != null){
+      for (Map.Entry<Object, Object> e : promoted) {
+        String key = (String) e.getKey();
+        key = key.substring(prefixLen);
+
+        put(key, e.getValue());
+      }
+    }
+  }
+
+  
+  @Override
+  public Object setProperty (String key, String newValue) {    
+    Object oldValue = put(key, newValue);    
+    notifyPropertyChangeListeners(key, (String)oldValue, newValue);
+    return oldValue;
+  }
+
+  public void parse (String s) {
+    
+    int i = s.indexOf("=");
+    if (i > 0) {
+      String key, val;
+      
+      if (i > 1 && s.charAt(i-1)=='+') { // append
+        key = s.substring(0, i-1).trim();
+        val = s.substring(i+1); // it's going to be normalized anyways
+        append(key, val);
+        
+      } else { // put
+        key = s.substring(0, i).trim();
+        val = s.substring(i+1);
+        setProperty(key, val);
+      }
+      
+    }
+  }
+  
+  protected void notifyPropertyChangeListeners (String key, String oldValue, String newValue) {
+    if (changeListeners != null) {
+      for (ConfigChangeListener l : changeListeners) {
+        l.propertyChanged(this, key, oldValue, newValue);
+      }
+    }    
+  }
+  
+  public String[] asStringArray (String s){
+    return split(s);
+  }
+  
+  public TreeMap<Object,Object> asOrderedMap() {
+    TreeMap<Object,Object> map = new TreeMap<Object,Object>();
+    map.putAll(this);
+    return map;
+  }
+  
+  //--- various debugging methods
+
+  public void print (PrintWriter pw) {
+    pw.println("----------- Config contents");
+
+    // just how much do you have to do to get a printout with keys in alphabetical order :<
+    TreeSet<String> kset = new TreeSet<String>();
+    for (Enumeration<?> e = propertyNames(); e.hasMoreElements();) {
+      Object k = e.nextElement();
+      if (k instanceof String) {
+        kset.add( (String)k);
+      }
+    }
+
+    for (String key : kset) {
+      String val = getProperty(key);
+      pw.print(key);
+      pw.print(" = ");
+      pw.println(val);
+    }
+
+    pw.flush();
+  }
+
+  public void printSources (PrintWriter pw) {
+    pw.println("----------- Config sources");
+    for (Object src : sources){
+      pw.println(src);
+    }    
+  }
+  
+  public void printEntries() {
+    PrintWriter pw = new PrintWriter(System.out);
+    print(pw);
+  }
+
+  public String getSourceName (Object src){
+    if (src instanceof File){
+      return ((File)src).getAbsolutePath();
+    } else if (src instanceof URL){
+      return ((URL)src).toString();
+    } else {
+      return src.toString();
+    }
+  }
+  
+  public List<Object> getSources() {
+    return sources;
+  }
+  
+  public void printStatus(Logger log) {
+    int idx = 0;
+    
+    for (Object src : sources){
+      if (src instanceof File){
+        log.config("configuration source " + idx++ + " : " + getSourceName(src));
+      }
+    }
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/ConfigChangeListener.java b/src/main/gov/nasa/jpf/ConfigChangeListener.java
new file mode 100644 (file)
index 0000000..24a3cfc
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * listener for gov.nasa.jpf.Config changes. Implementors
+ * can register themselves upon initialization, to react to
+ * downstream changes even if they cache or process Config
+ * settings for increased performance.
+ * 
+ * the notification is per-key
+ */
+public interface ConfigChangeListener {
+
+  /**
+   * a JPF property was changed during runtime (e.g. by using the Verify API
+   * or encountering annotations)
+   */
+  void propertyChanged (Config conf, String key, String oldValue, String newValue);
+  
+  /**
+   *  this can be used to let a config listener remove itself, which is
+   *  required if the same Config object is used for several JPF runs
+   */
+  void jpfRunTerminated (Config conf);
+}
diff --git a/src/main/gov/nasa/jpf/Error.java b/src/main/gov/nasa/jpf/Error.java
new file mode 100644 (file)
index 0000000..39f3eb8
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.ThreadList;
+
+/**
+ * class used to store property violations (property and path)
+ */
+public class Error {
+
+  int            id;
+  
+  Property       property;
+  private String errorMessage;
+  
+  private Path   path;
+  ThreadList     threadList;
+  
+  public Error (int id, Property prop, Path p, ThreadList l) {
+    this.id = id;
+    property = prop;
+    errorMessage = prop.getErrorMessage();    
+    path = p; // client has to clone in case we go on
+    threadList = l;
+  }
+
+  public int getId() {
+    return id;
+  }
+  
+  public String getDescription () {
+    StringBuilder sb = new StringBuilder();
+    sb.append(property.getClass().getName());
+    
+    String s = property.getExplanation();
+    if (s != null) {
+      sb.append(" (\"");
+      sb.append(s);
+      sb.append("\")");
+    }
+    
+    return sb.toString();
+  }
+
+  public String getDetails() {
+    return errorMessage;
+  }
+  
+  public Path getPath () {
+    return path;
+  }
+
+  public Property getProperty () {
+    return property;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/GenericProperty.java b/src/main/gov/nasa/jpf/GenericProperty.java
new file mode 100644 (file)
index 0000000..b2eaffb
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+
+import java.io.PrintWriter;
+
+/**
+ * generic abstract base class implementing program properties. This is mostly
+ * a convenience construct that implements error printout, so that only
+ * the check itself has to be provided
+ * 
+ * <2do> why is this still not an interface ??
+ */
+public abstract class GenericProperty implements Property, Cloneable {
+  @Override
+  public abstract boolean check (Search search, VM vm);
+
+  protected GenericProperty () {
+    // nothing yet
+  }
+  
+  @Override
+  public Property clone() throws CloneNotSupportedException {
+    return (Property) super.clone();
+  }
+  
+  @Override
+  public String getErrorMessage () {
+    return null;
+  }
+
+  @Override
+  public String getExplanation () {
+    return null;
+  }
+  
+  @Override
+  public void reset () {
+    // nothing to do here, but Property implementors that store
+    // stuff have to override (it's called if search.multiple_errors is on)
+  }
+  
+  @Override
+  public void printOn (PrintWriter pw) {
+    pw.println(getErrorMessage());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/JPF.java b/src/main/gov/nasa/jpf/JPF.java
new file mode 100644 (file)
index 0000000..b4009a5
--- /dev/null
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.report.Reporter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.SearchListener;
+import gov.nasa.jpf.tool.RunJPF;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.LogManager;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.RunRegistry;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.NoOutOfMemoryErrorProperty;
+import gov.nasa.jpf.vm.VMListener;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Logger;
+
+
+/**
+ * main class of the JPF verification framework. This reads the configuration,
+ * instantiates the Search and VM objects, and kicks off the Search
+ */
+public class JPF implements Runnable {
+  
+  public static String VERSION = "8.0"; // the major version number
+
+  static Logger logger     = null; // initially
+
+  public enum Status { NEW, RUNNING, DONE };
+
+  class ConfigListener implements ConfigChangeListener {
+    
+    /**
+     * check for new listeners that are dynamically configured
+     */
+    @Override
+    public void propertyChanged(Config config, String key, String oldValue, String newValue) {
+      if ("listener".equals(key)) {
+        if (oldValue == null)
+          oldValue = "";
+        
+        String[] nv = config.asStringArray(newValue);
+        String[] ov = config.asStringArray(oldValue);
+        String[] newListeners = Misc.getAddedElements(ov, nv);
+        Class<?>[] argTypes = { Config.class, JPF.class };          // Many listeners have 2 parameter constructors
+        Object[] args = {config, JPF.this };
+        
+        if (newListeners != null) {
+          for (String clsName : newListeners) {
+            try {
+              JPFListener newListener = config.getInstance("listener", clsName, JPFListener.class, argTypes, args);
+              addListener(newListener);
+              logger.info("config changed, added listener " + clsName);
+
+            } catch (JPFConfigException cfx) {
+              logger.warning("listener change failed: " + cfx.getMessage());
+            }
+          }
+        }
+      }
+    }
+    
+    /**
+     * clean up to avoid a sublte but serious memory leak when using the
+     * same config for multiple JPF objects/runs - this listener is an inner
+     * class object that keeps its encapsulating JPF instance alive
+     */
+    @Override
+    public void jpfRunTerminated(Config config){
+      config.removeChangeListener(this);
+    }
+  }
+  
+  /** this is the backbone of all JPF configuration */
+  Config config;
+  
+  /** The search policy used to explore the state space */
+  Search search;
+
+  /** Reference to the virtual machine used by the search */
+  VM vm;
+
+  /** the report generator */
+  Reporter reporter;
+
+  Status status = Status.NEW;
+
+  /** a list of listeners that get automatically added from VM, Search or Reporter initialization */
+  List<VMListener> pendingVMListeners;
+  List<SearchListener> pendingSearchListeners;
+
+  
+  /** we use this as safety margin, to be released upon OutOfMemoryErrors */
+  byte[] memoryReserve;
+  
+  private static Logger initLogging(Config conf) {
+    LogManager.init(conf);
+    return getLogger("gov.nasa.jpf");
+  }
+
+  /**
+   * use this one to get a Logger that is initialized via our Config mechanism. Note that
+   * our own Loggers do NOT pass
+   */
+  public static JPFLogger getLogger (String name) {
+    return LogManager.getLogger( name);
+  }
+
+  public static void main(String[] args){
+    int options = RunJPF.getOptions(args);
+
+    if (args.length == 0 || RunJPF.isOptionEnabled( RunJPF.HELP,options)) {
+      RunJPF.showUsage();
+      return;
+    }
+    if (RunJPF.isOptionEnabled( RunJPF.ADD_PROJECT,options)){
+      RunJPF.addProject(args);
+      return;
+    }
+    
+    if (RunJPF.isOptionEnabled( RunJPF.BUILD_INFO,options)){
+      RunJPF.showBuild(RunJPF.class.getClassLoader());
+    }
+
+    if (RunJPF.isOptionEnabled( RunJPF.LOG,options)){
+      Config.enableLogging(true);
+    }
+
+    Config conf = createConfig(args);
+
+    if (RunJPF.isOptionEnabled( RunJPF.SHOW, options)) {
+      conf.printEntries();
+    }
+
+    start(conf, args);
+  }
+
+  public static void start(Config conf, String[] args){
+    // this is redundant to jpf.report.<publisher>.start=..config..
+    // but nobody can remember this (it's only used to produce complete reports)
+
+    if (logger == null) {
+      logger = initLogging(conf);
+    }
+
+    if (!checkArgs(args)){
+      return;
+    }
+
+    setNativeClassPath(conf); // in case we have to find a shell
+
+    // check if there is a shell class specification, in which case we just delegate
+    JPFShell shell = conf.getInstance("shell", JPFShell.class);
+    if (shell != null) {
+      shell.start(args); // responsible for exception handling itself
+
+    } else {
+      // no shell, we start JPF directly
+      LogManager.printStatus(logger);
+      conf.printStatus(logger);
+
+      // this has to be done after we checked&consumed all "-.." arguments
+      // this is NOT about checking properties!
+      checkUnknownArgs(args);
+
+      try {
+        JPF jpf = new JPF(conf);
+        jpf.run();
+
+      } catch (ExitException x) {
+        logger.severe( "JPF terminated");
+
+        // this is how we get most runtime config exceptions
+        if (x.shouldReport()) {
+          x.printStackTrace();
+        }
+
+        /**
+        Throwable cause = x.getCause();
+        if ((cause != null) && conf.getBoolean("pass_exceptions", false)) {
+          cause.fillInStackTrace();
+          throw cause;
+        }
+        **/
+
+      } catch (JPFException jx) {
+        logger.severe( "JPF exception, terminating: " + jx.getMessage());
+        if (conf.getBoolean("jpf.print_exception_stack")) {
+
+          Throwable cause = jx.getCause();
+          if (cause != null && cause != jx){
+            cause.printStackTrace();
+          } else {
+            jx.printStackTrace();
+          }
+        }
+        // pass this on, caller has to handle
+        throw jx;
+      }
+    }
+  }
+
+  
+  static void setNativeClassPath(Config config) {
+    if (!config.hasSetClassLoader()) {
+      config.initClassLoader( JPF.class.getClassLoader());
+    }
+  }
+
+
+  protected JPF (){
+    // just to support unit test mockups
+  }
+
+  /**
+   * create new JPF object. Note this is always guaranteed to return, but the
+   * Search and/or VM object instantiation might have failed (i.e. the JPF
+   * object might not be really usable). If you directly use getSearch() / getVM(),
+   * check for null
+   */
+  public JPF(Config conf) {
+    config = conf;
+
+    setNativeClassPath(config); // before we do anything else
+
+    if (logger == null) { // maybe somebody created a JPF object explicitly
+      logger = initLogging(config);
+    }
+
+    initialize();
+  }
+
+  /**
+   * convenience method if caller doesn't care about Config
+   */
+  public JPF (String ... args) {
+    this( createConfig(args));
+  }
+  
+  private void initialize() {
+    VERSION = config.getString("jpf.version", VERSION);
+    memoryReserve = new byte[config.getInt("jpf.memory_reserve", 64 * 1024)]; // in bytes
+    
+    try {
+      
+      Class<?>[] vmArgTypes = { JPF.class, Config.class };
+      Object[] vmArgs = { this, config };
+      vm = config.getEssentialInstance("vm.class", VM.class, vmArgTypes, vmArgs);
+
+      Class<?>[] searchArgTypes = { Config.class, VM.class };
+      Object[] searchArgs = { config, vm };
+      search = config.getEssentialInstance("search.class", Search.class,
+                                                searchArgTypes, searchArgs);
+
+      // although the Reporter will always be notified last, this has to be set
+      // first so that it can register utility listeners like Statistics that
+      // can be used by configured listeners
+      Class<?>[] reporterArgTypes = { Config.class, JPF.class };
+      Object[] reporterArgs = { config, this };
+      reporter = config.getInstance("report.class", Reporter.class, reporterArgTypes, reporterArgs);
+      if (reporter != null){
+        search.setReporter(reporter);
+      }
+      
+      addListeners();
+      
+      config.addChangeListener(new ConfigListener());
+      
+    } catch (JPFConfigException cx) {
+      logger.severe(cx.toString());
+      //cx.getCause().printStackTrace();      
+      throw new ExitException(false, cx);
+    }
+  }  
+
+  
+  public Status getStatus() {
+    return status;
+  }
+  
+  public boolean isRunnable () {
+    return ((vm != null) && (search != null));
+  }
+
+  public void addPropertyListener (PropertyListenerAdapter pl) {
+    if (vm != null) {
+      vm.addListener( pl);
+    }
+    if (search != null) {
+      search.addListener( pl);
+      search.addProperty(pl);
+    }
+  }
+
+  public void addSearchListener (SearchListener l) {
+    if (search != null) {
+      search.addListener(l);
+    }
+  }
+
+  protected void addPendingVMListener (VMListener l){
+    if (pendingVMListeners == null){
+      pendingVMListeners = new ArrayList<VMListener>();
+    }
+    pendingVMListeners.add(l);
+  }
+  
+  protected void addPendingSearchListener (SearchListener l){
+    if (pendingSearchListeners == null){
+      pendingSearchListeners = new ArrayList<SearchListener>();
+    }
+    pendingSearchListeners.add(l);
+  }
+  
+  public void addListener (JPFListener l) {    
+    // the VM is instantiated first
+    if (l instanceof VMListener) {
+      if (vm != null) {
+        vm.addListener( (VMListener) l);
+        
+      } else { // no VM yet, we are in VM initialization
+        addPendingVMListener((VMListener)l);
+      }
+    }
+    
+    if (l instanceof SearchListener) {
+      if (search != null) {
+        search.addListener( (SearchListener) l);
+        
+      } else { // no search yet, we are in Search initialization
+        addPendingSearchListener((SearchListener)l);
+      }
+    }
+  }
+
+  public <T> T getListenerOfType( Class<T> type){
+    if (search != null){
+      T listener = search.getNextListenerOfType(type, null);
+      if (listener != null){
+        return listener;
+      }
+    }
+    
+    if (vm != null){
+      T listener = vm.getNextListenerOfType(type, null);
+      if (listener != null){
+        return listener;
+      }
+    }
+    
+    return null;
+  }
+  
+  public boolean addUniqueTypeListener (JPFListener l) {
+    boolean addedListener = false;
+    Class<?> cls = l.getClass();
+    
+    if (l instanceof VMListener) {
+      if (vm != null) {
+        if (!vm.hasListenerOfType(cls)) {
+          vm.addListener( (VMListener) l);
+          addedListener = true;
+        }
+      }
+    }
+    if (l instanceof SearchListener) {
+      if (search != null) {
+        if (!search.hasListenerOfType(cls)) {
+          search.addListener( (SearchListener) l);
+          addedListener = true;
+        }
+      }
+    }
+
+    return addedListener;
+  }
+  
+  
+  public void removeListener (JPFListener l){
+    if (l instanceof VMListener) {
+      if (vm != null) {
+        vm.removeListener( (VMListener) l);
+      }
+    }
+    if (l instanceof SearchListener) {
+      if (search != null) {
+        search.removeListener( (SearchListener) l);
+      }
+    }
+  }
+
+  public void addVMListener (VMListener l) {
+    if (vm != null) {
+      vm.addListener(l);
+    }
+  }
+
+  public void addSearchProperty (Property p) {
+    if (search != null) {
+      search.addProperty(p);
+    }
+  }
+    
+  /**
+   * this is called after vm, search and reporter got instantiated
+   */
+  void addListeners () {
+    Class<?>[] argTypes = { Config.class, JPF.class };
+    Object[] args = { config, this };
+
+    // first listeners that were automatically added from VM, Search and Reporter initialization
+    if (pendingVMListeners != null){
+      for (VMListener l : pendingVMListeners) {
+       vm.addListener(l);
+      }      
+      pendingVMListeners = null;
+    }
+    
+    if (pendingSearchListeners != null){
+      for (SearchListener l : pendingSearchListeners) {
+       search.addListener(l);
+      }
+      pendingSearchListeners = null;
+    }
+    
+    // and finally everything that's user configured
+    List<JPFListener> listeners = config.getInstances("listener", JPFListener.class, argTypes, args);
+    if (listeners != null) {
+      for (JPFListener l : listeners) {
+        addListener(l);
+      }
+    }
+  }
+
+  public Reporter getReporter () {
+    return reporter;
+  }
+
+  public <T extends Publisher> boolean addPublisherExtension (Class<T> pCls, PublisherExtension e) {
+    if (reporter != null) {
+      return reporter.addPublisherExtension(pCls, e);
+    }
+    return false;
+  }
+
+  public <T extends Publisher> void setPublisherItems (Class<T> pCls,
+                                                        int category, String[] topics) {
+    if (reporter != null) {
+      reporter.setPublisherItems(pCls, category, topics);
+    }
+  }
+
+
+  public Config getConfig() {
+    return config;
+  }
+
+  /**
+   * return the search object. This can be null if the initialization has failed
+   */
+  public Search getSearch() {
+    return search;
+  }
+
+  /**
+   * return the VM object. This can be null if the initialization has failed
+   */
+  public VM getVM() {
+    return vm;
+  }
+
+  public static void exit() {
+    // Hmm, exception as non local return. But we might be called from a
+    // context we don't want to kill
+    throw new ExitException();
+  }
+
+  public boolean foundErrors() {
+    return !(search.getErrors().isEmpty());
+  }
+
+  /**
+   * this assumes that we have checked and 'consumed' (nullified) all known
+   * options, so we just have to check for any '-' option prior to the
+   * target class name
+   */
+  static void checkUnknownArgs (String[] args) {
+    for ( int i=0; i<args.length; i++) {
+      if (args[i] != null) {
+        if (args[i].charAt(0) == '-') {
+          logger.warning("unknown command line option: " + args[i]);
+        }
+        else {
+          // this is supposed to be the target class name - everything that follows
+          // is supposed to be processed by the program under test
+          break;
+        }
+      }
+    }
+  }
+
+  public static void printBanner (Config config) {
+    System.out.println("Java Pathfinder core system v" +
+                  config.getString("jpf.version", VERSION) +
+                  " - (C) 2005-2014 United States Government. All rights reserved.");
+  }
+
+
+  /**
+   * find the value of an arg that is either specific as
+   * "-key=value" or as "-key value". If not found, the supplied
+   * defValue is returned
+   */
+  static String getArg(String[] args, String pattern, String defValue, boolean consume) {
+    String s = defValue;
+
+    if (args != null){
+      for (int i = 0; i < args.length; i++) {
+        String arg = args[i];
+
+        if (arg != null) {
+          if (arg.matches(pattern)) {
+            int idx=arg.indexOf('=');
+            if (idx > 0) {
+              s = arg.substring(idx+1);
+              if (consume) {
+                args[i]=null;
+              }
+            } else if (i < args.length-1) {
+              s = args[i+1];
+              if (consume) {
+                args[i] = null;
+                args[i+1] = null;
+              }
+            }
+            break;
+          }
+        }
+      }
+    }
+    
+    return s;
+  }
+
+  /**
+   * what property file to look for
+   */
+  static String getConfigFileName (String[] args) {
+    if (args.length > 0) {
+      // check if the last arg is a mode property file
+      // if yes, it has to include a 'target' spec
+      int idx = args.length-1;
+      String lastArg = args[idx];
+      if (lastArg.endsWith(".jpf") || lastArg.endsWith(".properties")) {
+        if (lastArg.startsWith("-c")) {
+          int i = lastArg.indexOf('=');
+          if (i > 0) {
+            lastArg = lastArg.substring(i+1);
+          }
+        }
+        args[idx] = null;
+        return lastArg;
+      }
+    }
+    
+    return getArg(args, "-c(onfig)?(=.+)?", "jpf.properties", true);
+  }
+
+  /**
+   * return a Config object that holds the JPF options. This first
+   * loads the properties from a (potentially configured) properties file, and
+   * then overlays all command line arguments that are key/value pairs
+   */
+  public static Config createConfig (String[] args) {
+    return new Config(args);
+  }
+  
+  /**
+   * runs the verification.
+   */
+  @Override
+  public void run() {
+    Runtime rt = Runtime.getRuntime();
+
+    // this might be executed consecutively, so notify everybody
+    RunRegistry.getDefaultRegistry().reset();
+
+    if (isRunnable()) {
+      try {
+        if (vm.initialize()) {
+          status = Status.RUNNING;
+          search.search();
+        }
+      } catch (OutOfMemoryError oom) {
+        
+        // try to get memory back before we do anything that makes it worse
+        // (note that we even try to avoid calls here, we are on thin ice)
+
+        // NOTE - we don't try to recover at this point (that is what we do
+        // if we fall below search.min_free within search()), we only want to
+        // terminate gracefully (incl. report)
+
+        memoryReserve = null; // release something
+        long m0 = rt.freeMemory();
+        long d = 10000;
+        
+        // see if we can reclaim some memory before logging or printing statistics
+        for (int i=0; i<10; i++) {
+          rt.gc();
+          long m1 = rt.freeMemory();
+          if ((m1 <= m0) || ((m1 - m0) < d)) {
+            break;
+          }
+          m0 = m1;
+        }
+        
+        logger.severe("JPF out of memory");
+
+        // that's questionable, but we might want to see statistics / coverage
+        // to know how far we got. We don't inform any other listeners though
+        // if it throws an exception we bail - we can't handle it w/o memory
+        try {
+          search.notifySearchConstraintHit("JPF out of memory");
+          search.error(new NoOutOfMemoryErrorProperty());            // JUnit tests will succeed if OOM isn't flagged.
+          reporter.searchFinished(search);
+        } catch (Throwable t){
+          throw new JPFListenerException("exception during out-of-memory termination", t);
+        }
+        
+      // NOTE - this is not an exception firewall anymore
+
+      } finally {
+        status = Status.DONE;
+
+        config.jpfRunTerminated();
+        cleanUp();        
+      }
+    }
+  }
+  
+  protected void cleanUp(){
+    search.cleanUp();
+    vm.cleanUp();
+    reporter.cleanUp();
+  }
+  
+  public List<Error> getSearchErrors () {
+    if (search != null) {
+      return search.getErrors();
+    }
+
+    return null;
+  }
+
+  public Error getLastError () {
+    if (search != null) {
+      return search.getLastError();
+    }
+
+    return null;
+  }
+  
+  
+  // some minimal sanity checks
+  static boolean checkArgs (String[] args){
+    String lastArg = args[args.length-1];
+    if (lastArg != null && lastArg.endsWith(".jpf")){
+      if (!new File(lastArg).isFile()){
+        logger.severe("application property file not found: " + lastArg);
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public static void handleException(JPFException e) {
+    logger.severe(e.getMessage());
+    exit();
+  }
+
+  /**
+   * private helper class for local termination of JPF (without killing the
+   * whole Java process via System.exit).
+   * While this is basically a bad non-local goto exception, it seems to be the
+   * least of evils given the current JPF structure, and the need to terminate
+   * w/o exiting the whole Java process. If we just do a System.exit(), we couldn't
+   * use JPF in an embedded context
+   */
+  @SuppressWarnings("serial")
+  public static class ExitException extends RuntimeException {
+    boolean report = true;
+    
+    ExitException() {}
+    
+    ExitException (boolean report, Throwable cause){
+      super(cause);
+      
+      this.report = report;
+    }
+    
+    ExitException(String msg) {
+      super(msg);
+    }
+    
+    public boolean shouldReport() {
+      return report;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/JPFClassLoader.java b/src/main/gov/nasa/jpf/JPFClassLoader.java
new file mode 100644 (file)
index 0000000..d737330
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import java.io.File;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+/**
+ * classloader that is used by Config to instantiate from JPF configured
+ * paths. This is a standard parent-first loader to avoid multiple class
+ * instances when using our Run*.jar tools
+ *
+ * The main reason for having our own classloader is dynamically configured resource
+ * and library lookup
+ */
+public class JPFClassLoader extends URLClassLoader {
+
+  String[] nativeLibs;
+
+
+  static {
+    //ClassLoader.registerAsParallelCapable(); // for jdk7
+  }
+  
+  public JPFClassLoader (URL[] urls){
+    super(urls);
+  }
+
+  public JPFClassLoader (URL[] urls, String[] libs, ClassLoader parent){
+    super(urls, parent);
+
+    nativeLibs = libs;
+  }
+
+  @Override
+  protected String findLibrary (String libBaseName){
+
+    if (nativeLibs != null){
+      String libName = File.separator + System.mapLibraryName(libBaseName);
+
+      for (String libPath : nativeLibs) {
+        if (libPath.endsWith(libName)) {
+          return libPath;
+        }
+      }
+    }
+
+    return null; // means VM uses java.library.path to look it up
+  }
+
+  /**
+   * we make it public since we add paths dynamically during JPF init
+   * 
+   * Note this is ignored according to the javadocs if the provided url is already in the classpath.
+   * We do rely on this feature since me might add jpf.jar several times during bootstrap
+   */
+  @Override
+  public void addURL (URL url){
+    if (url != null){
+      super.addURL(url);
+    }
+  }
+  
+  public void setNativeLibs (String[] libs){
+    nativeLibs = libs;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/JPFConfigException.java b/src/main/gov/nasa/jpf/JPFConfigException.java
new file mode 100644 (file)
index 0000000..57e413a
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * this class wraps the various exceptions we might encounter during
+ * initialization and use of Config
+ */
+public class JPFConfigException extends JPFException {
+
+  public JPFConfigException(String msg) {
+    super(msg);
+  }
+
+  public JPFConfigException(String msg, Throwable cause) {
+    super(msg, cause);
+  }
+
+  public JPFConfigException(String key, Class<?> cls, String failure) {
+    super("error instantiating class " + cls.getName() + " for entry \"" + key + "\":" + failure);
+  }
+
+  public JPFConfigException(String key, Class<?> cls, String failure, Throwable cause) {
+    this(key, cls, failure);
+    initCause(cause);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("JPF configuration error: ");
+    sb.append(getMessage());
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/JPFErrorException.java b/src/main/gov/nasa/jpf/JPFErrorException.java
new file mode 100644 (file)
index 0000000..b6b0932
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * internal JPF error
+ */
+@SuppressWarnings("serial")
+public class JPFErrorException extends JPFException {
+  public JPFErrorException (String s) {
+    super(s);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/JPFException.java b/src/main/gov/nasa/jpf/JPFException.java
new file mode 100644 (file)
index 0000000..0d89416
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import java.io.PrintStream;
+
+/**
+ * common root for all exceptions thrown by JPF
+ */
+@SuppressWarnings("serial")
+public class JPFException extends RuntimeException {
+
+  public JPFException (String details) {
+    super(details);
+  }
+
+  public JPFException (Throwable cause) {
+    super(cause);
+  }
+
+  public JPFException (String details, Throwable cause){
+    super(details, cause);
+  }
+  
+  @Override
+  public void printStackTrace (PrintStream out) {
+    out.println("---------------------- JPF error stack trace ---------------------");
+    super.printStackTrace(out);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/JPFListener.java b/src/main/gov/nasa/jpf/JPFListener.java
new file mode 100644 (file)
index 0000000..d53b491
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * this is just a common root type for VMListeners and SearchListeners. No
+ * own interface, just a type tag. It's main purpose is to provide some 
+ * typechecks during config-based reflection instantiation
+ */
+public interface JPFListener {
+  // see VMListener, SearchListener
+}
diff --git a/src/main/gov/nasa/jpf/JPFListenerException.java b/src/main/gov/nasa/jpf/JPFListenerException.java
new file mode 100644 (file)
index 0000000..8517498
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * JPFException that wraps whatever can go wrong in a listener during notification
+ */
+public class JPFListenerException extends JPFException {
+
+  public JPFListenerException(String details, Throwable cause) {
+    super(details, cause);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/JPFNativePeerException.java b/src/main/gov/nasa/jpf/JPFNativePeerException.java
new file mode 100644 (file)
index 0000000..bd4c168
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * JPFException that wraps whatever can go wrong in a native method
+ */
+public class JPFNativePeerException extends JPFException {
+
+  public JPFNativePeerException(String details, Throwable cause) {
+    super(details, cause);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/JPFShell.java b/src/main/gov/nasa/jpf/JPFShell.java
new file mode 100644 (file)
index 0000000..b8093c6
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * type that can be used to instantiate and run a JPF object
+ * Shell objects can be configured via the JPF 'shell' property, and are
+ * typically JPF user interface implementations
+ * 
+ * Instantiation and start() call are done from the JPF.main() method
+ *
+ * Usually, JPFShell implementors have a <init>(Config) ctor so that they
+ * don't need to obtain a new Config object. This is the reason for
+ * using a shell *instance* instead of a static main() method - we cannot
+ * pass the Config object that was already created by JPF.main() at this point
+ * into the shell main(), which means we would either have to turn Config
+ * into a singleton (causing problems for multiple JPF runs), or create a new
+ * Config object within the shell. Since initialization of Config objects is
+ * an expensive task with our new bootstrapping, it seems better to pass the
+ * Config object from JPF.main() (which is done automatically when using
+ * Config based initialization)
+ */
+public interface JPFShell {
+
+  void start(String[] args);
+}
diff --git a/src/main/gov/nasa/jpf/ListenerAdapter.java b/src/main/gov/nasa/jpf/ListenerAdapter.java
new file mode 100644 (file)
index 0000000..ed470bf
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.jvm.ClassFile;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.SearchListener;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.VMListener;
+
+/**
+ * Adapter class that dummy implements both VMListener and SearchListener interfaces
+ * Used to ease implementation of listeners that only process a few notifications
+ */
+public abstract class ListenerAdapter implements VMListener, SearchListener, PublisherExtension {
+
+  //--- the VMListener interface
+  @Override
+  public void vmInitialized(VM vm) {}
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo currentThread, Instruction nextInstruction, Instruction executedInstruction) {}
+  @Override
+  public void executeInstruction(VM vm, ThreadInfo currentThread, Instruction instructionToExecute) {}
+  @Override
+  public void threadStarted(VM vm, ThreadInfo startedThread) {}
+  @Override
+  public void threadWaiting (VM vm, ThreadInfo waitingThread) {}
+  @Override
+  public void threadNotified (VM vm, ThreadInfo notifiedThread) {}
+  @Override
+  public void threadInterrupted (VM vm, ThreadInfo interruptedThread) {}
+  @Override
+  public void threadScheduled (VM vm, ThreadInfo scheduledThread) {}
+  @Override
+  public void threadBlocked (VM vm, ThreadInfo blockedThread, ElementInfo lock) {}
+  @Override
+  public void threadTerminated(VM vm, ThreadInfo terminatedThread) {}
+  @Override
+  public void loadClass (VM vm, ClassFile cf) {}
+  @Override
+  public void classLoaded(VM vm, ClassInfo loadedClass) {}
+  @Override
+  public void objectCreated(VM vm, ThreadInfo currentThread, ElementInfo newObject) {}
+  @Override
+  public void objectReleased(VM vm, ThreadInfo currentThread, ElementInfo releasedObject) {}
+  @Override
+  public void objectLocked (VM vm, ThreadInfo currentThread, ElementInfo lockedObject) {}
+  @Override
+  public void objectUnlocked (VM vm, ThreadInfo currentThread, ElementInfo unlockedObject) {}
+  @Override
+  public void objectWait (VM vm, ThreadInfo currentThread, ElementInfo waitingObject) {}
+  @Override
+  public void objectNotify (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject) {}
+  @Override
+  public void objectNotifyAll (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject) {}
+  @Override
+  public void objectExposed (VM vm, ThreadInfo currentThread, ElementInfo fieldOwnerObject, ElementInfo exposedObject) {}
+  @Override
+  public void objectShared (VM vm, ThreadInfo currentThread, ElementInfo sharedObject) {}
+  @Override
+  public void gcBegin(VM vm) {}
+  @Override
+  public void gcEnd(VM vm) {}
+  @Override
+  public void exceptionThrown(VM vm, ThreadInfo currentThread, ElementInfo thrownException) {}
+  @Override
+  public void exceptionBailout(VM vm, ThreadInfo currentThread) {}
+  @Override
+  public void exceptionHandled(VM vm, ThreadInfo currentThread) {}
+  @Override
+  public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {}
+  @Override
+  public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG) {}
+  @Override
+  public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {}
+  @Override
+  public void choiceGeneratorProcessed (VM vm, ChoiceGenerator<?> processedCG) {}
+  @Override
+  public void methodEntered (VM vm, ThreadInfo currentThread, MethodInfo enteredMethod) {}
+  @Override
+  public void methodExited (VM vm, ThreadInfo currentThread, MethodInfo exitedMethod) {}
+
+
+  //--- the SearchListener interface
+  @Override
+  public void stateAdvanced(Search search) {}
+  @Override
+  public void stateProcessed(Search search) {}
+  @Override
+  public void stateBacktracked(Search search) {}
+  @Override
+  public void statePurged(Search search) {}
+  @Override
+  public void stateStored(Search search) {}
+  @Override
+  public void stateRestored(Search search) {}
+  @Override
+  public void searchProbed (Search search){}
+  @Override
+  public void propertyViolated(Search search) {}
+  @Override
+  public void searchStarted(Search search) {}
+  @Override
+  public void searchConstraintHit(Search search) {}
+  @Override
+  public void searchFinished(Search search) {}
+
+
+  //--- PublisherExtension interface
+  @Override
+  public void publishStart (Publisher publisher) {}
+  @Override
+  public void publishTransition (Publisher publisher) {}
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {}
+  @Override
+  public void publishConstraintHit (Publisher publisher) {}
+  @Override
+  public void publishFinished (Publisher publisher) {}
+  @Override
+  public void publishProbe (Publisher publisher) {}
+
+}
diff --git a/src/main/gov/nasa/jpf/Property.java b/src/main/gov/nasa/jpf/Property.java
new file mode 100644 (file)
index 0000000..ead8073
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.Printable;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * abstraction that is used by Search objects to determine if program
+ * properties have been violated (e.g. NoUncaughtExceptions)
+ */
+public interface Property extends Printable {
+
+  /**
+   * return true if property is NOT violated
+   */
+  boolean check (Search search, VM vm);
+
+  String getErrorMessage ();
+  
+  String getExplanation();
+  
+  void reset (); // required for search.multiple_errors
+  
+  Property clone() throws CloneNotSupportedException; // so that we can store multiple errors
+}
diff --git a/src/main/gov/nasa/jpf/PropertyListenerAdapter.java b/src/main/gov/nasa/jpf/PropertyListenerAdapter.java
new file mode 100644 (file)
index 0000000..88c9a66
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.jvm.ClassFile;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.SearchListener;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.VMListener;
+
+/**
+ * abstract base class that dummy implements Property, Search- and VMListener methods
+ * convenient for creating listeners that act as properties, just having to override
+ * the methods they need
+ *
+ * the only local functionality is that instances register themselves automatically
+ * as property when the search is started
+ *
+ * <2do> rewrite once GenericProperty is an interface
+ */
+public abstract class PropertyListenerAdapter extends GenericProperty implements
+    SearchListener, VMListener, PublisherExtension {
+
+  //--- Property interface
+  @Override
+  public boolean check(Search search, VM vm) {
+    // return false if property is violated
+    return true;
+  }
+
+  @Override
+  public void reset () {
+    // override if the property has any local state
+  }
+
+  @Override
+  public Property clone() throws CloneNotSupportedException {
+    return super.clone();
+  }
+  
+//--- the VMListener interface
+  @Override
+  public void vmInitialized(VM vm) {}
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo currentThread, Instruction nextInstruction, Instruction executedInstruction) {}
+  @Override
+  public void executeInstruction(VM vm, ThreadInfo currentThread, Instruction instructionToExecute) {}
+  @Override
+  public void threadStarted(VM vm, ThreadInfo startedThread) {}
+  @Override
+  public void threadWaiting (VM vm, ThreadInfo waitingThread) {}
+  @Override
+  public void threadNotified (VM vm, ThreadInfo notifiedThread) {}
+  @Override
+  public void threadInterrupted (VM vm, ThreadInfo interruptedThread) {}
+  @Override
+  public void threadScheduled (VM vm, ThreadInfo scheduledThread) {}
+  @Override
+  public void threadBlocked (VM vm, ThreadInfo blockedThread, ElementInfo lock) {}
+  @Override
+  public void threadTerminated(VM vm, ThreadInfo terminatedThread) {}
+  @Override
+  public void loadClass (VM vm, ClassFile cf) {}
+  @Override
+  public void classLoaded(VM vm, ClassInfo loadedClass) {}
+  @Override
+  public void objectCreated(VM vm, ThreadInfo currentThread, ElementInfo newObject) {}
+  @Override
+  public void objectReleased(VM vm, ThreadInfo currentThread, ElementInfo releasedObject) {}
+  @Override
+  public void objectLocked (VM vm, ThreadInfo currentThread, ElementInfo lockedObject) {}
+  @Override
+  public void objectUnlocked (VM vm, ThreadInfo currentThread, ElementInfo unlockedObject) {}
+  @Override
+  public void objectWait (VM vm, ThreadInfo currentThread, ElementInfo waitingObject) {}
+  @Override
+  public void objectNotify (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject) {}
+  @Override
+  public void objectNotifyAll (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject) {}
+  @Override
+  public void objectExposed (VM vm, ThreadInfo currentThread, ElementInfo fieldOwnerObject, ElementInfo exposedObject) {}
+  @Override
+  public void objectShared (VM vm, ThreadInfo currentThread, ElementInfo sharedObject) {}
+  @Override
+  public void gcBegin(VM vm) {}
+  @Override
+  public void gcEnd(VM vm) {}
+  @Override
+  public void exceptionThrown(VM vm, ThreadInfo currentThread, ElementInfo thrownException) {}
+  @Override
+  public void exceptionBailout(VM vm, ThreadInfo currentThread) {}
+  @Override
+  public void exceptionHandled(VM vm, ThreadInfo currentThread) {}
+  @Override
+  public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {}
+  @Override
+  public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG) {}
+  @Override
+  public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {}
+  @Override
+  public void choiceGeneratorProcessed (VM vm, ChoiceGenerator<?> processedCG) {}
+  @Override
+  public void methodEntered (VM vm, ThreadInfo currentThread, MethodInfo enteredMethod) {}
+  @Override
+  public void methodExited (VM vm, ThreadInfo currentThread, MethodInfo exitedMethod) {}
+
+
+  //--- the SearchListener interface
+  @Override
+  public void stateAdvanced(Search search) {}
+  @Override
+  public void stateProcessed(Search search) {}
+  @Override
+  public void stateBacktracked(Search search) {}
+  @Override
+  public void statePurged(Search search) {}
+  @Override
+  public void stateStored(Search search) {}
+  @Override
+  public void stateRestored(Search search) {}
+  @Override
+  public void searchProbed (Search search){}
+  @Override
+  public void propertyViolated(Search search) {}
+  @Override
+  public void searchStarted(Search search) {
+    search.addProperty(this);
+  }
+  @Override
+  public void searchConstraintHit(Search search) {}
+  @Override
+  public void searchFinished(Search search) {}
+
+
+  //--- PublisherExtension interface
+  @Override
+  public void publishStart (Publisher publisher) {}
+  @Override
+  public void publishTransition (Publisher publisher) {}
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {}
+  @Override
+  public void publishConstraintHit (Publisher publisher) {}
+  @Override
+  public void publishFinished (Publisher publisher) {}
+  @Override
+  public void publishProbe (Publisher publisher) {}
+}
diff --git a/src/main/gov/nasa/jpf/State.java b/src/main/gov/nasa/jpf/State.java
new file mode 100644 (file)
index 0000000..0601584
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.search.SearchState;
+import gov.nasa.jpf.vm.RestorableVMState;
+
+/**
+ * abstraction of JPF execution state that can be queried and stored by
+ * listeners 
+ */
+public class State {
+  RestorableVMState     vmState;
+  SearchState searchState;
+  boolean     hasSuccessor; // <2do> pcm - do we really need this?
+  boolean     isNew;
+
+  public State (boolean isNew, boolean hasSuccessor, SearchState searchState, 
+                RestorableVMState vmState) {
+    this.isNew = isNew;
+    this.hasSuccessor = hasSuccessor;
+    this.searchState = searchState;
+    this.vmState = vmState;
+  }
+
+  public boolean isNew () {
+    return isNew;
+  }
+
+  public SearchState getSearchState () {
+    return searchState;
+  }
+
+  public RestorableVMState getVMState () {
+    return vmState;
+  }
+
+  public boolean hasSuccessor () {
+    return hasSuccessor;
+  }
+
+  public void restore () {
+  }
+}
diff --git a/src/main/gov/nasa/jpf/StateExtension.java b/src/main/gov/nasa/jpf/StateExtension.java
new file mode 100644 (file)
index 0000000..a293b31
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+public interface StateExtension {
+
+}
diff --git a/src/main/gov/nasa/jpf/SystemAttribute.java b/src/main/gov/nasa/jpf/SystemAttribute.java
new file mode 100644 (file)
index 0000000..189ce8e
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+/**
+ * a type tag for attributes used by JPF
+ * SystemAttributes are protected by the .util.ObjectList API against accidental
+ * set(..) overwrite, they have to be removed by forceSet(..)
+ */
+public interface SystemAttribute {
+  // nothing in here
+}
diff --git a/src/main/gov/nasa/jpf/jvm/ClassFile.java b/src/main/gov/nasa/jpf/jvm/ClassFile.java
new file mode 100644 (file)
index 0000000..d3dbdb5
--- /dev/null
@@ -0,0 +1,2735 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.jvm.JVMByteCodeReader;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.BailOut;
+import gov.nasa.jpf.util.BinaryClassSource;
+
+import java.io.File;
+
+/**
+ * class to read and dissect Java classfile contents (as specified by the Java VM
+ * spec  http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#16628
+ */
+public class ClassFile extends BinaryClassSource {
+
+  public static final int CONSTANT_UTF8 = 1;
+  public static final int CONSTANT_INTEGER = 3;
+  public static final int CONSTANT_FLOAT = 4;
+  public static final int CONSTANT_LONG = 5;
+  public static final int CONSTANT_DOUBLE = 6;
+  public static final int CONSTANT_CLASS = 7;
+  public static final int CONSTANT_STRING = 8;
+  public static final int FIELD_REF = 9;
+  public static final int METHOD_REF = 10;
+  public static final int INTERFACE_METHOD_REF = 11;
+  public static final int NAME_AND_TYPE = 12;
+  public static final int METHOD_HANDLE = 15;
+  public static final int METHOD_TYPE = 16;
+  public static final int INVOKE_DYNAMIC = 18;
+
+  public static final int REF_GETFIELD = 1;
+  public static final int REF_GETSTATIC = 2;
+  public static final int REF_PUTFIELD = 3;
+  public static final int REF_PUTSTATIC = 4;
+  public static final int REF_INVOKEVIRTUAL = 5;
+  public static final int REF_INVOKESTATIC = 6;
+  public static final int REF_INVOKESPECIAL = 7;
+  public static final int REF_NEW_INVOKESPECIAL = 8;
+  public static final int REF_INVOKEINTERFACE = 9;
+
+  // used to store types in cpValue[]
+  public static enum CpInfo {
+    Unused_0,                 // 0
+    ConstantUtf8,             // 1
+    Unused_2,                 // 2
+    ConstantInteger,          // 3
+    ConstantFloat,            // 4
+    ConstantLong,             // 5
+    ConstantDouble,           // 6
+    ConstantClass,            // 7
+    ConstantString,           // 8
+    FieldRef,                 // 9
+    MethodRef,                // 10
+    InterfaceMethodRef,       // 11
+    NameAndType,              // 12
+    Unused_13,
+    Unused_14,
+    MethodHandle,             // 15
+    MethodType,               // 16
+    Unused_17,
+    InvokeDynamic             // 18
+  }
+
+  // <2do> this is going away
+  String requestedTypeName; // the type name that caused this classfile to be loaded
+
+  // the const pool
+  int[] cpPos;     // cpPos[i] holds data start index for cp_entry i (0 is unused)
+  Object[] cpValue; // cpValue[i] hold the String/Integer/Float/Double associated with corresponding cp_entries
+  
+  //--- ctors
+  public ClassFile (byte[] data, int offset){
+    super(data,offset);
+  }
+
+  public ClassFile (byte[] data){
+    super(data,0);
+  }
+
+  public ClassFile (String typeName, byte[] data){
+    super(data,0);
+    
+    this.requestedTypeName = typeName;
+  }
+  
+  public ClassFile (String typeName, byte[] data, int offset){
+    super(data, offset);
+    
+    this.requestedTypeName = typeName;
+  }
+
+  public ClassFile (File file) throws ClassParseException {
+    super(file);
+  }
+
+  public ClassFile (String pathName)  throws ClassParseException {
+    super( new File(pathName));
+  }
+
+
+
+  
+  /**
+   * set classfile data.  This is mainly provided to allow
+   * on-the-fly classfile instrumentation with 3rd party libraries
+   * 
+   * BEWARE - like getData(), this method can cause parsing to fail if the
+   * provided data does not conform to the VM specs. In particular, this
+   * method should ONLY be called before executing parse(ClassFileReader) and
+   * will otherwise throw a JPFException
+   */
+  public void setData(byte[] newData){
+    if (cpPos != null){
+      throw new JPFException("concurrent modification of ClassFile data");
+    }
+    
+    data = newData;
+  }
+  
+  /**
+   * return the typename this classfile gets loaded for
+   * <2do> this is going away
+   */
+  public String getRequestedTypeName(){
+    return requestedTypeName;
+  }
+
+
+  //--- general attributes
+  public static final String SYNTHETIC_ATTR = "Synthetic";
+  public static final String DEPRECATED_ATTR = "Deprecated";
+  public static final String SIGNATURE_ATTR = "Signature";
+  public static final String RUNTIME_INVISIBLE_ANNOTATIONS_ATTR = "RuntimeInvisibleAnnotations";
+  public static final String RUNTIME_VISIBLE_ANNOTATIONS_ATTR = "RuntimeVisibleAnnotations";
+  public static final String RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR = "RuntimeVisibleTypeAnnotations";
+
+  //--- standard field attributes
+  public static final String CONST_VALUE_ATTR = "ConstantValue";
+
+  protected final static String[] stdFieldAttrs = {
+    CONST_VALUE_ATTR, SYNTHETIC_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
+    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR };
+
+
+  //--- standard method attributes
+  public static final String CODE_ATTR = "Code";
+  public static final String EXCEPTIONS_ATTR = "Exceptions";
+  public static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR = "RuntimeInvisibleParameterAnnotations";
+  public static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR = "RuntimeVisibleParameterAnnotations";
+  public static final String ANNOTATIONDEFAULT_ATTR = "AnnotationDefault";
+
+  protected final static String[] stdMethodAttrs = { 
+    CODE_ATTR, EXCEPTIONS_ATTR, SYNTHETIC_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
+    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR,
+    RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR,
+    RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR,
+    RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR,
+    ANNOTATIONDEFAULT_ATTR
+  };
+
+
+  //--- standard code attributes
+  public static final String LINE_NUMBER_TABLE_ATTR = "LineNumberTable";
+  public static final String LOCAL_VAR_TABLE_ATTR = "LocalVariableTable";
+
+  protected final static String[] stdCodeAttrs = { LINE_NUMBER_TABLE_ATTR, LOCAL_VAR_TABLE_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR };
+
+
+  //--- standard class attributes
+  public static final String  SOURCE_FILE_ATTR = "SourceFile";
+  public static final String  INNER_CLASSES_ATTR = "InnerClasses";
+  public static final String  ENCLOSING_METHOD_ATTR = "EnclosingMethod";
+  public static final String  BOOTSTRAP_METHOD_ATTR = "BootstrapMethods";
+  
+  protected final static String[] stdClassAttrs = {
+    SOURCE_FILE_ATTR, DEPRECATED_ATTR, INNER_CLASSES_ATTR, DEPRECATED_ATTR, SIGNATURE_ATTR,
+    RUNTIME_INVISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_ANNOTATIONS_ATTR, RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR,
+    ENCLOSING_METHOD_ATTR, BOOTSTRAP_METHOD_ATTR };
+
+
+  protected String internStdAttrName(int cpIdx, String name, String[] stdNames){
+    for (int i=0; i<stdNames.length; i++){
+      if (stdNames[i] == name) return name;
+    }
+    for (int i=0; i<stdNames.length; i++){
+      String stdName = stdNames[i];
+      if (stdName.equals(name)){
+        cpValue[cpIdx] = stdName;
+        return stdName;
+      }
+    }
+    return name;
+  }
+
+
+  //--- constpool access
+
+  //--- the primitive info cpValue
+  public String utf8At(int utf8InfoIdx){
+    //assert data[cpPos[utf8InfoIdx]] == 1 : "not a utf8_info tag";
+    return (String) cpValue[utf8InfoIdx];
+  }
+
+  public int intAt(int intInfoIdx){
+    //assert data[cpPos[intInfoIdx]] == 3 : "not a int_info tag";
+    return (Integer) cpValue[intInfoIdx];
+  }
+
+  public float floatAt(int floatInfoIdx){
+    //assert data[cpPos[floatInfoIdx]] == 4 : "not a float_info tag";
+    return (Float) cpValue[floatInfoIdx];
+  }
+
+  public long longAt(int longInfoIdx){
+    //assert data[cpPos[longInfoIdx]] == 5 : "not a long_info tag";
+    return (Long) cpValue[longInfoIdx];
+  }
+
+  public double doubleAt(int doubleInfoIdx){
+    //assert data[cpPos[doubleInfoIdx]] == 6 : "not a double_info tag";
+    return (Double) cpValue[doubleInfoIdx];
+  }
+
+  //--- those two are delegated but resolved
+  public String classNameAt(int classInfoIdx){
+    //assert data[cpPos[classInfoIdx]] == 7 : "not a Class_info tag";
+    return (String) cpValue[classInfoIdx];
+  }
+
+  public String stringAt(int stringInfoIdx){
+    //assert data[cpPos[stringInfoIdx]] == 8 : "not a String_info tag";
+    return (String) cpValue[stringInfoIdx];
+  }
+
+  //--- composite infos
+
+  // the generic ones (if we don't care what kind of reference type this is)
+  public String refClassNameAt(int cpIdx){
+    return (String) cpValue[ u2(cpPos[cpIdx]+1)];
+  }
+  public String refNameAt(int cpIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[cpIdx]+3)]+1));
+  }
+  public String refDescriptorAt(int cpIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[cpIdx]+3)]+3));
+  }
+
+  public int mhRefTypeAt (int methodHandleInfoIdx){
+    return u1(cpPos[methodHandleInfoIdx]+1);
+  }
+  public int mhMethodRefIndexAt  (int methodHandleInfoIdx){
+    return u2(cpPos[methodHandleInfoIdx]+2);
+  }
+  
+  // those could check ref types
+  public String fieldClassNameAt(int fieldRefInfoIdx){
+    //assert data[cpPos[fieldRefInfoIdx]] == 9 : "not a Fieldref_info tag";
+    return (String) cpValue[ u2(cpPos[fieldRefInfoIdx]+1)];
+  }
+  public String fieldNameAt(int fieldRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[fieldRefInfoIdx]+3)]+1));
+  }
+  public String fieldDescriptorAt(int fieldRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[fieldRefInfoIdx]+3)]+3));
+  }
+
+  public String methodClassNameAt(int methodRefInfoIdx){
+    return (String) cpValue[ u2(cpPos[methodRefInfoIdx]+1)];
+  }
+  public String methodNameAt(int methodRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[methodRefInfoIdx]+3)]+1));
+  }
+  public String methodDescriptorAt(int methodRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[methodRefInfoIdx]+3)]+3));
+  }
+
+  public String methodTypeDescriptorAt (int methodTypeInfoIdx){
+    return utf8At( u2(cpPos[methodTypeInfoIdx]+1));
+  }
+  
+  public String interfaceMethodClassNameAt(int ifcMethodRefInfoIdx){
+    return (String) cpValue[ u2(cpPos[ifcMethodRefInfoIdx]+1)];
+  }
+  public String interfaceMethodNameAt(int ifcMethodRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[ifcMethodRefInfoIdx]+3)]+1));
+  }
+  public String interfaceMethodDescriptorAt(int ifcMethodRefInfoIdx){
+    return utf8At( u2( cpPos[ u2(cpPos[ifcMethodRefInfoIdx]+3)]+3));
+  }
+  
+  public int bootstrapMethodIndex (int cpInvokeDynamicIndex){
+    return u2(cpPos[cpInvokeDynamicIndex]+1);
+  }
+  public String samMethodNameAt(int cpInvokeDynamicIndex) {
+    return utf8At( u2( cpPos[ u2(cpPos[cpInvokeDynamicIndex]+3)]+1)); 
+  }
+  public String callSiteDescriptor(int cpInvokeDynamicIndex) {
+    return utf8At( u2( cpPos[ u2(cpPos[cpInvokeDynamicIndex]+3)]+3)); 
+  }
+  
+  public String getRefTypeName (int refCode){
+    switch (refCode){
+      case REF_GETFIELD:      return "getfield";
+      case REF_GETSTATIC:     return "getstatic";
+      case REF_PUTFIELD:      return "putfield";
+      case REF_PUTSTATIC:     return "putstatic";
+      case REF_INVOKEVIRTUAL: return "invokevirtual";
+      case REF_INVOKESTATIC:  return "invokestatic";
+      case REF_INVOKESPECIAL: return "invokespecial";
+      case REF_NEW_INVOKESPECIAL: return "new-invokespecial";
+      case REF_INVOKEINTERFACE: return "invokeinterface";
+      default:
+        return "<unknown>";
+    }
+  }
+  
+  public String getTypeName (int typeCode){
+    switch(typeCode){
+      case 4: return "boolean";
+      case 5: return "char";
+      case 6: return "float";
+      case 7: return "double";
+      case 8: return "byte";
+      case 9: return "short";
+      case 10: return "int";
+      case 11: return "long";
+      default:
+        return "<unknown>";
+    }
+  }
+
+  @Override
+  public int getPos(){
+    return pos;
+  }
+
+  public int getPc(){
+    return pc;
+  }
+
+  //--- traverse/analyze the const pool (this is rather exotic)
+
+  public int getNumberOfCpEntries(){
+    return cpValue.length;
+  }
+
+  public Object getCpValue (int i){
+    return cpValue[i];
+  }
+
+  public int getCpTag (int i){
+    return data[cpPos[i]];
+  }
+
+  /**
+   * the result can be used as input for u2(dataIndex)
+   *
+   * NOTE - this returns -1 for the dreaded unused extra entries associated
+   * with ConstantDouble and ConstantLong
+   */
+  public int getDataPosOfCpEntry (int i){
+    return cpPos[i];
+  }
+
+  //--- standard attributes
+
+  public Object getConstValueAttribute(int dataPos){
+    int cpIdx = u2(dataPos);
+    Object v = cpValue[cpIdx];
+    return v;
+  }
+
+  public String getSourceFileAttribute(int dataPos){
+    // SourceFile_attribute { u2 attr_name_idx; u4 attr_length; u2 sourcefile_idx<utf8>; }
+
+    int cpIdx = u2(dataPos + 6);
+    Object v = cpValue[cpIdx];
+    return (String)v;
+  }
+
+  
+  //--- low level readers
+
+  public final int u1(int dataIdx){
+    return data[dataIdx] & 0xff;
+  }
+
+  public final int u2(int dataIdx){
+    return ((data[dataIdx]&0xff) << 8) | (data[dataIdx+1]&0xff);
+  }
+
+  public final int i1(int dataIdx) {
+    return data[dataIdx++];
+  }
+
+  public final int i2(int dataIdx) {
+    int idx = dataIdx;
+    return (data[idx++] << 8) | (data[idx]&0xff);
+  }
+
+  public final int readU2(){
+    int idx = pos;
+    pos += 2;
+    return ((data[idx++]&0xff) << 8) | (data[idx]&0xff);
+  }
+
+  public final int readI2() {
+    int idx = pos;
+    pos += 2;
+    return (data[idx++] << 8) | (data[idx]&0xff);
+  }
+
+  public final int readI4(){
+    int idx = pos;
+    pos += 4;
+    byte[] data = this.data;
+
+    return (data[idx++] <<24) | ((data[idx++]&0xff) << 16) | ((data[idx++]&0xff) << 8) | (data[idx]&0xff);
+  }
+
+  
+  //--- reader notifications
+  private void setClass(ClassFileReader reader, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
+    int p = pos;
+    reader.setClass( this, clsName, superClsName, flags, cpCount);
+    pos = p;
+  }
+
+  private void setInterfaceCount(ClassFileReader reader, int ifcCount){
+    int p = pos;
+    reader.setInterfaceCount( this, ifcCount);
+    pos = p;
+  }
+  private void setInterface(ClassFileReader reader, int ifcIndex, String ifcName){
+    int p = pos;
+    reader.setInterface( this, ifcIndex, ifcName);
+    pos = p;
+  }
+  private void setInterfacesDone(ClassFileReader reader){
+    int p = pos;
+    reader.setInterfacesDone( this);
+    pos = p;
+  }
+
+
+  private void setFieldCount(ClassFileReader reader, int fieldCount){
+    int p = pos;
+    reader.setFieldCount( this, fieldCount);
+    pos = p;
+
+  }
+  private void setField(ClassFileReader reader, int fieldIndex, int accessFlags, String name, String descriptor){
+    int p = pos;
+    reader.setField( this, fieldIndex, accessFlags, name, descriptor);
+    pos = p;
+  }
+  private void setFieldAttributeCount(ClassFileReader reader, int fieldIndex, int attrCount){
+    int p = pos;
+    reader.setFieldAttributeCount( this, fieldIndex, attrCount);
+    pos = p;
+  }
+  private void setFieldAttribute(ClassFileReader reader, int fieldIndex, int attrIndex, String name, int attrLength){
+    int p = pos + attrLength;
+    reader.setFieldAttribute( this, fieldIndex, attrIndex, name, attrLength);
+    pos = p;
+  }
+  private void setFieldAttributesDone(ClassFileReader reader, int fieldIndex){
+    int p = pos;
+    reader.setFieldAttributesDone( this, fieldIndex);
+    pos = p;
+  }
+  private void setFieldDone(ClassFileReader reader, int fieldIndex){
+    int p = pos;
+    reader.setFieldDone( this, fieldIndex);
+    pos = p;
+  }
+  private void setFieldsDone(ClassFileReader reader){
+    int p = pos;
+    reader.setFieldsDone( this);
+    pos = p;
+  }
+  private void setConstantValue(ClassFileReader reader, Object tag, Object value){
+    int p = pos;
+    reader.setConstantValue( this, tag, value);
+    pos = p;
+  }
+
+  private void setMethodCount(ClassFileReader reader, int methodCount){
+    int p = pos;
+    reader.setMethodCount( this, methodCount);
+    pos = p;
+  }
+  private void setMethod(ClassFileReader reader, int methodIndex, int accessFlags, String name, String descriptor){
+    int p = pos;
+    reader.setMethod( this, methodIndex, accessFlags, name, descriptor);
+    pos = p;
+  }
+  private void setMethodAttributeCount(ClassFileReader reader, int methodIndex, int attrCount){
+    int p = pos;
+    reader.setMethodAttributeCount( this, methodIndex, attrCount);
+    pos = p;
+  }
+  private void setMethodAttribute(ClassFileReader reader, int methodIndex, int attrIndex, String name, int attrLength){
+    int p = pos + attrLength;
+    reader.setMethodAttribute( this, methodIndex, attrIndex, name, attrLength);
+    pos = p;
+  }
+  private void setMethodAttributesDone(ClassFileReader reader, int methodIndex){
+    int p = pos;
+    reader.setMethodAttributesDone( this, methodIndex);
+    pos = p;
+  }
+  private void setMethodDone(ClassFileReader reader, int methodIndex){
+    int p = pos;
+    reader.setMethodDone( this, methodIndex);
+    pos = p;
+  }
+  private void setMethodsDone(ClassFileReader reader){
+    int p = pos;
+    reader.setMethodsDone( this);
+    pos = p;
+  }
+  private void setExceptionCount(ClassFileReader reader, Object tag, int exceptionCount){
+    int p = pos;
+    reader.setExceptionCount( this, tag, exceptionCount);
+    pos = p;
+  }
+  private void setExceptionsDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setExceptionsDone( this, tag);
+    pos = p;
+  }
+  private void setException(ClassFileReader reader, Object tag, int exceptionIndex, String exceptionType){
+    int p = pos;
+    reader.setException( this, tag, exceptionIndex, exceptionType);
+    pos = p;
+  }
+  private void setCode(ClassFileReader reader, Object tag, int maxStack, int maxLocals, int codeLength){
+    int p = pos + codeLength;
+    reader.setCode( this, tag, maxStack, maxLocals, codeLength);
+    pos = p;
+  }
+  private void setExceptionTableCount(ClassFileReader reader, Object tag, int exceptionTableCount){
+    int p = pos;
+    reader.setExceptionHandlerTableCount( this, tag, exceptionTableCount);
+    pos = p;
+  }
+  private void setExceptionTableEntry(ClassFileReader reader, Object tag, int exceptionIndex,
+          int startPc, int endPc, int handlerPc, String catchType){
+    int p = pos;
+    reader.setExceptionHandler( this, tag, exceptionIndex, startPc, endPc, handlerPc, catchType);
+    pos = p;
+  }
+  private void setExceptionTableDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setExceptionHandlerTableDone( this, tag);
+    pos = p;
+  }
+
+  private void setCodeAttributeCount(ClassFileReader reader, Object tag, int attrCount){
+    int p = pos;
+    reader.setCodeAttributeCount( this, tag, attrCount);
+    pos = p;
+  }
+  private void setCodeAttribute(ClassFileReader reader, Object tag, int attrIndex, String name, int attrLength){
+    int p = pos + attrLength;
+    reader.setCodeAttribute( this, tag, attrIndex, name, attrLength);
+    pos = p;
+  }
+  private void setCodeAttributesDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setCodeAttributesDone( this, tag);
+    pos = p;
+  }
+          
+  private void setLineNumberTableCount(ClassFileReader reader, Object tag, int lineNumberCount){
+    int p = pos;
+    reader.setLineNumberTableCount( this, tag, lineNumberCount);
+    pos = p;
+  }
+  private void setLineNumber(ClassFileReader reader, Object tag, int lineIndex, int lineNumber, int startPc){
+    int p = pos;
+    reader.setLineNumber( this, tag, lineIndex, lineNumber, startPc);
+    pos = p;
+  }
+  private void setLineNumberTableDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setLineNumberTableDone( this, tag);
+    pos = p;
+  }
+
+  private void setLocalVarTableCount(ClassFileReader reader, Object tag, int localVarCount){
+    int p = pos;
+    reader.setLocalVarTableCount( this, tag, localVarCount);
+    pos = p;
+  }
+  private void setLocalVar(ClassFileReader reader, Object tag, int localVarIndex, String varName, String descriptor,
+                      int scopeStartPc, int scopeEndPc, int slotIndex){
+    int p = pos;
+    reader.setLocalVar( this, tag, localVarIndex, varName, descriptor, scopeStartPc, scopeEndPc, slotIndex);
+    pos = p;
+  }
+  private void setLocalVarTableDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setLocalVarTableDone( this, tag);
+    pos = p;
+  }
+
+
+  private void setClassAttributeCount(ClassFileReader reader, int attrCount){
+    int p = pos;
+    reader.setClassAttributeCount( this, attrCount);
+    pos = p;
+  }
+  private void setClassAttribute(ClassFileReader reader, int attrIndex, String name, int attrLength){
+    int p = pos + attrLength;
+    reader.setClassAttribute( this, attrIndex, name, attrLength);
+    pos = p;
+  }
+  private void setClassAttributesDone(ClassFileReader reader){
+    int p = pos;
+    reader.setClassAttributesDone(this);
+    pos = p;
+  }
+
+  private void setSourceFile(ClassFileReader reader, Object tag, String pathName){
+    int p = pos;
+    reader.setSourceFile( this, tag, pathName);
+    pos = p;
+  }
+  
+  private void setBootstrapMethodCount (ClassFileReader reader, Object tag, int bootstrapMethodCount){
+    int p = pos;
+    reader.setBootstrapMethodCount( this, tag, bootstrapMethodCount);
+    pos = p;    
+  }
+  private void setBootstrapMethod (ClassFileReader reader, Object tag, int idx, 
+                                   int refKind, String cls, String mth, String descriptor, int[] cpArgs){
+    int p = pos;
+    reader.setBootstrapMethod( this, tag, idx, refKind, cls, mth, descriptor, cpArgs);
+    pos = p;    
+  }
+  private void setBootstrapMethodsDone (ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setBootstrapMethodsDone( this, tag);
+    pos = p;    
+  }
+  
+  private void setInnerClassCount(ClassFileReader reader, Object tag, int innerClsCount){
+    int p = pos;
+    reader.setInnerClassCount( this, tag, innerClsCount);
+    pos = p;
+  }
+  private void setInnerClass(ClassFileReader reader, Object tag, int innerClsIndex, String outerName, String innerName,
+          String innerSimpleName, int accessFlags){
+    int p = pos;
+    reader.setInnerClass( this, tag, innerClsIndex, outerName, innerName, innerSimpleName, accessFlags);
+    pos = p;
+  }
+  private void setEnclosingMethod(ClassFileReader reader, Object tag, String enclosingClass, String enclosedMethod, String descriptor){
+    int p = pos;
+         reader.setEnclosingMethod( this, tag, enclosingClass, enclosedMethod, descriptor);
+         pos = p;
+  }
+  private void setInnerClassesDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setInnerClassesDone(this, tag);
+    pos = p;
+  }
+
+  private void setAnnotationCount(ClassFileReader reader, Object tag, int annotationCount){
+    int p = pos;
+    reader.setAnnotationCount( this, tag, annotationCount);
+    pos = p;
+  }
+  private void setAnnotation(ClassFileReader reader, Object tag, int annotationIndex, String annotationType){
+    int p = pos;
+    reader.setAnnotation( this, tag, annotationIndex, annotationType);
+    pos = p;
+  }
+  private void setAnnotationsDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setAnnotationsDone(this, tag);
+    pos = p;
+  }
+
+  private void setTypeAnnotationCount(ClassFileReader reader, Object tag, int annotationCount){
+    int p = pos;
+    reader.setTypeAnnotationCount( this, tag, annotationCount);
+    pos = p;
+  }
+  private void setTypeAnnotationsDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setTypeAnnotationsDone(this, tag);
+    pos = p;
+  }
+
+  
+  private void setAnnotationValueCount(ClassFileReader reader, Object tag, int annotationIndex, int nValuePairs){
+    int p = pos;
+    reader.setAnnotationValueCount( this, tag, annotationIndex, nValuePairs);
+    pos = p;
+  }
+  private void setPrimitiveAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, Object val){
+    int p = pos;
+    reader.setPrimitiveAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+    pos = p;
+  }
+  private void setStringAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String s){
+    int p = pos;
+    reader.setStringAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, s);
+    pos = p;
+  }
+  private void setClassAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String typeName){
+    int p = pos;
+    reader.setClassAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, typeName);
+    pos = p;
+  }
+  private void setEnumAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String enumType, String enumValue){
+    int p = pos;
+    reader.setEnumAnnotationValue( this, tag, annotationIndex, valueIndex, elementName, arrayIndex, enumType, enumValue);
+    pos = p;
+  }
+
+  private void setAnnotationValueElementCount(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int elementCount){
+    int p = pos;
+    reader.setAnnotationValueElementCount(this, tag, annotationIndex, valueIndex, elementName, elementCount);
+    pos = p;
+  }
+  private void setAnnotationValueElementsDone(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex,
+          String elementName){
+    int p = pos;
+    reader.setAnnotationValueElementsDone(this, tag, annotationIndex, valueIndex, elementName);
+    pos = p;
+  }
+
+  public void setAnnotationValuesDone(ClassFileReader reader, Object tag, int annotationIndex){
+    int p = pos;
+    reader.setAnnotationValuesDone(this, tag, annotationIndex);
+    pos = p;
+  }
+
+  private void setParameterCount(ClassFileReader reader, Object tag, int parameterCount){
+    int p = pos;
+    reader.setParameterCount(this, tag, parameterCount);
+    pos = p;
+  }
+  private void setParameterAnnotationCount(ClassFileReader reader, Object tag, int paramIndex, int annotationCount){
+    int p = pos;
+    reader.setParameterAnnotationCount(this, tag, paramIndex, annotationCount);
+    pos = p;
+  }
+  private void setParameterAnnotation(ClassFileReader reader, Object tag, int annotationIndex, String annotationType){
+    int p = pos;
+    reader.setParameterAnnotation( this, tag, annotationIndex, annotationType);
+    pos = p;
+  }
+  private void setParameterAnnotationsDone(ClassFileReader reader, Object tag, int paramIndex){
+    int p = pos;
+    reader.setParameterAnnotationsDone(this, tag, paramIndex);
+    pos = p;
+  }
+  private void setParametersDone(ClassFileReader reader, Object tag){
+    int p = pos;
+    reader.setParametersDone(this, tag);
+    pos = p;
+  }
+
+  public void setSignature(ClassFileReader reader, Object tag, String signature){
+    int p = pos;
+    reader.setSignature(this, tag, signature);
+    pos = p;
+  }
+
+  //--- parsing
+
+  /**
+   * this is the main parsing routine that uses the ClassFileReader interface
+   * to tell clients about the classfile contents
+   *
+   * ClassFile structure: http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html#74353
+   *   u4 magic;  // 0xcafebabe
+   *   u2 minor_version;
+   *   u2 major_version;
+   *
+   *   u2 constant_pool_count;
+   *   cp_entry constant_pool[constant_pool_count-1];
+   *   u2 access_flags;
+   *
+   *   u2 this_class;
+   *   u2 super_class;
+   *
+   *   u2 interfaces_count;
+   *   u2 interfaces[interfaces_count];
+   *
+   *   u2 fields_count;
+   *   field_info fields[fields_count];
+   *
+   *   u2 methods_count;
+   *   method_info methods[methods_count];
+   *
+   *   u2 attributes_count;
+   *   attribute_info attributes[attributes_count];
+   */
+  public void parse( ClassFileReader reader)  throws ClassParseException {
+    int cpIdx;
+
+    try {
+      // yeah, cafebabe
+      int magic = readI4();
+      if (magic != 0xCAFEBABE) {
+        error("wrong magic: " + Integer.toHexString(magic));
+      }
+
+      // we don't do much with the version numbers yet
+      int minor = readU2();
+      int major = readU2();
+
+      // get the const pool
+      int cpCount = readU2();
+      cpPos = new int[cpCount];
+      cpValue = new Object[cpCount];
+      parseCp(cpCount);
+
+      // the class essentials
+      int accessFlags = readU2();
+
+      cpIdx = readU2();
+      String clsName = (String) cpValue[cpIdx];
+
+      cpIdx = readU2();
+      String superClsName = (String) cpValue[cpIdx];
+
+
+      setClass(reader, clsName, superClsName, accessFlags, cpCount);
+
+      // interfaces
+      int ifcCount = readU2();
+      parseInterfaces(reader, ifcCount);
+
+      // fields
+      int fieldCount = readU2();
+      parseFields(reader, fieldCount);
+
+      // methods
+      int methodCount = readU2();
+      parseMethods(reader, methodCount);
+
+      // class attributes
+      int classAttrCount = readU2();
+      parseClassAttributes(reader, classAttrCount);
+
+    } catch (BailOut x){
+      // nothing, just a control exception to shortcut the classfile parsing
+    }
+  }
+
+
+  //--- constpool parsing
+
+  public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException {
+    
+    int n = 0; // the number of chars in buf
+    char[] buf = new char[len]; // it can't be more, but it can be less chars
+    
+    // \u0001 - \u007f             : single byte chars:  0xxxxxxx
+    // \u0000 and \u0080 - \u07ff  : double byte chars:  110xxxxx, 10xxxxxx
+    // \u0800 - \uffff             : tripple byte chars: 1110xxxx, 10xxxxxx, 10xxxxxx
+    
+    int max = pos+len;
+    for (int i=pos; i<max; i++){
+      int c = data[i] & 0xff;
+      if ((c & 0x80) == 0){ // single byte char  0xxxxxxx
+        buf[n++] = (char)c;
+        
+      } else {
+        if ((c & 0x40) != 0){      // 11xxxxxx
+          
+          // for the sake of efficiency, we don't check for the trailing zero bit in the marker,
+          // we just mask it out
+          if ((c & 0x20) == 0) {   // 110xxxxx - double byte char
+            buf[n++] = (char) (((c & 0x1f) << 6) | (data[++i] & 0x3f));
+            
+          } else {                 // 1110xxxx - tripple byte char
+            buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f));
+          }
+          
+        } else {
+          throw new ClassParseException("malformed modified UTF-8 input: ");
+        }
+      }
+    }
+    
+    return new String(buf, 0, n);
+  }
+
+  
+  // the protected methods are called automatically, the public parse..Attr() methods
+  // are called optionally from the corresponding ClassFileReader.set..Attribute() method.
+  // Note that these calls have to provide the ClassFileReader as an argument because
+  // we might actually switch to another reader (e.g. MethodInfos for parseCodeAttr)
+
+  protected void parseCp(int cpCount)  throws ClassParseException {
+    int j = pos;
+
+    byte[] data = this.data;
+    int[] dataIdx = this.cpPos;
+    Object[] values = this.cpValue;
+
+    //--- first pass: store data index values and convert non-delegating constant values
+    // cp_entry[0] is traditionally unused
+    for (int i=1; i<cpCount; i++) {
+      switch (data[j]){
+        case 0:
+          error("illegal constpool tag");
+
+        case CONSTANT_UTF8:  // utf8_info { u1 tag; u2 length; u1 bytes[length]; }
+          dataIdx[i] = j++;
+          int len = ((data[j++]&0xff) <<8) | (data[j++]&0xff);
+
+          String s = readModifiedUTF8String( data, j, len);
+          values[i] = s;
+
+          j += len;
+          break;
+
+        case 2:
+          error("illegal constpool tag");
+
+        case CONSTANT_INTEGER:  // Integer_info { u1 tag; u4 bytes; }
+          dataIdx[i] = j++;
+
+          int iVal = (data[j++]&0xff)<<24 | (data[j++]&0xff)<<16 | (data[j++]&0xff)<<8 | (data[j++]&0xff);
+          values[i] = new Integer(iVal);
+          break;
+
+        case CONSTANT_FLOAT:  // Float_info  { u1 tag; u4 bytes; }
+          dataIdx[i] = j++;
+
+          int iBits = (data[j++]&0xff)<<24 | (data[j++]&0xff)<<16 | (data[j++]&0xff)<<8 | (data[j++]&0xff);
+          float fVal = Float.intBitsToFloat(iBits);
+          values[i] = new Float(fVal);
+          break;
+
+        case CONSTANT_LONG:  // Long_info { u1 tag; u4 high_bytes; u4 low_bytes; }
+          dataIdx[i] = j++;
+          long lVal =  (data[j++]&0xffL)<<56 | (data[j++]&0xffL)<<48 | (data[j++]&0xffL)<<40 | (data[j++]&0xffL)<<32
+                    | (data[j++]&0xffL)<<24 | (data[j++]&0xffL)<<16 | (data[j++]&0xffL)<<8 | (data[j++]&0xffL);
+          values[i] = new Long(lVal);
+
+          dataIdx[++i] = -1;  // 8 byte cpValue occupy 2 index slots
+          break;
+
+        case CONSTANT_DOUBLE:  // Double_info  { u1 tag; u4 high_bytes; u4 low_bytes; }
+          dataIdx[i] = j++;
+
+          long lBits = (data[j++]&0xffL)<<56 | (data[j++]&0xffL)<<48 | (data[j++]&0xffL)<<40 | (data[j++]&0xffL)<<32
+                    | (data[j++]&0xffL)<<24 | (data[j++]&0xffL)<<16 | (data[j++]&0xffL)<<8 | (data[j++]&0xffL);
+          double dVal = Double.longBitsToDouble(lBits);
+          values[i] = new Double(dVal);
+
+          dataIdx[++i] = -1;  // 8 byte cpValue occupy 2 index slots
+          break;
+
+        case CONSTANT_CLASS:  // Class_info { u1 tag; u2 name_index<utf8>; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.ConstantClass;
+
+          j += 3;
+          break;
+
+        case CONSTANT_STRING:  // String_info { u1 tag; u2 string_index<utf8>; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.ConstantString;
+
+          j += 3;
+          break;
+
+        case FIELD_REF:  // Fieldref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.FieldRef;
+          j += 5;
+          break;
+
+        case METHOD_REF: // Methodref_info  { u1 tag; u2 class_index; u2 name_and_type_index; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.MethodRef;
+          j += 5;
+          break;
+
+        case INTERFACE_METHOD_REF: // InterfaceMethodref_info { u1 tag; u2 class_index; u2 name_and_type_index; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.InterfaceMethodRef;
+          j += 5;
+          break;
+
+        case NAME_AND_TYPE: // NameAndType_info { u1 tag; u2 name_index<utf8>; u2 descriptor_index<utf8>; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.NameAndType;
+
+          j += 5;
+          break;
+
+        //--- the Java 8 ones
+          
+        case METHOD_HANDLE: // MethodHandle_info { u1 tag; u1 reference_kind; u2 reference_index<mthref>; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.MethodHandle;
+          j += 4;
+          break;
+          
+        case METHOD_TYPE:  // MethodType_info { u1 tag;  u2 descriptor_index<utf8>; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.MethodType;
+          j += 3;
+          break;
+
+        case INVOKE_DYNAMIC: //  InvokeDynamic_info { u1 tag; u2 bootstrap_method_attr_index; u2 name_and_type_index; }
+          dataIdx[i] = j;
+          values[i] = CpInfo.InvokeDynamic;
+          j += 5;
+          break;
+          
+        default:
+          error("illegal constpool tag: " + data[j]);
+      }
+    }
+
+    pos = j;
+
+    //--- second pass: store values of delegating constant values
+    for (int i=1; i<cpCount; i++){
+      Object v = cpValue[i];
+
+      // we store string and class constants as their utf8 string values
+      if (v == CpInfo.ConstantClass || v == CpInfo.ConstantString){
+         cpValue[i] = cpValue[u2(cpPos[i]+1)];
+      }
+    }
+  }
+
+  protected void parseInterfaces(ClassFileReader reader, int ifcCount){
+
+    setInterfaceCount(reader, ifcCount);
+
+    for (int i=0; i<ifcCount; i++){
+      int cpIdx = readU2();
+      setInterface(reader, i, classNameAt(cpIdx));
+    }
+
+    setInterfacesDone(reader);
+  }
+
+  //--- fields
+  protected void parseFields(ClassFileReader reader, int fieldCount) {
+
+    setFieldCount(reader, fieldCount);
+
+    for (int i=0; i<fieldCount; i++){
+      int accessFlags = readU2();
+
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      cpIdx = readU2();
+      String descriptor = utf8At(cpIdx);
+
+      setField(reader, i, accessFlags, name, descriptor);
+
+      int attrCount = readU2();
+      parseFieldAttributes(reader, i, attrCount);
+
+      setFieldDone(reader, i);
+    }
+
+    setFieldsDone(reader);
+  }
+
+  protected void parseFieldAttributes(ClassFileReader reader, int fieldIdx, int attrCount){
+    setFieldAttributeCount(reader, fieldIdx, attrCount);
+
+    for (int i=0; i<attrCount; i++){
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      name = internStdAttrName(cpIdx, name, stdFieldAttrs);
+
+      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
+      setFieldAttribute(reader, fieldIdx, i, name, attrLength);
+    }
+
+    setFieldAttributesDone(reader, fieldIdx);
+  }
+
+  /**
+   * optionally called by reader to obtain a ConstantValue field attribute
+   * 
+   *   ConstantValue {u2 attrName<utf8>; u4 attrLength; u2 constIndex<class|string|int|float|long|double> }
+   * 
+   * pos is at constIndex
+   */
+  public void parseConstValueAttr(ClassFileReader reader, Object tag){
+    int cpIdx = readU2();
+    setConstantValue(reader, tag, cpValue[cpIdx]);
+  }
+
+
+  //--- methods
+  protected void parseMethods(ClassFileReader reader, int methodCount) {
+
+    setMethodCount(reader, methodCount);
+
+    for (int i=0; i<methodCount; i++){
+      int accessFlags = readU2();
+
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      cpIdx = readU2();
+      String descriptor = utf8At(cpIdx);
+
+      setMethod(reader, i, accessFlags, name, descriptor);
+
+      int attrCount = readU2();
+      parseMethodAttributes(reader, i, attrCount);
+
+      setMethodDone(reader, i);
+    }
+
+    setMethodsDone(reader);
+  }
+
+  protected void parseMethodAttributes(ClassFileReader reader, int methodIdx, int attrCount){
+    setMethodAttributeCount(reader, methodIdx, attrCount);
+
+    for (int i=0; i<attrCount; i++){
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      name = internStdAttrName(cpIdx, name, stdMethodAttrs);
+
+      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
+      setMethodAttribute(reader, methodIdx, i, name, attrLength);
+    }
+
+    setMethodAttributesDone(reader, methodIdx);
+  }
+
+  public void parseExceptionAttr (ClassFileReader reader, Object tag){
+    int exceptionCount = readU2();
+    setExceptionCount(reader, tag, exceptionCount);
+
+    for (int i=0; i<exceptionCount; i++){
+      int cpIdx = readU2();
+      String exceptionType = classNameAt(cpIdx);
+      setException(reader, tag, i, exceptionType);
+    }
+
+    setExceptionsDone(reader, tag);
+  }
+
+  /**
+   * (optionally) called by reader from within the setMethodAttribute() notification
+   * This means we have recursive notification since this is a variable length
+   * attribute that has variable length attributes
+   *
+   * Code_attribute { u2 attr_name_index<utf8>; u4 attr_length;
+   *                  u2 max_stack; u2 max_locals;
+   *                  u4 code_length; u1 code[code_length];
+   *                  u2 exception_table_length;
+   *                  { u2 start_pc; u2 end_pc; u2  handler_pc; u2  catch_type<class_entry>;
+   *                  }        exception_table[exception_table_length];
+   *                  u2 attributes_count;
+   *                  attribute_info attributes[attributes_count];  }
+   *
+   * pos is at max_stack
+   */
+  public void parseCodeAttr (ClassFileReader reader, Object tag){
+    int maxStack = readU2();
+    int maxLocals = readU2();
+    int codeLength = readI4();  // no code length > 2GB supported
+    int codeStartPos = pos;
+
+    setCode(reader, tag, maxStack, maxLocals, codeLength);
+
+    int exceptionCount = readU2();
+    setExceptionTableCount(reader, tag, exceptionCount);
+
+    for (int i = 0; i < exceptionCount; i++) {
+      int startPc = readU2();
+      int endPc = readU2();
+      int handlerPc = readU2();
+
+      int cpIdx = readU2();
+      String catchType = (String) cpValue[cpIdx]; // a Constant_class
+
+      setExceptionTableEntry(reader, tag, i, startPc, endPc, handlerPc, catchType);
+    }
+    setExceptionTableDone(reader, tag);
+
+    int attrCount = readU2();
+    parseCodeAttrAttributes(reader, tag, attrCount);
+  }
+
+
+  protected void parseCodeAttrAttributes(ClassFileReader reader, Object tag, int attrCount){
+
+    setCodeAttributeCount(reader, tag, attrCount);
+
+    for (int i=0; i<attrCount; i++){
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      name = internStdAttrName(cpIdx, name, stdCodeAttrs);
+
+      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
+      setCodeAttribute(reader, tag, i, name, attrLength);
+    }
+
+    setCodeAttributesDone(reader, tag);
+  }
+
+  /**
+   * optionally called from ClassFileReader.setCodeAttribute() to parse LineNumberTables
+   *   LineNumberTable { u2 attrName; u4 attrLength;
+   *                     u2 lineCount;
+   *                     { u2 startPc; u2 lineNumber; } [lineCount] };
+   * pos is at lineCount
+   */
+  public void parseLineNumberTableAttr(ClassFileReader reader, Object tag){
+    int lineCount = readU2();
+    setLineNumberTableCount(reader, tag, lineCount);
+    
+    for (int i=0; i<lineCount; i++){
+      int startPc = readU2();
+      int lineNumber = readU2();
+      setLineNumber(reader, tag, i, lineNumber, startPc);
+    }
+
+    setLineNumberTableDone(reader, tag);
+  }
+
+  
+  /**
+   * optionally called from ClassFileReader.setCodeAttribute() to parse LocalVarTables
+   *   LocalVarTableTable { u2 attrName; u4 attrLength;
+   *                        u2 localVarCount;
+   *                        { u2 startPc; u2 lineNumber; } [lineCount] };
+   * pos is at localVarCount
+   */
+  public void parseLocalVarTableAttr(ClassFileReader reader, Object tag){
+    int localVarCount = readU2();
+    setLocalVarTableCount(reader, tag, localVarCount);
+    
+    for (int i=0; i<localVarCount; i++){
+      int startPc = readU2();
+      int length = readU2();
+      int cpIdx = readU2();
+      String varName = (String) cpValue[cpIdx];
+      cpIdx = readU2();
+      String descriptor = (String)  cpValue[cpIdx];
+      int slotIndex = readU2();
+      
+      setLocalVar(reader, tag, i, varName, descriptor, startPc, startPc+length-1, slotIndex );
+    }
+
+    setLocalVarTableDone(reader, tag);
+  }
+
+  //--- class
+  protected void parseClassAttributes(ClassFileReader reader, int attrCount){
+
+    setClassAttributeCount(reader, attrCount);
+
+    for (int i=0; i<attrCount; i++){
+      int cpIdx = readU2();
+      String name = utf8At(cpIdx);
+
+      name = internStdAttrName(cpIdx, name, stdClassAttrs);
+
+      int attrLength = readI4(); // actually U4, but we don't support 2GB attributes
+      setClassAttribute(reader, i, name, attrLength);
+    }
+
+    setClassAttributesDone(reader);
+  }
+
+
+  /**
+   * (optionally) called by ClassFileReader from within setClassAttribute() notification
+   *
+   * InnerClass { u2 nameIdx<utf8>; u4 length; u2 sourceFile<utf8>; }
+   */
+  public void parseSourceFileAttr(ClassFileReader reader, Object tag){
+    int cpIdx = readU2();
+    String pathName = utf8At(cpIdx);
+    setSourceFile(reader, tag, pathName);
+  }
+
+  /**
+   * (optionally) called by ClassFileReader from within setClassAttribute() notification
+   *
+   * InnerClass { 
+   *    u2 nameIdx<utf8>; 
+   *    u4 length;
+   *    u2 classCount;
+   *    { u2 innerCls<cls>;
+   *      u2 outerCls<cls>;
+   *      u2 innerName<utf8>; 
+   *      u2 innerAccessFlags;
+   *    } classes[classCount] }
+   * }
+   * 
+   * pos is at classCount
+   */
+  public void parseInnerClassesAttr(ClassFileReader reader, Object tag){
+    int innerClsCount = readU2();    
+    setInnerClassCount(reader, tag, innerClsCount);
+
+    for (int i = 0; i < innerClsCount; i++) {
+      int cpIdx = readU2();
+      String innerClsName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
+      cpIdx = readU2();
+      String outerClsName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
+      cpIdx = readU2();
+      String innerSimpleName = (cpIdx != 0) ? (String) cpValue[cpIdx] : null;
+      int accessFlags = readU2();
+
+      setInnerClass(reader, tag, i, outerClsName, innerClsName, innerSimpleName, accessFlags);
+    }
+
+    setInnerClassesDone(reader, tag);
+  }
+  
+  /**
+   * EnclosingMethod_attribute {
+   *   u2 attribute_name_index;
+   *   u4 attribute_length;
+   *   u2 class_index     -> Class_info { u1 tag; u2 name_index->utf8 }
+   *   u2 method_index    -> NameAndType_info { u1 tag; u2 name_index->utf8; u2 descriptor_index->utf8 }
+   * }
+   */
+  public void parseEnclosingMethodAttr(ClassFileReader reader, Object tag){
+    String enclosedMethod = null;
+    String descriptor = null;
+    
+    int cpIdx = readU2(); // start of Class_info
+    String enclosingClass =  nameAt(cpIdx);
+    
+    cpIdx = readU2(); // start of NameAndType_info
+    
+    // check if this is inside a method - we also get EnclosingMethod_infos for
+    // classes that are not immediately enclosed
+    if (cpIdx != 0){
+      enclosedMethod = nameAt(cpIdx);    
+      descriptor = descriptorAt(cpIdx);
+    }
+    
+    setEnclosingMethod(reader, tag, enclosingClass, enclosedMethod, descriptor);
+  }
+  
+  /**
+   * BootstrapMethods_attribute {
+   *     u2 attribute_name_index;
+   *     u4 attribute_length;
+   *     u2 num_bootstrap_methods;
+   *     {   u2 bootstrap_method_ref; -> MethodHandle
+   *         u2 num_bootstrap_arguments;
+   *         u2 bootstrap_arguments[num_bootstrap_arguments];
+   *     } bootstrap_methods[num_bootstrap_methods];
+   * }
+   * 
+   * pos is at num_bootstrap_methods
+  */
+  public void parseBootstrapMethodAttr (ClassFileReader reader, Object tag){
+    int nBootstrapMethods = readU2();
+    
+    setBootstrapMethodCount(reader, tag, nBootstrapMethods);
+    
+    for (int i=0; i<nBootstrapMethods; i++){
+      int cpMhIdx = readU2();
+      int nArgs = readU2();
+      int[] bmArgs = new int[nArgs];
+      for (int j=0; j<nArgs; j++){
+        bmArgs[j] = readU2();
+      }
+      
+      // kind of this method handle
+      int refKind = mhRefTypeAt(cpMhIdx);
+      
+      // CONSTANT_Methodref_info structure
+      int mrefIdx = mhMethodRefIndexAt(cpMhIdx);
+      
+      String clsName = methodClassNameAt(mrefIdx);
+      String mthName = methodNameAt(mrefIdx);
+      String descriptor = methodDescriptorAt(mrefIdx);
+      
+      setBootstrapMethod(reader, tag, i, refKind, clsName, mthName, descriptor, bmArgs);
+    }
+    
+    setBootstrapMethodsDone( reader, tag);
+  }
+  
+  String nameAt(int nameTypeInfoIdx) {
+    return utf8At(u2(cpPos[nameTypeInfoIdx] + 1));
+  }
+  
+  String descriptorAt (int nameTypeInfoIdx){
+    return utf8At( u2( cpPos[nameTypeInfoIdx]+3));
+  }
+
+// those are as per http://java.sun.com/docs/books/jvms/second_edition/ClassFileFormat-Java5.pdf
+
+  /*
+   *   element_value {
+   *     u1 tag;
+   *     union {
+   *       u2 const_value_index;
+   *       { u2 type_name_index; u2 const_name_index; } enum_const_value;
+   *       u2 class_info_index;
+   *       annotation annotation_value;
+   *       { u2 num_values; element_value values[num_values]; } array_value;
+   *     } value;
+   *   }
+   *   valid tags are primitve type codes B,C,D,F,I,J,S,Z
+   *   plus:   's'=String, 'e'=enum, 'c'=class, '@'=annotation, '['=array
+   */
+  void parseAnnotationValue(ClassFileReader reader, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex){
+    int cpIdx;
+    Object val;
+
+    int t = readUByte();
+    switch (t){
+      case 'Z':
+        // booleans have to be treated differently since there is no CONSTANT_Boolean, i.e. values are
+        // stored as CONSTANT_Integer in the constpool, i.e. the cpValue doesn't have the right type
+        cpIdx = readU2();
+        val = cpValue[cpIdx];
+        val = Boolean.valueOf((Integer)val == 1);
+        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+        break;        
+
+      case 'B':
+        cpIdx = readU2();
+        val = cpValue[cpIdx];
+        val = Byte.valueOf(((Integer)val).byteValue());
+        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+        break;
+        
+      case 'C':
+        cpIdx = readU2();
+        val = cpValue[cpIdx];
+        val = Character.valueOf((char)((Integer)val).shortValue());
+        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+        break;
+        
+      case 'S':
+        cpIdx = readU2();
+        val = cpValue[cpIdx];
+        val = Short.valueOf(((Integer)val).shortValue());
+        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+        break;
+
+      case 'I':
+      case 'F':
+      case 'D':
+      case 'J':
+        cpIdx = readU2();
+        val = cpValue[cpIdx];
+        setPrimitiveAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, val);
+        break;
+
+      case 's':
+        cpIdx = readU2();
+        String s = (String) cpValue[cpIdx];
+        setStringAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, s);
+        break;
+
+      case 'e':
+        cpIdx = readU2();
+        String enumTypeName = (String)cpValue[cpIdx];
+        cpIdx = readU2();
+        String enumConstName = (String)cpValue[cpIdx];
+        setEnumAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, enumTypeName, enumConstName);
+        break;
+
+      case 'c':
+        cpIdx = readU2();
+        String className = (String)cpValue[cpIdx];
+        setClassAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, arrayIndex, className);
+        break;
+
+      case '@':
+        parseAnnotation(reader, tag, 0, false);  // getting recursive here
+        break;
+
+      case '[':
+        int arrayLen = readU2();
+        setAnnotationValueElementCount(reader, tag, annotationIndex, valueIndex, elementName, arrayLen);
+        for (int i=0; i<arrayLen; i++){
+          parseAnnotationValue(reader, tag, annotationIndex, valueIndex, elementName, i);
+        }
+        setAnnotationValueElementsDone(reader, tag, annotationIndex, valueIndex, elementName);
+        break;
+    }
+  }
+
+  /*
+   *   annotation {
+   *     u2 type_index;
+   *     u2 num_element_value_pairs;
+   *     {
+   *       u2 element_name_index;
+   *       element_value value;
+   *     } element_value_pairs[num_element_value_pairs]
+   *   }
+   */
+  void parseAnnotation (ClassFileReader reader, Object tag, int annotationIndex, boolean isParameterAnnotation){
+    int cpIdx = readU2();
+    String annotationType = (String)cpValue[cpIdx];
+
+    if (isParameterAnnotation){
+      setParameterAnnotation(reader, tag, annotationIndex, annotationType);
+    } else {
+      setAnnotation(reader, tag, annotationIndex, annotationType);
+    }
+
+    parseAnnotationValues(reader, tag, annotationIndex);
+  }
+
+  void parseAnnotationValues (ClassFileReader reader, Object tag, int annotationIndex){
+    int nValuePairs = readU2();
+    setAnnotationValueCount(reader, tag, annotationIndex, nValuePairs);
+
+    for (int i=0; i<nValuePairs; i++){
+      int cpIdx = readU2();
+      String elementName = (String)cpValue[cpIdx];
+      parseAnnotationValue(reader, tag, annotationIndex, i, elementName, -1);
+    }
+
+    setAnnotationValuesDone(reader, tag, annotationIndex);
+  }
+  
+  /*
+   * class, field, method annotation attributes (only one per target)
+   *
+   *  Runtime[In]VisibleAnnotations_attribute {
+   *     u2 attribute_name_index;
+   *     u4 attribute_length;
+   *     u2 num_annotations;        << pos
+   *     annotation annotations[num_annotations];
+   *   }
+   */
+  public void parseAnnotationsAttr (ClassFileReader reader, Object tag){
+    int numAnnotations = readU2();
+    setAnnotationCount(reader, tag, numAnnotations);
+
+    for (int i=0; i<numAnnotations; i++){
+      parseAnnotation(reader, tag, i, false);
+    }
+
+    setAnnotationsDone(reader, tag);
+  }
+
+  
+  // JSR 308 type annotation target types
+  public static final int CLASS_TYPE_PARAMETER                 = 0x00;
+  public static final int METHOD_TYPE_PARAMETER                = 0x01;
+  public static final int CLASS_EXTENDS                        = 0x10;
+  public static final int CLASS_TYPE_PARAMETER_BOUND           = 0x11;
+  public static final int METHOD_TYPE_PARAMETER_BOUND          = 0x12;
+  public static final int FIELD                                = 0x13;
+  public static final int METHOD_RETURN                        = 0x14;
+  public static final int METHOD_RECEIVER                      = 0x15;
+  public static final int METHOD_FORMAL_PARAMETER              = 0x16;
+  public static final int THROWS                               = 0x17;
+  public static final int LOCAL_VARIABLE                       = 0x40;
+  public static final int RESOURCE_VARIABLE                    = 0x41;
+  public static final int EXCEPTION_PARAMETER                  = 0x42;
+  public static final int INSTANCEOF                           = 0x43;
+  public static final int NEW                                  = 0x44;
+  public static final int CONSTRUCTOR_REFERENCE                = 0x45;
+  public static final int METHOD_REFERENCE                     = 0x46;
+  public static final int CAST                                 = 0x47;
+  public static final int CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT = 0x48;
+  public static final int METHOD_INVOCATION_TYPE_ARGUMENT      = 0x49;
+  public static final int CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT  = 0x4a;
+  public static final int METHOD_REFERENCE_TYPE_ARGUMENT       = 0x4b;  
+  
+  public static String getTargetTypeName (int targetType){
+    switch (targetType){
+      case CLASS_TYPE_PARAMETER: return "class type parameter";
+      case METHOD_TYPE_PARAMETER: return "method type parameter";
+      case CLASS_EXTENDS: return "super class";
+      case CLASS_TYPE_PARAMETER_BOUND: return "class type parameter bound";
+      case METHOD_TYPE_PARAMETER_BOUND: return "method type parameter bound";
+      case FIELD: return "field";
+      case METHOD_RETURN: return "method return";
+      case METHOD_RECEIVER: return "method receiver";
+      case METHOD_FORMAL_PARAMETER: return "method formal parameter";
+      case THROWS: return "throws";
+      case LOCAL_VARIABLE: return "local variable";
+      case RESOURCE_VARIABLE: return "resource variable";
+      case EXCEPTION_PARAMETER: return "exception parameter";
+      case INSTANCEOF: return "instanceof";
+      case NEW: return "new";
+      case CONSTRUCTOR_REFERENCE: return "ctor reference";
+      case METHOD_REFERENCE: return "method reference";
+      case CAST: return "case";
+      case METHOD_INVOCATION_TYPE_ARGUMENT: return "method invocation type argument";
+      case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: return "ctor reference type argument";
+      case METHOD_REFERENCE_TYPE_ARGUMENT: return "method reference type argument";
+      default:
+        return "<unknown target type 0x" + Integer.toHexString(targetType);
+    }
+  }
+  
+  public static String getTypePathEncoding (short[] typePath){
+    if (typePath == null){
+      return "()";
+    }
+    
+    StringBuffer sb = new StringBuffer();
+    for (int i=0; i<typePath.length;i++){
+      int e = typePath[i];
+      sb.append('(');
+      sb.append( Integer.toString((e>>8) & 0xff));
+      sb.append( Integer.toString(e & 0xff));
+      sb.append(')');
+    }
+    
+    return sb.toString();
+  }
+  
+  public static String getScopeEncoding (long[] scopeEntries){
+    StringBuffer sb = new StringBuffer();
+    for (int i=0; i<scopeEntries.length;i++){
+      long e = scopeEntries[i];
+      int slotIndex = (int)(e & 0xffff);
+      int length = (int)((e >> 16) & 0xffff);
+      int startPc = (int)((e >> 32) & 0xffff);
+      
+      if (i>0){
+        sb.append(',');
+      }
+      
+      sb.append('[');
+      sb.append( Integer.toString(startPc));
+      sb.append("..");
+      sb.append( Integer.toString(startPc + length-1));
+      sb.append("]#");
+      sb.append(slotIndex);
+    }
+    
+    return sb.toString();
+  }
+  
+  // JSR 308 type annotation, which adds 3 fields to the old annotation structure
+  //
+  //  type_annotation {
+  //      u1 target_type;        // targeted program element (sec 3.2)
+  //      union {                // ?? this is probably packed - variable size unions make no sense
+  //          type_parameter_target;
+  //          supertype_target;
+  //          type_parameter_bound_target;
+  //          empty_target;
+  //          method_formal_parameter_target;
+  //          throws_target;
+  //          localvar_target;
+  //          catch_target;
+  //          offset_target;
+  //          type_argument_target;
+  //      } target_info;         // targeted program element (sec 3.3)
+  //
+  //      type_path target_path; // encoding of annotation position in compound type (array, generic, etc., sec 3.4)
+  //
+  //                             // standard annotation fields
+  //      u2 type_index;         // the annotation type
+  //      u2 num_element_value_pairs;
+  //      {
+  //          u2 element_name_index;
+  //          element_value value;
+  //      } element_value_pairs[num_element_value_pairs];
+  //  }
+  //
+  //  struct type_path {
+  //    u1              path_length;
+  //    type_path_entry path[path_length];
+  //  }
+  //
+  //  struct type_path_entry {
+  //    u1 type_path_kind;
+  //        // 0: deeper in array type
+  //        // 1: deeper in nested type
+  //        // 2: bound of wildcard typearg
+  //        // 3: type argument of parameterized type
+  //    u1 type_argument_index;
+  //        // 0, if type_path_kind == 0,1,2
+  //        // 0-based index of type arg in parameterized type if type_path_kind i== 3
+  //  }
+  
+  int getTargetInfoSize (int targetType){
+    int len = 3; // max static length are xx_TYPE_ARGUMENTs
+    if (targetType == LOCAL_VARIABLE || targetType == RESOURCE_VARIABLE){
+      len = Math.max( len, u2(pos) * 6); // three u2 values per entry
+    }
+    
+    return len;
+  }
+
+  int getTypePathSize (short[] typePath){
+    int typePathSize = 1;
+    if (typePath != null) {
+      typePathSize += typePath.length * 2;
+    }
+    return typePathSize;
+  }
+  
+  
+  short[] readTypePath (){
+    short[] typePath = null;
+    
+    int pathLength = readUByte();
+    if (pathLength > 0){
+      typePath = new short[pathLength];
+      for (int i=0; i<pathLength; i++){
+        int pathKind = (short)readUByte();
+        int argIdx = (short)readUByte();
+        typePath[i]= (short)((pathKind << 8) | argIdx);
+      }
+    }
+    
+    return typePath;
+  }
+
+  String readAnnotationType (){
+    int cpIdx = readU2();
+    String annotationType = (String)cpValue[cpIdx];
+    return annotationType;
+  }
+
+  void setTypeAnnotation (ClassFileReader reader, Object tag, int annotationIndex) {
+    int targetType = readUByte();
+    
+    switch (targetType){
+      case CLASS_TYPE_PARAMETER:
+      case METHOD_TYPE_PARAMETER: {
+        // type_parameter_target { u1 type_parameter_index; }
+        int typeParamIdx = readUByte();
+        reader.setTypeParameterAnnotation( this, tag, annotationIndex, targetType, typeParamIdx, readTypePath(), readAnnotationType());
+        break;
+      } 
+      case CLASS_EXTENDS: {
+        // supertype_target { u2 supertype_index; }
+        int superTypeIdx = readU2();
+        reader.setSuperTypeAnnotation( this, tag, annotationIndex, targetType, superTypeIdx, readTypePath(), readAnnotationType());
+        break;
+      }
+      case CLASS_TYPE_PARAMETER_BOUND:
+      case METHOD_TYPE_PARAMETER_BOUND: {
+        // type_parameter_bound_target { u1 type_parameter_index; u1 bound_index; }
+        int typeParamIdx = readUByte();
+        int boundIdx = readUByte();
+        reader.setTypeParameterBoundAnnotation(this, tag, annotationIndex, targetType, typeParamIdx, boundIdx, readTypePath(), readAnnotationType());
+        break;
+      }
+      case METHOD_RETURN:
+      case METHOD_RECEIVER:
+      case FIELD:
+        // empty_target {}
+        reader.setTypeAnnotation( this, tag, annotationIndex, targetType, readTypePath(), readAnnotationType());
+        break;
+        
+      case METHOD_FORMAL_PARAMETER: {
+        // method_formal_parameter_target { u1 method_formal_parameter_index; }
+        int formalParamIdx = readUByte();
+        reader.setFormalParameterAnnotation( this, tag, annotationIndex, targetType, formalParamIdx, readTypePath(), readAnnotationType());
+        break;
+      }
+      case THROWS: {
+        // throws_target { u2 throws_type_index; }
+        int throwsTypeIdx = readU2();
+        reader.setThrowsAnnotation( this, tag, annotationIndex, targetType, throwsTypeIdx, readTypePath(), readAnnotationType());        
+        break;
+      } 
+      case LOCAL_VARIABLE:
+      case RESOURCE_VARIABLE: {
+        // this can't just refer to a LocalVarInfo since those depend on debug compile options
+        //
+        //  localvar_target {
+        //      u2 table_length;  // number of entries, not bytes
+        //      {
+        //          u2 start_pc;
+        //          u2 length; // bytecode offset length
+        //          u2 index;  // local var idx
+        //      } table[table_length];
+        //  }
+        int tableLength = readU2();
+        long[] scopeEntries = new long[tableLength];
+        for (int i=0; i<tableLength; i++){
+          int startPc = readU2();
+          int length = readU2();
+          int slotIdx = readU2();
+          scopeEntries[i] = ((long)startPc << 32) | ((long)length << 16) | slotIdx;
+        }
+        reader.setVariableAnnotation( this, tag, annotationIndex, targetType, scopeEntries, readTypePath(), readAnnotationType());
+        break;
+      }
+      case EXCEPTION_PARAMETER: {
+        // catch_target { u2 exception_table_index; }
+        int exceptionIdx = readU2();
+        reader.setExceptionParameterAnnotation( this, tag, annotationIndex, targetType, exceptionIdx, readTypePath(), readAnnotationType());        
+        break;
+      }
+      case INSTANCEOF:
+      case METHOD_REFERENCE:
+      case CONSTRUCTOR_REFERENCE:
+      case NEW: {
+        // offset_target { u2 offset; }   // insn offset within bytecode
+        int offset = readU2();
+        reader.setBytecodeAnnotation(this, tag, annotationIndex, targetType, offset, readTypePath(), readAnnotationType());
+        break;
+      }
+      case CAST:
+      case CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT:
+      case METHOD_INVOCATION_TYPE_ARGUMENT:
+      case CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT:
+      case METHOD_REFERENCE_TYPE_ARGUMENT: {
+        //  type_argument_target {
+        //      u2 offset;
+        //      u1 type_argument_index;
+        //  }
+        int offset = readU2();
+        int typeArgIdx = readUByte();
+        reader.setBytecodeTypeParameterAnnotation(this, tag, annotationIndex, targetType, offset, typeArgIdx, readTypePath(), readAnnotationType());
+        break;
+      }
+      
+      default:
+        // <2do - report this to the reader
+        throw new RuntimeException("unknown type annotation target: 0x" + Integer.toHexString(targetType));
+    }
+  }
+
+  
+  void parseTypeAnnotation (ClassFileReader reader, Object tag, int annotationIndex) {
+   
+    // this does the respective setXTypeAnnotation() reader callback
+    //dumpData(pos, 16);
+    setTypeAnnotation(reader, tag, annotationIndex);
+    
+    // now set the annotation value pairs
+    parseAnnotationValues( reader, tag, annotationIndex);
+  }
+  
+  /*
+   * Runtime[In]VisibleTypeAnnotations_attribute {
+   *    u2 attribute_name_index;
+   *    u4 attribute_length;
+   *    u2 num_annotations;
+   *    type_annotation annotations[num_annotations];
+   * }
+   */
+  public void parseTypeAnnotationsAttr (ClassFileReader reader, Object tag) {
+    int numAnnotations = readU2();
+    setTypeAnnotationCount(reader, tag, numAnnotations);
+
+    for (int i=0; i<numAnnotations; i++){
+      parseTypeAnnotation(reader, tag, i);
+    }
+
+    setTypeAnnotationsDone(reader, tag);
+  }
+  
+  /*
+   *   RuntimeInvisibleParameterAnnotations_attribute {
+   *     u2 attribute_name_index;
+   *     u4 attribute_length;
+   *     u1 num_parameters; << pos
+   *     {
+   *       u2 num_annotations;
+   *       annotation annotations[num_annotations];
+   *     } parameter_annotations[num_parameters];
+   *   }
+   */
+   public void parseParameterAnnotationsAttr(ClassFileReader reader, Object tag){
+     int numParameters = readUByte();
+     setParameterCount(reader, tag, numParameters);
+     for (int i=0; i<numParameters; i++){
+       int numAnnotations = readU2();
+
+       setParameterAnnotationCount(reader, tag, i, numAnnotations);
+       for (int j=0; j<numAnnotations; j++){
+         parseAnnotation(reader, tag, j, true);
+       }
+       setParameterAnnotationsDone(reader, tag, i);
+     }
+     setParametersDone(reader, tag);
+   }
+
+  /**
+   *  Signature_attribute {
+   *    u2 attribute_name_index;
+   *    u4 attr-length;
+   *    u2 signature-index << pos
+   *  }
+   */
+   public void parseSignatureAttr(ClassFileReader reader, Object tag){
+     int cpIdx = readU2();
+     setSignature(reader, tag, utf8At(cpIdx));
+   }
+
+
+  /**
+   *    AnnotationDefault_attribute {
+   *      u2 attribute_name_index;
+   *      u4 attribute_length;
+   *      element_value default_value; << pos
+   *    }
+   */
+   public void parseAnnotationDefaultAttr(ClassFileReader reader, Object tag){
+     parseAnnotationValue(reader, tag, -1, -1, null, -1);
+   }
+
+
+//   EnclosingMethod_attribute {
+//     u2 attribute_name_index;
+//     u4 attribute_length;
+//     u2 class_index
+//     u2 method_index;
+//   }
+
+//   LocalVariableTypeTable_attribute {  // Code attr
+//     u2 attribute_name_index;
+//     u4 attribute_length;
+//     u2 local_variable_type_table_length;
+//     {
+//       u2 start_pc;
+//       u2 length;
+//       u2 name_index;
+//       u2 signature_index;
+//       u2 index;
+//     } local_variable_type_table[local_variable_type_table_length];
+//   }
+
+
+
+
+  public void parseBytecode(JVMByteCodeReader reader, Object tag, int codeLength){
+    int localVarIndex;
+    int cpIdx;
+    int constVal;
+    int offset;
+    int defaultOffset;
+
+    boolean isWide = false; // modifier for Xload,Xstore,ret and iinc
+
+    int startPos = pos;
+    int endPos = pos+codeLength;
+    int nextPos;
+
+
+    while (pos < endPos){
+      pc = pos - startPos;
+
+      int opcode = readUByte();
+      switch (opcode){
+        case 0: // nop
+          reader.nop();
+          break;
+        case 1:  // aconst_null
+          reader.aconst_null();
+          break;
+        case 2: // iconst_m1
+          reader.iconst_m1();
+          break;
+        case 3: // iconst_0
+          reader.iconst_0();
+          break;
+        case 4: // iconst_1
+          reader.iconst_1();
+          break;
+        case 5: // iconst_2
+          reader.iconst_2();
+          break;
+        case 6: // iconst_3
+          reader.iconst_3();
+          break;
+        case 7: // iconst_4
+          reader.iconst_4();
+          break;
+        case 8: // iconst_5
+          reader.iconst_5();
+          break;
+        case 9: // lconst_0
+          reader.lconst_0();
+          break;
+        case 10: // lconst_1
+          reader.lconst_1();
+          break;
+        case 11: // fconst_0
+          reader.fconst_0();
+          break;
+        case 12: // fconst_1
+          reader.fconst_1();
+          break;
+        case 13: // fconst_2
+          reader.fconst_2();
+          break;
+        case 14: // dconst_0
+          reader.dconst_0();
+          break;
+        case 15: // dconst_1
+          reader.dconst_1();
+          break;
+        case 16: // bipush
+          constVal = readByte();
+          reader.bipush(constVal);
+          break;
+        case 17: // sipush
+          constVal = readI2();
+          reader.sipush(constVal);
+          break;
+        case 18: // ldc
+          cpIdx = readUByte();
+          reader.ldc_(cpIdx);
+          break;
+        case 19: // ldc_w
+          cpIdx = readU2();
+          reader.ldc_w_(cpIdx);
+          break;
+        case 20: // ldc2_w
+          cpIdx = readU2();
+          reader.ldc2_w(cpIdx);
+          break;
+        case 21: // iload
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.iload(localVarIndex);
+          break;
+        case 22: // lload
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.lload(localVarIndex);
+          break;
+        case 23: // fload
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.fload(localVarIndex);
+          break;
+        case 24: // dload
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.dload(localVarIndex);
+          break;
+        case 25: // aload
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.aload(localVarIndex);
+          break;
+        case 26: // iload_0
+          reader.iload_0();
+          break;
+        case 27: // iload_1
+          reader.iload_1();
+          break;
+        case 28: // iload_2
+          reader.iload_2();
+          break;
+        case 29: // iload_3
+          reader.iload_3();
+          break;
+        case 30: // lload_0
+          reader.lload_0();
+          break;
+        case 31: // lload_1
+          reader.lload_1();
+          break;
+        case 32: // lload_2
+          reader.lload_2();
+          break;
+        case 33: // lload_3
+          reader.lload_3();
+          break;
+        case 34: // fload_0
+          reader.fload_0();
+          break;
+        case 35: // fload_1
+          reader.fload_1();
+          break;
+        case 36: // fload_2
+          reader.fload_2();
+          break;
+        case 37: // fload_3
+          reader.fload_3();
+          break;
+        case 38: // dload_0
+          reader.dload_0();
+          break;
+        case 39: // dload_1
+          reader.dload_1();
+          break;
+        case 40: // dload_2
+          reader.dload_2();
+          break;
+        case 41: // dload_3
+          reader.dload_3();
+          break;
+        case 42: // aload_0
+          reader.aload_0();
+          break;
+        case 43: // aload_1
+          reader.aload_1();
+          break;
+        case 44: // aload_2
+          reader.aload_2();
+          break;
+        case 45: // aload_3
+          reader.aload_3();
+          break;
+        case 46: // iaload
+          reader.iaload();
+          break;
+        case 47: // laload
+          reader.laload();
+          break;
+        case 48: // faload
+          reader.faload();
+          break;
+        case 49: // daload
+          reader.daload();
+          break;
+        case 50: // aaload
+          reader.aaload();
+          break;
+        case 51: // baload
+          reader.baload();
+          break;
+        case 52: // caload
+          reader.caload();
+          break;
+        case 53: // saload
+          reader.saload();
+          break;
+        case 54: // istore
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.istore(localVarIndex);
+          break;
+        case 55: // lstore
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.lstore(localVarIndex);
+          break;
+        case 56: // fstore
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.fstore(localVarIndex);
+          break;
+        case 57: // dstore
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.dstore(localVarIndex);
+          break;
+        case 58: // astore
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.astore(localVarIndex);
+          break;
+        case 59: // istore_0
+          reader.istore_0();
+          break;
+        case 60: // istore_1
+          reader.istore_1();
+          break;
+        case 61: // istore_2
+          reader.istore_2();
+          break;
+        case 62: // istore_3
+          reader.istore_3();
+          break;
+        case 63: // lstore_0
+          reader.lstore_0();
+          break;
+        case 64: // lstore_1
+          reader.lstore_1();
+          break;
+        case 65: // lstore_2
+          reader.lstore_2();
+          break;
+        case 66: // lstore_3
+          reader.lstore_3();
+          break;
+        case 67: // fstore_0
+          reader.fstore_0();
+          break;
+        case 68: // fstore_1
+          reader.fstore_1();
+          break;
+        case 69: // fstore_2
+          reader.fstore_2();
+          break;
+        case 70: // fstore_3
+          reader.fstore_3();
+          break;
+        case 71: //dstore_0
+          reader.dstore_0();
+          break;
+        case 72: //dstore_1
+          reader.dstore_1();
+          break;
+        case 73: //dstore_2
+          reader.dstore_2();
+          break;
+        case 74: //dstore_3
+          reader.dstore_3();
+          break;
+        case 75: // astore_0
+          reader.astore_0();
+          break;
+        case 76: // astore_1
+          reader.astore_1();
+          break;
+        case 77: // astore_2
+          reader.astore_2();
+          break;
+        case 78: // astore_3
+          reader.astore_3();
+          break;
+        case 79: // iastore
+          reader.iastore();
+          break;
+        case 80: // lastore
+          reader.lastore();
+          break;
+        case 81: // fastore
+          reader.fastore();
+          break;
+        case 82: // dastore
+          reader.dastore();
+          break;
+        case 83: // aastore
+          reader.aastore();
+          break;
+        case 84: // bastore
+          reader.bastore();
+          break;
+        case 85: // castore
+          reader.castore();
+          break;
+        case 86: // sastore
+          reader.sastore();
+          break;
+        case 87: // pop
+          reader.pop();
+          break;
+        case 88: // pop2
+          reader.pop2();
+          break;
+        case 89: // dup
+          reader.dup();
+          break;
+        case 90: // dup_x1
+          reader.dup_x1();
+          break;
+        case 91: // dup_x2
+          reader.dup_x2();
+          break;
+        case 92: // dup2
+          reader.dup2();
+          break;
+        case 93: // dup2_x1
+          reader.dup2_x1();
+          break;
+        case 94: // dup2_x2
+          reader.dup2_x2();
+          break;
+        case 95: // swap
+          reader.swap();
+          break;
+        case 96: // iadd
+          reader.iadd();
+          break;
+        case 97: // ladd
+          reader.ladd();
+          break;
+        case 98: // fadd
+          reader.fadd();
+          break;
+        case 99: // dadd
+          reader.dadd();
+          break;
+        case 100: // isub
+          reader.isub();
+          break;
+        case 101: // lsub
+          reader.lsub();
+          break;
+        case 102: // fsub
+          reader.fsub();
+          break;
+        case 103: // dsub
+          reader.dsub();
+          break;
+        case 104: // imul
+          reader.imul();
+          break;
+        case 105: // lmul
+          reader.lmul();
+          break;
+        case 106: // fmul
+          reader.fmul();
+          break;
+        case 107: // dmul
+          reader.dmul();
+          break;
+        case 108: // idiv
+          reader.idiv();
+          break;
+        case 109: // ldiv
+          reader.ldiv();
+          break;
+        case 110: // fdiv
+          reader.fdiv();
+          break;
+        case 111: //ddiv
+          reader.ddiv();
+          break;
+        case 112: // irem
+          reader.irem();
+          break;
+        case 113: // lrem
+          reader.lrem();
+          break;
+        case 114: // frem
+          reader.frem();
+          break;
+        case 115: // drem
+          reader.drem();
+          break;
+        case 116: // ineg
+          reader.ineg();
+          break;
+        case 117: // lneg
+          reader.lneg();
+          break;
+        case 118: // fneg
+          reader.fneg();
+          break;
+        case 119: // dneg
+          reader.dneg();
+          break;
+        case 120: // ishl
+          reader.ishl();
+          break;
+        case 121: // lshl
+          reader.lshl();
+          break;
+        case 122: // ishr
+          reader.ishr();
+          break;
+        case 123: // lshr
+          reader.lshr();
+          break;
+        case 124: // iushr
+          reader.iushr();
+          break;
+        case 125: // lushr
+          reader.lushr();
+          break;
+        case 126: // iand
+          reader.iand();
+          break;
+        case 127: // land
+          reader.land();
+          break;
+        case 128: // ior
+          reader.ior();
+          break;
+        case 129: // lor
+          reader.lor();
+          break;
+        case 130: // ixor
+          reader.ixor();
+          break;
+        case 131: // lxor
+          reader.lxor();
+          break;
+        case 132: // iinc
+          if (isWide){
+            localVarIndex = readU2();
+            constVal = readI2();
+          } else {
+            localVarIndex = readUByte();
+            constVal = readByte();
+          }
+          reader.iinc(localVarIndex, constVal);
+          break;
+        case 133: // i2l
+          reader.i2l();
+          break;
+        case 134: // i2f
+          reader.i2f();
+          break;
+        case 135: // i2d
+          reader.i2d();
+          break;
+        case 136: // l2i
+          reader.l2i();
+          break;
+        case 137: // l2f
+          reader.l2f();
+          break;
+        case 138: // l2d
+          reader.l2d();
+          break;
+        case 139: // f2i
+          reader.f2i();
+          break;
+        case 140: // f2l
+          reader.f2l();
+          break;
+        case 141: // f2d
+          reader.f2d();
+          break;
+        case 142: // d2i
+          reader.d2i();
+          break;
+        case 143: // d2l
+          reader.d2l();
+          break;
+        case 144: // d2f
+          reader.d2f();
+          break;
+        case 145: // i2b
+          reader.i2b();
+          break;
+        case 146: // i2c
+          reader.i2c();
+          break;
+        case 147: // i2s
+          reader.i2s();
+          break;
+        case 148: // lcmp
+          reader.lcmp();
+          break;
+        case 149: // fcmpl
+          reader.fcmpl();
+          break;
+        case 150: // fcmpg
+          reader.fcmpg();
+          break;
+        case 151: // dcmpl
+          reader.dcmpl();
+          break;
+        case 152: // dcmpg
+          reader.dcmpg();
+          break;
+        case 153: // ifeq
+          offset = readI2();
+          reader.ifeq(offset);
+          break;
+        case 154: // ifne
+          offset = readI2();
+          reader.ifne(offset);
+          break;
+        case 155: // iflt
+          offset = readI2();
+          reader.iflt(offset);
+          break;
+        case 156: // ifge
+          offset = readI2();
+          reader.ifge(offset);
+          break;
+        case 157: // ifgt
+          offset = readI2();
+          reader.ifgt(offset);
+          break;
+        case 158: // ifle
+          offset = readI2();
+          reader.ifle(offset);
+          break;
+        case 159: // if_icmpeq
+          offset = readI2();
+          reader.if_icmpeq(offset);
+          break;
+        case 160: // if_icmpne
+          offset = readI2();
+          reader.if_icmpne(offset);
+          break;
+        case 161: // if_icmplt
+          offset = readI2();
+          reader.if_icmplt(offset);
+          break;
+        case 162: // if_icmpge
+          offset = readI2();
+          reader.if_icmpge(offset);
+          break;
+        case 163: // if_icmpgt
+          offset = readI2();
+          reader.if_icmpgt(offset);
+          break;
+        case 164: // if_icmple
+          offset = readI2();
+          reader.if_icmple(offset);
+          break;
+        case 165: // if_acmpeq
+          offset = readI2();
+          reader.if_acmpeq(offset);
+          break;
+        case 166: // if_acmpne
+          offset = readI2();
+          reader.if_acmpne(offset);
+          break;
+        case 167: // goto
+          offset = readI2();
+          reader.goto_(offset);
+          break;
+        case 168: // jsr
+          offset = readI2();
+          reader.jsr(offset);
+          break;
+        case 169: // ret
+          localVarIndex = isWide ? readU2() : readUByte();
+          reader.ret(localVarIndex);
+          break;
+        case 170: // tableswitch
+          pos = (((pc+4)>>2)<<2)+startPos; // skip over padding
+
+          defaultOffset = readI4();
+          int low = readI4();
+          int high = readI4();
+
+          int len = high-low+1;
+          nextPos = pos + len*4;
+          reader.tableswitch(defaultOffset, low, high);
+          pos = nextPos;
+          break;
+        case 171: // lookupswitch
+          pos = (((pc+4)>>2)<<2)+startPos; // skip over padding
+
+          defaultOffset = readI4();
+          int nPairs = readI4();
+
+          nextPos = pos + (nPairs*8);
+          reader.lookupswitch(defaultOffset, nPairs);
+          pos = nextPos;
+          break;
+        case 172: // ireturn
+          reader.ireturn();
+          break;
+        case 173: // lreturn
+          reader.lreturn();
+          break;
+        case 174: // freturn
+          reader.freturn();
+          break;
+        case 175: // dreturn
+          reader.dreturn();
+          break;
+        case 176: // areturn
+          reader.areturn();
+          break;
+        case 177: // return
+          reader.return_();
+          break;
+        case 178: // getstatic
+          cpIdx = readU2(); // CP index of fieldRef
+          reader.getstatic(cpIdx);
+          break;
+        case 179: // putstatic
+          cpIdx = readU2(); // CP index of fieldRef
+          reader.putstatic(cpIdx);
+          break;
+        case 180: // getfield
+          cpIdx = readU2(); // CP index of fieldRef
+          reader.getfield(cpIdx);
+          break;
+        case 181: // putfield
+          cpIdx = readU2(); // CP index of fieldRef
+          reader.putfield(cpIdx);
+          break;
+        case 182: // invokevirtual
+          cpIdx = readU2(); // CP index of methodRef
+          reader.invokevirtual(cpIdx);
+          break;
+        case 183: // invokespecial
+          cpIdx = readU2(); // CP index of methodRef
+          reader.invokespecial(cpIdx);
+          break;
+        case 184: // invokestatic
+          cpIdx = readU2(); // CP index of methodRef
+          reader.invokestatic(cpIdx);
+          break;
+        case 185: // invokeinterface
+          cpIdx = readU2(); // CP index of methodRef
+          int count = readUByte();
+          int zero = readUByte(); // must be 0
+          reader.invokeinterface(cpIdx, count, zero);
+          break;
+        case 186: // invokedynamic
+          cpIdx = readU2(); // CP index of bootstrap method
+          readUByte();  // 0
+          readUByte(); //  0
+          reader.invokedynamic(cpIdx);
+          break;
+        case 187: // new
+          cpIdx = readU2();
+          reader.new_(cpIdx);
+          break;
+        case 188: // newarray
+          int aType = readUByte();
+          reader.newarray(aType);
+          break;
+        case 189: // anewarray
+          cpIdx = readU2(); // CP index of component type
+          reader.anewarray(cpIdx);
+          break;
+        case 190: // arraylength
+          reader.arraylength();
+          break;
+        case 191: // athrow
+          reader.athrow();
+          break;
+        case 192: // checkcast
+          cpIdx = readU2(); // cast type cp index
+          reader.checkcast(cpIdx);
+          break;
+        case 193: // instanceof
+          cpIdx = readU2(); // check type cp index
+          reader.instanceof_(cpIdx);
+          break;
+        case 194: // monitorenter
+          reader.monitorenter();
+          break;
+        case 195: // monitorexit
+          reader.monitorexit();
+          break;
+        case 196: // wide
+          isWide = true;
+          // affects immediate operand width if next bytecode is:
+          //  iload,fload,aload,lload,dload,
+          //  istore,fstore,astore,lstore,dstore
+          //  ret
+          reader.wide();
+          continue;
+        case 197: // multianewarray
+          cpIdx = readU2();
+          int dimensions = readUByte();
+          reader.multianewarray(cpIdx, dimensions);
+          break;
+        case 198: // ifnull
+          offset = readI2();
+          reader.ifnull(offset);
+          break;
+        case 199: // ifnonnull
+          offset = readI2();
+          reader.ifnonnull(offset);
+          break;
+        case 200: // goto_w
+          offset = readI4();
+          reader.goto_w(offset);
+          break;
+        case 201: // jsr_w
+          offset = readI4();
+          reader.jsr_w(offset);
+          break;
+          
+          
+        default:
+          reader.unknown(opcode);
+      }
+
+      isWide = false; // reset wide modifier
+    }
+
+  }
+
+  //--- those can only be called from within a JVMByteCodeReader.tableswitch() notification
+  public void parseTableSwitchEntries(JVMByteCodeReader reader, int low, int high){
+    for (int val=low; val<=high; val++){
+      int offset = readI4();
+      reader.tableswitchEntry(val, offset);
+    }
+  }
+  public int getTableSwitchOffset(int low, int high, int defaultOffset, int val){
+    if (val < low || val > high){
+      return defaultOffset;
+    }
+
+    int n = Math.abs(val - low);
+    pos += n*4;
+    int pcOffset = readI4();
+
+    return pcOffset;
+  }
+
+  //--- those can only be called from within a JVMByteCodeReader.lookupswitch() notification
+  public void parseLookupSwitchEntries(JVMByteCodeReader reader, int nEntries){
+    for (int i=0; i<nEntries; i++){
+      int value = readI4();
+      int offset = readI4();
+      reader.lookupswitchEntry(i, value, offset);
+    }
+  }
+  public int getLookupSwitchOffset(int nEntries, int defaultOffset, int val){
+    for (int i=0; i<nEntries; i++){
+      int match = readI4();
+      if (val > match){
+        pos +=4;
+      } else if (val == match) {
+        int offset = readI4();
+        return offset;
+      } else {
+        break;
+      }
+    }
+    return defaultOffset;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/ClassFilePrinter.java b/src/main/gov/nasa/jpf/jvm/ClassFilePrinter.java
new file mode 100644 (file)
index 0000000..d41cdd9
--- /dev/null
@@ -0,0 +1,780 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.jvm.JVMByteCodePrinter;
+import gov.nasa.jpf.util.StructuredPrinter;
+import gov.nasa.jpf.vm.ClassParseException;
+import java.io.PrintWriter;
+
+/**
+ * simple tool to print contents of a classfile
+ *
+ * <2do> use indentation level variable and formated output
+ */
+public class ClassFilePrinter extends StructuredPrinter implements ClassFileReader {
+
+
+  public static void main(String[] args){
+    ClassFilePrinter printer = new ClassFilePrinter();
+
+    try {
+      ClassFile cf = new ClassFile(args[0]);
+      cf.parse(printer);
+
+    } catch (ClassParseException cfx){
+      cfx.printStackTrace();
+    }
+  }
+
+  @Override
+  public void setClass(ClassFile cf, String clsName, String superClsName, int flags, int cpCount) {
+    printSectionHeader( "constpool");
+    printCp(pw,cf);
+
+    incIndent();
+    printSectionHeader( "class");
+    pw.printf("%sclass=%s\n", indent, clsName);
+    pw.printf("%ssuperclass=%s\n", indent, superClsName);
+    pw.printf("%sflags=0x%X\n", indent, flags);
+  }
+
+  //--- interfaces
+  @Override
+  public void setInterfaceCount(ClassFile cf, int ifcCount) {
+    pw.printf("%sinterface count=%d\n", indent, ifcCount);
+    incIndent();
+  }
+
+  @Override
+  public void setInterface(ClassFile cf, int ifcIndex, String ifcName) {
+    pw.printf("%s[%d]: %s\n", indent, ifcIndex, ifcName);
+  }
+
+  @Override
+  public void setInterfacesDone(ClassFile cf){
+    decIndent();
+  }
+
+  //--- fields
+  @Override
+  public void setFieldCount(ClassFile cf, int fieldCount) {
+    printSectionHeader( "fields");
+    pw.printf( "%sfield count=%d\n", indent, fieldCount);
+  }
+
+  @Override
+  public void setField(ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor) {
+    pw.printf("%s[%d]: %s, type=%s,flags=0x%X", indent, fieldIndex, name, descriptor, accessFlags);
+  }
+
+  @Override
+  public void setFieldAttributeCount(ClassFile cf, int fieldIndex, int attrCount) {
+    pw.printf(", attr count=%d\n", attrCount);
+    incIndent();
+  }
+
+  @Override
+  public void setFieldAttribute(ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength) {
+    pw.printf("%s[%d]: %s", indent, attrIndex, name);
+
+    if (name == ClassFile.CONST_VALUE_ATTR) {
+      cf.parseConstValueAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
+        cf.parseTypeAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.SIGNATURE_ATTR){
+      cf.parseSignatureAttr(this, null);
+
+    } else {
+      pw.printf(" ,length=%d,data=[",attrLength );
+      printRawData(pw, cf, attrLength, 10);
+      pw.println(']');
+    }
+  }
+
+  @Override
+  public void setFieldAttributesDone(ClassFile cf, int fieldIndex){
+    decIndent();
+  }
+
+  @Override
+  public void setFieldDone(ClassFile cf, int fieldIndex){
+    pw.println();
+  }
+
+  @Override
+  public void setFieldsDone(ClassFile cf){
+  }
+
+  @Override
+  public void setConstantValue(ClassFile cf, Object tag, Object value) {
+    pw.printf(" value=%s\n", value);
+  }
+
+  //--- methods
+  @Override
+  public void setMethodCount(ClassFile cf, int methodCount) {
+    printSectionHeader( "methods");
+    pw.printf( "%smethod count=%d\n", indent, methodCount);
+  }
+
+  @Override
+  public void setMethod(ClassFile cf, int methodIndex, int accessFlags, String name, String descriptor) {
+    pw.printf("%s[%d]: %s%s, flags=0x%X", indent, methodIndex, name, descriptor, accessFlags);
+  }
+
+  @Override
+  public void setMethodAttributeCount(ClassFile cf, int methodIndex, int attrCount) {
+    pw.printf(", attr count=%d\n", attrCount);
+    incIndent();
+  }
+
+  @Override
+  public void setMethodAttribute(ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) {
+    pw.printf("%s[%d]: %s", indent, attrIndex, name);
+
+    if (name == ClassFile.CODE_ATTR) {
+      cf.parseCodeAttr(this, null);
+
+    } else if (name == ClassFile.EXCEPTIONS_ATTR){
+      cf.parseExceptionAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR){
+      cf.parseParameterAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
+      cf.parseTypeAnnotationsAttr(this, null);
+      
+    } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR){
+      cf.parseParameterAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.SIGNATURE_ATTR){
+      cf.parseSignatureAttr(this, null);
+
+    } else {
+      pw.printf(" ,length=%d,data=[", attrLength );
+      printRawData(pw, cf, attrLength, 10);
+      pw.println(']');
+    }
+  }
+
+  @Override
+  public void setMethodAttributesDone(ClassFile cf, int methodIndex){
+    decIndent();
+  }
+
+  @Override
+  public void setMethodDone(ClassFile cf, int methodIndex){
+    pw.println();
+  }
+
+  @Override
+  public void setMethodsDone(ClassFile cf){
+  }
+
+  @Override
+  public void setExceptionCount(ClassFile cf, Object tag, int exceptionCount){
+    pw.printf(", count=%d\n", exceptionCount);
+    incIndent();
+  }
+  @Override
+  public void setException(ClassFile cf, Object tag, int exceptionIndex, String exceptionType){
+    pw.printf("%s[%d]: %s\n", indent, exceptionIndex, exceptionType);
+  }
+  @Override
+  public void setExceptionsDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+
+  @Override
+  public void setCode(ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {
+    pw.printf(", maxStack=%d,maxLocals=%d,length=%d\n", maxStack, maxLocals,codeLength);
+    incIndent();
+    JVMByteCodePrinter bcPrinter = new JVMByteCodePrinter(pw, cf, indent);
+    cf.parseBytecode(bcPrinter, tag, codeLength);
+    decIndent();
+  }
+
+  @Override
+  public void setExceptionHandlerTableCount(ClassFile cf, Object tag, int exceptionTableCount) {
+    pw.printf("%sexception table count=%d\n", indent, exceptionTableCount);
+    incIndent();
+  }
+  @Override
+  public void setExceptionHandler(ClassFile cf, Object tag, int exceptionIndex,
+          int startPc, int endPc, int handlerPc, String catchType) {
+    pw.printf("%s[%d]: type=%s, range=[%d..%d], handler=%d\n", indent, exceptionIndex, catchType, startPc, endPc, handlerPc);
+  }
+  @Override
+  public void setExceptionHandlerTableDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+  @Override
+  public void setCodeAttributeCount(ClassFile cf, Object tag, int attrCount) {
+    pw.printf("%scode attribute count=%d\n", indent, attrCount);
+    incIndent();
+  }
+  @Override
+  public void setCodeAttribute(ClassFile cf, Object tag, int attrIndex, String name, int attrLength) {
+    pw.printf("%s[%d]: %s", indent, attrIndex, name);
+
+    if (name == ClassFile.LINE_NUMBER_TABLE_ATTR) {
+      cf.parseLineNumberTableAttr(this, tag);
+
+    } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) {
+      cf.parseLocalVarTableAttr(this, tag);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
+        cf.parseTypeAnnotationsAttr(this, tag);
+
+    } else {  // generic
+      pw.printf(" ,length=%d,data=[", attrLength );
+      printRawData(pw, cf, attrLength, 10);
+      pw.println(']');
+    }
+  }
+  @Override
+  public void setCodeAttributesDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+  @Override
+  public void setLineNumberTableCount(ClassFile cf, Object tag, int lineNumberCount) {
+    pw.printf(", linenumber count=%d\n", lineNumberCount);
+    incIndent();
+  }
+  @Override
+  public void setLineNumber(ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {
+    pw.printf("%s[%d]: line=%d, pc=%d\n", indent, lineIndex, lineNumber, startPc);
+  }
+  @Override
+  public void setLineNumberTableDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+  @Override
+  public void setLocalVarTableCount(ClassFile cf, Object tag, int localVarCount) {
+    pw.printf(", localVar count=%d\n", localVarCount);
+    incIndent();
+  }
+  @Override
+  public void setLocalVar(ClassFile cf, Object tag, int localVarIndex,
+          String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex) {
+    pw.printf("%s[%d]: %s, type=%s, scope=[%d..%d], slot=%d\n", indent, localVarIndex, varName, descriptor,
+            scopeStartPc, scopeEndPc, slotIndex);
+  }
+  @Override
+  public void setLocalVarTableDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+  //--- class attributes
+  @Override
+  public void setClassAttributeCount(ClassFile cf, int attrCount) {
+    printSectionHeader( "class attributes");
+    pw.printf("%sclass attribute count=%d\n", indent, attrCount);
+    incIndent();
+  }
+  @Override
+  public void setClassAttribute(ClassFile cf, int attrIndex, String name, int attrLength) {
+    pw.printf("%s[%d]: %s", indent, attrIndex, name);
+
+    if (name == ClassFile.SOURCE_FILE_ATTR) {
+      cf.parseSourceFileAttr(this, null);
+
+    } else if (name == ClassFile.DEPRECATED_ATTR) {
+
+    } else if (name == ClassFile.INNER_CLASSES_ATTR) {
+      cf.parseInnerClassesAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
+      cf.parseTypeAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR){
+      cf.parseAnnotationsAttr(this, null);
+
+    } else if (name == ClassFile.SIGNATURE_ATTR){
+      cf.parseSignatureAttr(this, null);
+
+    } else if (name == ClassFile.ENCLOSING_METHOD_ATTR){
+      cf.parseEnclosingMethodAttr(this, null);
+
+    } else if (name == ClassFile.BOOTSTRAP_METHOD_ATTR){
+      cf.parseBootstrapMethodAttr(this, null);
+
+    } else {
+      pw.printf(" ,length=%d,data=[", attrLength );
+      printRawData(pw, cf, attrLength, 10);
+      pw.println(']');
+    }
+  }
+  @Override
+  public void setClassAttributesDone(ClassFile cf){
+    decIndent();
+  }
+
+  @Override
+  public void setEnclosingMethod(ClassFile cf, Object tag, String enclosingClass, String enclosingMethod, String descriptor) {
+    if (enclosingMethod != null){
+      pw.printf(", enclosingClass=%s, method=%s%s\n", enclosingClass, enclosingMethod, descriptor);
+    } else {
+      pw.printf(", enclosingClass=%s\n", enclosingClass);
+    }
+  }
+
+  
+  @Override
+  public void setSourceFile(ClassFile cf, Object tag, String pathName){
+    pw.printf(", path=%s\n", pathName);
+  }
+
+  @Override
+  public void setInnerClassCount(ClassFile cf, Object tag, int innerClsCount) {
+    pw.printf( ", inner class count=%d\n", innerClsCount);
+    incIndent();
+  }
+  @Override
+  public void setInnerClass(ClassFile cf, Object tag, int innerClsIndex,
+          String outerName, String innerName, String innerSimpleName, int accessFlags) {
+    pw.printf("%s[%d]: %s, fullName=%s, outerClass=%s, flags=0x%X\n", indent, innerClsIndex,
+            innerSimpleName, innerName, outerName, accessFlags);
+  }
+  @Override
+  public void setInnerClassesDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+  @Override
+  public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) {
+    pw.printf( ", bootstrap method count=%d\n", count);
+    incIndent();
+  }
+  
+  @Override
+  public void setBootstrapMethod (ClassFile cf, Object tag, int idx, 
+                                  int refKind, String cls, String mth, String descriptor, int[] cpArgs){
+    String refTypeName = cf.getRefTypeName(refKind);
+    pw.printf("%s[%d]: %s %s.%s%s\n", indent, idx, refTypeName, cls, mth, descriptor);
+    incIndent();
+    pw.printf("%smethod arg count: %d\n", indent, cpArgs.length);
+    incIndent();
+    for (int i=0; i<cpArgs.length; i++){
+      int cpIdx = cpArgs[i];
+      String arg = getBootstrapMethodArgAsString(cf, cpIdx);
+      pw.printf("%s[%d]: %s\n", indent, i, arg);
+    }
+    decIndent();
+    decIndent();
+  }
+  
+  String getBootstrapMethodArgAsString (ClassFile cf, int cpIdx){
+    StringBuilder sb = new StringBuilder();
+    Object cpValue = cf.getCpValue(cpIdx);
+    sb.append('@');
+    sb.append(cpIdx);
+    sb.append(" (");
+    sb.append( cpValue);
+    sb.append("): ");
+    
+    if (cpValue instanceof ClassFile.CpInfo){
+      switch ((ClassFile.CpInfo)cpValue){
+        case MethodType:
+          sb.append( cf.methodTypeDescriptorAt(cpIdx));
+          break;
+        case MethodHandle:
+          int methodRefIdx = cf.mhMethodRefIndexAt(cpIdx);
+          
+          sb.append( cf.getRefTypeName(cf.mhRefTypeAt(cpIdx)));
+          sb.append(' ');
+          sb.append( cf.methodClassNameAt(methodRefIdx));
+          sb.append('.');
+          sb.append( cf.methodNameAt(methodRefIdx));
+          sb.append( cf.methodDescriptorAt(methodRefIdx));
+          break;
+        default:
+          sb.append( cpValue.toString());
+      }
+    } else {
+      sb.append( cpValue.toString());
+    }
+    
+    return sb.toString();
+  }
+  
+  @Override
+  public void setBootstrapMethodsDone (ClassFile cf, Object tag) {
+    decIndent();
+  }
+  
+  @Override
+  public void setAnnotationCount(ClassFile cf, Object tag, int annotationCount){
+    pw.printf( " count=%d\n", annotationCount);
+    incIndent();
+  }
+  @Override
+  public void setAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType){
+    pw.printf("%s[%d]: %s", indent, annotationIndex, annotationType);
+  }
+  @Override
+  public void setAnnotationsDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+  
+  // Java 8 type annotations
+
+  @Override
+  public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){
+    pw.printf( " count=%d\n", annotationCount);
+    incIndent();
+  }
+
+  @Override
+  public void setTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                         int typeIndex, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, type index=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType), ClassFile.getTypePathEncoding(typePath), typeIndex);
+  }
+  @Override
+  public void setSuperTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                     int superTypeIdx, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, super type index=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType),  ClassFile.getTypePathEncoding(typePath), superTypeIdx);
+  }
+  @Override
+  public void setTypeParameterBoundAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
+                                     int typeParamIdx, int boundIdx, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, type index=%d, bound=%d)", indent, annotationIndex, annotationType,
+            ClassFile.getTargetTypeName(targetType),  ClassFile.getTypePathEncoding(typePath), typeParamIdx, boundIdx);
+  }
+  @Override
+  public void setTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
+                                short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s)", indent, annotationIndex, annotationType,
+            ClassFile.getTargetTypeName(targetType), ClassFile.getTypePathEncoding(typePath));
+  }
+  @Override
+  public void setFormalParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                           int formalParamIdx, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, formal param index=%d)", indent, annotationIndex, annotationType,
+            ClassFile.getTargetTypeName(targetType),  ClassFile.getTypePathEncoding(typePath), formalParamIdx);
+  }
+  @Override
+  public void setThrowsAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                  int throwsTypeIdx, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, throws index=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType),  ClassFile.getTypePathEncoding(typePath), throwsTypeIdx);
+  }
+  @Override
+  public void setVariableAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                    long[] scopeEntries, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, scope=%s)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType), ClassFile.getTypePathEncoding(typePath), ClassFile.getScopeEncoding(scopeEntries));
+    // 2do
+  }
+  @Override
+  public void setExceptionParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                              int exceptionIndex, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, catch type index=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType),  ClassFile.getTypePathEncoding(typePath), exceptionIndex);
+  }
+  @Override
+  public void setBytecodeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                    int offset, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, bytecode offset=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType), ClassFile.getTypePathEncoding(typePath), offset);
+  }
+  @Override
+  public void setBytecodeTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                           int offset, int typeArgIdx, short[] typePath, String annotationType){
+    pw.printf("%s[%d]: %s (%s, type path=%s, bytecode offset=%d, type arg=%d)", indent, annotationIndex, annotationType, 
+            ClassFile.getTargetTypeName(targetType), ClassFile.getTypePathEncoding(typePath), offset, typeArgIdx);
+  }
+  
+  @Override
+  public void setTypeAnnotationsDone(ClassFile cf, Object tag) {
+    decIndent();
+  }
+    
+  @Override
+  public void setAnnotationValueCount(ClassFile cf, Object tag, int annotationIndex, int nValuePairs){
+    pw.printf(" valueCount=%d\n", nValuePairs);
+    incIndent();
+  }
+
+  @Override
+  public void setPrimitiveAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, Object val){
+    if (arrayIndex < 0){
+      pw.printf("%s[%d]: %s=%s\n", indent, annotationIndex, elementName, val);
+    } else {
+      if (arrayIndex==0) {
+        pw.printf("%s[%d]: %s={", indent, valueIndex, elementName);
+      }  else {
+        pw.print(',');
+      }
+      pw.print(val);
+    }
+  }
+
+  @Override
+  public void setStringAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String s){
+    if (arrayIndex < 0){
+      pw.printf("%s[%d]: %s=\"%s\"\n", indent, annotationIndex, elementName, s);
+    } else {
+      if (arrayIndex==0) {
+        pw.printf("%s[%d]: %s={", indent, valueIndex, elementName);
+      }  else {
+        pw.print(',');
+      }
+      pw.printf("\"%s\"", s);
+    }
+  }
+
+  @Override
+  public void setClassAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String typeName){
+    if (arrayIndex < 0){
+      pw.printf("%s[%d]: %s=class %s\n", indent, annotationIndex, elementName, typeName);
+    } else {
+      if (arrayIndex==0) {
+        pw.printf("%s[%d]: %s={", indent, valueIndex, elementName);
+      }  else {
+        pw.print(',');
+      }
+      pw.printf("class %s", typeName);
+    }
+  }
+
+  @Override
+  public void setEnumAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String enumType, String enumValue){
+    if (arrayIndex < 0){
+      pw.printf("%s[%d]: %s=%s.%s\n", indent, annotationIndex, elementName, enumType, enumValue);
+    } else {
+      if (arrayIndex==0) {
+        pw.printf("%s[%d]: %s={", indent, valueIndex, elementName);
+      }  else {
+        pw.print(',');
+      }
+      pw.printf("%s.%s", enumType, enumValue);
+    }
+  }
+
+
+  @Override
+  public void setAnnotationValueElementCount(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int elementCount){
+  }
+  @Override
+  public void setAnnotationValueElementsDone(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName){
+    pw.println("}");
+  }
+
+  @Override
+  public void setAnnotationValuesDone(ClassFile cf, Object tag, int annotationIndex){
+    decIndent();
+  }
+
+  @Override
+  public void setParameterCount(ClassFile cf, Object tag, int parameterCount){
+    pw.printf(" parameterCount=%d\n", parameterCount);
+    incIndent();
+  }
+
+  @Override
+  public void setParameterAnnotationCount(ClassFile cf, Object tag, int paramIndex, int annotationCount){
+    pw.printf("%s[%d] count: %d\n", indent, paramIndex, annotationCount);
+    incIndent();
+  }
+
+  @Override
+  public void setParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType){
+    pw.printf("%s[%d]: %s", indent, annotationIndex, annotationType);
+  }
+
+  @Override
+  public void setParameterAnnotationsDone(ClassFile cf, Object tag, int paramIndex){
+    decIndent();
+  }
+
+  @Override
+  public void setParametersDone(ClassFile cf, Object tag){
+    decIndent();
+  }
+
+
+  @Override
+  public void setSignature(ClassFile cf, Object tag, String signature){
+    pw.printf(" %s\n", signature);
+  }
+
+  //--- internal stuff
+
+  protected void printCp (PrintWriter pw, ClassFile cf){
+    int nCpEntries = cf.getNumberOfCpEntries();
+
+    for (int i=1; i<nCpEntries; i++){
+
+      int j = cf.getDataPosOfCpEntry(i);
+
+      pw.print("  [");
+      pw.print(i);
+      pw.print("]: ");
+
+      if (j < 0) {
+        pw.println("<unused>");
+        continue;
+      }
+
+      switch (cf.u1(j)){
+        case ClassFile.CONSTANT_UTF8:
+          pw.print( "constant_utf8 {\"");
+          pw.print( cf.getCpValue(i));
+          pw.println("\"}");
+          break;
+        case ClassFile.CONSTANT_INTEGER:
+          pw.print( "constant_integer {");
+          pw.print( cf.getCpValue(i));
+          pw.println("}");
+          break;
+        case ClassFile.CONSTANT_FLOAT:
+          pw.print( "constant_float {");
+          pw.print( cf.getCpValue(i));
+          pw.println("}");
+          break;
+        case ClassFile.CONSTANT_LONG:
+          pw.print( "constant_long {");
+          pw.print( cf.getCpValue(i));
+          pw.println("}");
+          break;
+        case ClassFile.CONSTANT_DOUBLE:
+          pw.print( "constant_double {");
+          pw.print( cf.getCpValue(i));
+          pw.println("}");
+          break;
+        case ClassFile.CONSTANT_CLASS:
+          pw.print("constant_class {name=#");
+          pw.print( cf.u2(j+1));
+          pw.print("(\"");
+          pw.print( cf.classNameAt(i));
+          pw.println("\")}");
+          break;
+        case ClassFile.CONSTANT_STRING:
+          pw.print("constant_string {utf8=#");
+          pw.print( cf.u2(j+1));
+          pw.print("(\"");
+          pw.print( cf.stringAt(i));
+          pw.println("\")}");
+          break;
+        case ClassFile.FIELD_REF:
+          printRef(pw, cf, i, j, "fieldref");
+          break;
+        case ClassFile.METHOD_REF:
+          printRef(pw, cf, i, j, "methodref");
+          break;
+        case ClassFile.INTERFACE_METHOD_REF:
+          printRef(pw, cf, i, j, "interface_methodref");
+          break;
+        case ClassFile.NAME_AND_TYPE:
+          pw.print("name_and_type {name=#");
+          pw.print( cf.u2(j+1));
+          pw.print("(\"");
+          pw.print(cf.utf8At(cf.u2(j+1)));
+          pw.print("\"),desciptor=#");
+          pw.print( cf.u2(j+3));
+          pw.print("(\"");
+          pw.print(cf.utf8At(cf.u2(j+3)));
+          pw.println("\")}");
+          break;
+          
+        case ClassFile.METHOD_HANDLE:
+          pw.print("method_handle {");
+          pw.print("(\"");
+          pw.println("\")}");
+          break;
+          
+        case ClassFile.METHOD_TYPE:
+          pw.print("method_type {");
+          pw.print("(\"");
+          pw.println("\")}");
+          break;
+          
+        case ClassFile.INVOKE_DYNAMIC:
+          pw.print("invoke_dynamic {bootstrap=#");
+          pw.print(cf.u2(j+1));
+          pw.print("(\"");
+          pw.println("\")}");
+          break;
+          
+        default:
+          pw.print("ERROR: illegal tag" + cf.u1(j));
+      }
+    }
+    pw.println();
+  }
+
+  void printRef(PrintWriter pw, ClassFile cf, int cpIdx, int dataPos, String refType){
+    pw.print(refType);
+    pw.print(" {class=#");
+    pw.print(cf.u2(dataPos + 1));
+    pw.print("(\"");
+    pw.print(cf.refClassNameAt(cpIdx));
+    pw.print("\"),nameType=#");
+    pw.print(cf.u2(dataPos + 3));
+    pw.print("(\"");
+    pw.print(cf.refNameAt(cpIdx));
+    pw.print("\",\"");
+    pw.print(cf.refDescriptorAt(cpIdx));
+    pw.println("\")}");
+  }
+
+  void printRawData(PrintWriter pw, ClassFile cf, int dataLength, int maxBytes){
+    int max = Math.min(dataLength, maxBytes);
+    int max1 = max-1;
+    for (int i=0; i<max1; i++){
+      pw.printf("%02x ", cf.readUByte());
+    }
+    pw.printf("%02x", cf.readUByte());
+
+    if (dataLength>maxBytes){
+      pw.print("..");
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/ClassFileReader.java b/src/main/gov/nasa/jpf/jvm/ClassFileReader.java
new file mode 100644 (file)
index 0000000..6017fad
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.vm.ClassParseException;
+
+/**
+ * interface for classfile processors
+ *
+ * NOTE - all types are given ss descriptors, i.e. "Lx/y/Z;" or type codes such as "I"
+ *
+ * iteration groups always start with a
+ *   setXCount(int xCount)
+ *
+ * followed by xCount notifications
+ *   setX (int xIndex)
+ *
+ * with 0<=xIndex<xCount
+ */
+public interface ClassFileReader {
+
+  void setClass(ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException;
+
+
+  //--- interfaces
+  void setInterfaceCount(ClassFile cf, int ifcCount);
+
+  void setInterface(ClassFile cf, int ifcIndex, String ifcName);
+
+  void setInterfacesDone(ClassFile cf);
+
+
+  //--- fields
+  void setFieldCount(ClassFile cf, int fieldCount);
+
+  void setField(ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor);
+
+  void setFieldAttributeCount(ClassFile cf, int fieldIndex, int attrCount);
+
+  void setFieldAttribute(ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength);
+
+  void setFieldAttributesDone(ClassFile cf, int fieldIndex);
+
+  void setFieldDone(ClassFile cf, int fieldIndex);
+
+  void setFieldsDone(ClassFile cf);
+
+
+  //--- standard field attributes
+  void setConstantValue(ClassFile cf, Object tag, Object value);
+
+
+  //--- methods
+  void setMethodCount(ClassFile cf, int methodCount);
+
+  void setMethod(ClassFile cf, int methodIndex, int accessFlags, String name, String descriptor);
+
+  void setMethodAttributeCount(ClassFile cf, int methodIndex, int attrCount);
+
+  void setMethodAttribute(ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength);
+
+  void setMethodAttributesDone(ClassFile cf, int methodIndex);
+
+  void setMethodDone(ClassFile cf, int methodIndex);
+
+  void setMethodsDone(ClassFile cf);
+
+
+  //--- standard method attributes
+  void setExceptionCount (ClassFile cf, Object tag, int exceptionCount);
+
+  void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType);
+
+  void setExceptionsDone(ClassFile cf, Object tag);
+
+  void setCode(ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength);
+
+  void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount);
+
+  void setExceptionHandler(ClassFile cf, Object tag, int exceptionIndex, int startPc, int endPc, int handlerPc, String catchType);
+
+  void setExceptionHandlerTableDone(ClassFile cf, Object tag);
+
+  void setCodeAttributeCount(ClassFile cf, Object tag, int attrCount);
+
+  void setCodeAttribute(ClassFile cf, Object tag, int attrIndex, String name, int attrLength);
+
+  void setCodeAttributesDone (ClassFile cf, Object tag);
+
+
+  //--- standard code attribute attributes (yes, attributes can be nested)
+  void setLineNumberTableCount(ClassFile cf, Object tag, int lineNumberCount);
+  
+  void setLineNumber(ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc);
+
+  void setLineNumberTableDone(ClassFile cf, Object tag);
+
+
+  void setLocalVarTableCount(ClassFile cf, Object tag, int localVarCount);
+
+  void setLocalVar(ClassFile cf, Object tag, int localVarIndex, String varName, String descriptor,
+                      int scopeStartPc, int scopeEndPc, int slotIndex);
+
+  void setLocalVarTableDone(ClassFile cf, Object tag);
+
+
+  void setClassAttributeCount(ClassFile cf, int attrCount);
+
+  void setClassAttribute(ClassFile cf, int attrIndex, String name, int attrLength);
+
+  void setClassAttributesDone(ClassFile cf);
+
+
+  //--- standard class attributes
+  void setSourceFile(ClassFile cf, Object tag, String pathName);
+
+  void setInnerClassCount(ClassFile cf, Object tag, int innerClsCount);
+  void setInnerClass(ClassFile cf, Object tag, int innerClsIndex, String outerName, String innerName, String innerSimpleName, int accessFlags);
+  void setInnerClassesDone (ClassFile cf, Object tag);
+
+  void setBootstrapMethodCount (ClassFile cf, Object tag, int count);
+  void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs);
+  void setBootstrapMethodsDone (ClassFile cf, Object tag);
+  
+  void setEnclosingMethod(ClassFile cf, Object tag, String enclosingClass, String enclosingMethod, String descriptor);
+  
+  //--- annotations
+  void setAnnotationCount(ClassFile cf, Object tag, int annotationCount);
+  void setAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType);
+  void setAnnotationsDone(ClassFile cf, Object tag);
+
+  
+  // Java 8 type annotations
+  void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount);
+  
+  void setTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int typeIndex, short[] typePath, String annotationType);
+  void setSuperTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int superTypeIdx, short[] typePath, String annotationType);
+  void setTypeParameterBoundAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int typeParamIdx, int boundIdx, short[] typePath, String annotationType);
+  void setTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, short[] typePath, String annotationType);
+  void setFormalParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int formalParamIndex, short[] typePath, String annotationType);
+  void setThrowsAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int throwsTypeIndex, short[] typePath, String annotationType);
+  void setVariableAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, long[] scopeEntries, short[] typePath, String annotationType);
+  void setExceptionParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int exceptionIndex, short[] typePath, String annotationType);
+  void setBytecodeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int offset, short[] typePath, String annotationType);
+  void setBytecodeTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int offset, int typeArgIdx, short[] typePath, String annotationType);
+  
+  void setTypeAnnotationsDone(ClassFile cf, Object tag);
+
+  
+  
+  void setAnnotationValueCount(ClassFile cf, Object tag, int annotationIndex, int nValuePairs);
+
+  void setPrimitiveAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, Object val);
+
+  void setStringAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, String s);
+
+  void setClassAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex, String typeName);
+
+  void setEnumAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int arrayIndex,
+          String enumType, String enumValue);
+
+  void setAnnotationValueElementCount(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName, int elementCount);
+
+  void setAnnotationValueElementsDone(ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName);
+
+  void setAnnotationValuesDone(ClassFile cf, Object tag, int annotationIndex);
+
+  void setParameterCount(ClassFile cf, Object tag, int parameterCount);
+
+  void setParameterAnnotationCount(ClassFile cf, Object tag, int paramIndex, int annotationCount);
+
+  void setParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType);
+
+  void setParameterAnnotationsDone(ClassFile cf, Object tag, int paramIndex);
+
+  void setParametersDone(ClassFile cf, Object tag);
+
+
+  void setSignature(ClassFile cf, Object tag, String signature);
+}
diff --git a/src/main/gov/nasa/jpf/jvm/ClassFileReaderAdapter.java b/src/main/gov/nasa/jpf/jvm/ClassFileReaderAdapter.java
new file mode 100644 (file)
index 0000000..76fe464
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.vm.ClassParseException;
+
+/**
+ * adapter class implementing the ClassFileReader interface
+ */
+public class ClassFileReaderAdapter implements ClassFileReader {
+
+  @Override
+  public void setClass(ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {}
+
+  @Override
+  public void setInterfaceCount(ClassFile cf, int ifcCount) {}
+
+  @Override
+  public void setInterface(ClassFile cf, int ifcIndex, String ifcName) {}
+
+  @Override
+  public void setInterfacesDone(ClassFile cf) {};
+
+  @Override
+  public void setFieldCount(ClassFile cf, int fieldCount) {}
+
+  @Override
+  public void setField(ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor) {}
+
+  @Override
+  public void setFieldAttributeCount(ClassFile cf, int fieldIndex, int attrCount) {}
+
+  @Override
+  public void setFieldAttribute(ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength) {}
+
+  @Override
+  public void setFieldAttributesDone(ClassFile cf, int fieldIndex) {}
+
+  @Override
+  public void setFieldDone(ClassFile cf, int fieldIndex) {}
+
+  @Override
+  public void setFieldsDone(ClassFile cf) {}
+
+  @Override
+  public void setConstantValue(ClassFile cf, Object tag, Object value) {}
+
+  @Override
+  public void setMethodCount(ClassFile cf, int methodCount) {}
+
+  @Override
+  public void setMethod(ClassFile cf, int methodIndex, int accessFlags, String name, String descriptor) {}
+
+  @Override
+  public void setMethodAttributeCount(ClassFile cf, int methodIndex, int attrCount) {}
+
+  @Override
+  public void setMethodAttribute(ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) {}
+
+  @Override
+  public void setMethodAttributesDone(ClassFile cf, int methodIndex){}
+
+  @Override
+  public void setMethodDone(ClassFile cf, int methodIndex) {}
+
+  @Override
+  public void setMethodsDone(ClassFile cf) {}
+
+  @Override
+  public void setExceptionCount(ClassFile cf, Object tag, int exceptionCount) {}
+
+  @Override
+  public void setException(ClassFile cf, Object tag, int exceptionIndex, String exceptionType) {}
+
+  @Override
+  public void setExceptionsDone(ClassFile cf, Object tag) {}
+
+  @Override
+  public void setCode(ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {}
+
+  @Override
+  public void setExceptionHandlerTableCount(ClassFile cf, Object tag, int exceptionTableCount) {}
+
+  @Override
+  public void setExceptionHandler(ClassFile cf, Object tag, int exceptionIndex,
+          int startPc, int endPc, int handlerPc, String catchType) {}
+
+  @Override
+  public void setExceptionHandlerTableDone(ClassFile cf, Object tag) {}
+
+  @Override
+  public void setCodeAttributeCount(ClassFile cf, Object tag, int attrCount) {}
+
+  @Override
+  public void setCodeAttribute(ClassFile cf, Object tag, int attrIndex, String name, int attrLength) {}
+
+  @Override
+  public void setCodeAttributesDone (ClassFile cf, Object tag) {}
+
+  @Override
+  public void setLineNumberTableCount(ClassFile cf, Object tag, int lineNumberCount) {}
+
+  @Override
+  public void setLineNumber(ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {}
+
+  @Override
+  public void setLineNumberTableDone(ClassFile cf, Object tag) {}
+
+  @Override
+  public void setLocalVarTableCount(ClassFile cf, Object tag, int localVarCount) {}
+
+  @Override
+  public void setLocalVar(ClassFile cf, Object tag, int localVarIndex,
+          String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex) {}
+
+  @Override
+  public void setLocalVarTableDone (ClassFile cf, Object tag) {}
+
+  @Override
+  public void setClassAttributeCount(ClassFile cf, int attrCount) {}
+
+  @Override
+  public void setClassAttribute(ClassFile cf, int attrIndex, String name, int attrLength) {}
+
+  @Override
+  public void setClassAttributesDone(ClassFile cf) {}
+
+  @Override
+  public void setSourceFile(ClassFile cf, Object tag, String pathName) {}
+
+  @Override
+  public void setInnerClassCount(ClassFile cf, Object tag, int innerClsCount) {}
+
+  @Override
+  public void setInnerClass(ClassFile cf, Object tag, int innerClsIndex,
+          String outerName, String innerName, String innerSimpleName, int accessFlags) {}
+
+  @Override
+  public void setInnerClassesDone(ClassFile cf, Object tag) {}
+  
+  @Override
+  public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) {}
+  
+  @Override
+  public void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs){}
+  
+  @Override
+  public void setBootstrapMethodsDone (ClassFile cf, Object tag) {}
+  
+  @Override
+  public void setEnclosingMethod(ClassFile cf, Object tag, String enclosingClass, String enclosingMethod, String descriptor) {}
+
+  @Override
+  public void setAnnotationCount(ClassFile cf, Object tag, int annotationCount){}
+  @Override
+  public void setAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType){}
+  @Override
+  public void setAnnotationsDone(ClassFile cf, Object tag) {}
+
+  //--- Java 8 type annotations
+  @Override
+  public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){}
+  @Override
+  public void setTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int typeIndex, short[] typePath, String annotationType){}
+  @Override
+  public void setSuperTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int superTypeIdx, short[] typePath, String annotationType){}
+  @Override
+  public void setTypeParameterBoundAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int typeParamIdx, int boundIdx, short[] typePath, String annotationType){}
+  @Override
+  public void setTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, short[] typePath, String annotationType){}
+  @Override
+  public void setFormalParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int formalParamIdx, short[] typePath, String annotationType){}
+  @Override
+  public void setThrowsAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int throwsTypeIdx, short[] typePath, String annotationType){}
+  @Override
+  public void setVariableAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, long[] scopeEntries, short[] typePath, String annotationType){}
+  @Override
+  public void setExceptionParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int exceptionIndex, short[] typePath, String annotationType){}
+  @Override
+  public void setBytecodeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int offset, short[] typePath, String annotationType){}
+  @Override
+  public void setBytecodeTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, int offset, int typeArgIdx, short[] typePath, String annotationType){}
+  @Override
+  public void setTypeAnnotationsDone(ClassFile cf, Object tag) {}
+
+  @Override
+  public void setAnnotationValueCount(ClassFile cf, Object tag, int annotationIndex, int annotationCount) {}
+
+  @Override
+  public void setPrimitiveAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, Object val){}
+
+  @Override
+  public void setStringAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String s){}
+
+  @Override
+  public void setClassAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String typeName){}
+
+  @Override
+  public void setEnumAnnotationValue(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String enumType, String enumValue){}
+
+  @Override
+  public void setAnnotationValueElementCount(ClassFile cf, Object tag, int annotationIndex, int valueIndex, 
+          String elementName, int elementCount) {}
+
+  @Override
+  public void setAnnotationValueElementsDone(ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName) {}
+
+  @Override
+  public void setAnnotationValuesDone(ClassFile cf, Object tag, int annotationIndex) {}
+
+  @Override
+  public void setParameterCount(ClassFile cf, Object tag, int parameterCount) {}
+
+  @Override
+  public void setParameterAnnotationCount(ClassFile cf, Object tag, int paramIndex, int annotationCount) {}
+
+  @Override
+  public void setParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, String annotationType) {}
+
+  @Override
+  public void setParameterAnnotationsDone(ClassFile cf, Object tag, int paramIndex) {}
+
+  @Override
+  public void setParametersDone(ClassFile cf, Object tag) {}
+
+  @Override
+  public void setSignature(ClassFile cf, Object tag, String signature) {}
+}
diff --git a/src/main/gov/nasa/jpf/jvm/DirClassFileContainer.java b/src/main/gov/nasa/jpf/jvm/DirClassFileContainer.java
new file mode 100644 (file)
index 0000000..d523577
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.jvm.JVMClassFileContainer;
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.vm.ClassFileMatch;
+import gov.nasa.jpf.vm.ClassParseException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+/**
+ *
+ */
+public class DirClassFileContainer extends JVMClassFileContainer {
+
+  protected File dir;
+
+  static String getContainerURL(File dir){
+    try {
+      return dir.toURI().toURL().toString();
+    } catch (MalformedURLException e) {
+      return dir.getPath();
+    }
+  }
+
+  public DirClassFileContainer(File dir) {
+    super(dir.getPath(), getContainerURL(dir));
+
+    this.dir = dir;
+  }
+
+  @Override
+  public ClassFileMatch getMatch(String clsName) throws ClassParseException {
+    String pn = clsName.replace('.', File.separatorChar) + ".class";
+    File f = new File(dir, pn);
+
+    if (f.isFile()) {
+      FileInputStream fis = null;
+
+      try {
+        fis = new FileInputStream(f);
+        long len = f.length();
+        if (len > Integer.MAX_VALUE) {
+          error("classfile too big: " + f.getPath());
+        }
+        byte[] data = new byte[(int) len];
+        FileUtils.getContents(fis, data);
+
+        return new JVMClassFileMatch( clsName, getClassURL(clsName), data);
+
+      } catch (IOException iox) {
+        error("cannot read " + f.getPath());
+
+      } finally {
+        if (fis != null) {
+          try {
+            fis.close();
+          } catch (IOException iox) {
+            error("cannot close input stream for file " + f.getPath());
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMAnnotationParser.java b/src/main/gov/nasa/jpf/jvm/JVMAnnotationParser.java
new file mode 100644 (file)
index 0000000..8acef37
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.AnnotationParser;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.vm.Types;
+
+/**
+ * parser that reads annotation classfiles and extracts default value entries
+ * 
+ * Java annotations form a different type system. Java annotations are essentially
+ * restricted interfaces (no super-interface, no fields other than static finals
+ * that are inlined by the compiler)
+ * 
+ * Since Java annotations use only a small subset of the Java classfile format
+ * we only have to parse methods and method attributes
+ * 
+ * <2do> class and enum values are not yet supported
+ */
+public class JVMAnnotationParser extends ClassFileReaderAdapter implements AnnotationParser {
+
+  ClassFile cf;
+  AnnotationInfo ai;
+  
+  String key;
+  Object value;
+  Object[] valElements;
+
+  String annotationName;
+  AnnotationInfo.Entry[] entries;
+  
+  public JVMAnnotationParser (ClassFile cf) {
+    this.cf = cf;
+  }
+
+  @Override
+  public void parse (AnnotationInfo ai) throws ClassParseException {
+    this.ai = ai;
+    
+    cf.parse(this);
+  }
+    
+  //--- the overridden ClassFileReader methods
+
+  @Override
+  public void setClass (ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
+    entries = null;
+    annotationName = Types.getClassNameFromTypeName(clsName);
+    
+    ai.setName(annotationName);
+  }
+
+  @Override
+  public void setInterface (ClassFile cf, int ifcIndex, String ifcName) {
+    if (!"java/lang/annotation/Annotation".equals(ifcName)) {
+      throw new JPFException("illegal annotation interface of: " + annotationName + " is " + ifcName);
+    }
+  }
+
+  @Override
+  public void setMethodCount (ClassFile cf, int methodCount) {
+    entries = new AnnotationInfo.Entry[methodCount];
+  }
+
+  @Override
+  public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String descriptor) {
+    key = name;
+    value = null;
+  }
+
+  @Override
+  public void setMethodDone (ClassFile cf, int methodIndex){
+    entries[methodIndex] = new AnnotationInfo.Entry(key, value);
+  }
+  
+  @Override
+  public void setMethodsDone (ClassFile cf){
+    ai.setEntries(entries);
+  }
+  
+  @Override
+  public void setMethodAttribute (ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) {
+    if (name == ClassFile.ANNOTATIONDEFAULT_ATTR) {
+      cf.parseAnnotationDefaultAttr(this, key);
+    }
+  }
+
+  @Override
+  public void setClassAttribute (ClassFile cf, int attrIndex, String name, int attrLength) {
+    if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
+      key = null;
+      cf.parseAnnotationsAttr(this, null);
+    }
+  }
+
+  @Override
+  public void setAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
+    if (annotationType.equals("Ljava/lang/annotation/Inherited;")) {
+      ai.setInherited( true);
+    }
+  }
+
+  @Override
+  public void setPrimitiveAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, Object val) {
+    if (arrayIndex >= 0) {
+      valElements[arrayIndex] = val;
+    } else {
+      if (key != null){
+        value = val;
+      }
+    }
+  }
+
+  @Override
+  public void setStringAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String val) {
+    if (arrayIndex >= 0) {
+      valElements[arrayIndex] = val;
+    } else {
+      if (key != null){
+        value = val;
+      }
+    }
+  }
+
+  @Override
+  public void setClassAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String typeName) {
+    Object val = AnnotationInfo.getClassValue(typeName);
+    if (arrayIndex >= 0) {
+      valElements[arrayIndex] = val;
+    } else {
+      if (key != null){
+        value = val;
+      }
+    }
+  }
+
+  @Override
+  public void setEnumAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int arrayIndex, String enumType, String enumValue) {
+    Object val = AnnotationInfo.getEnumValue(enumType, enumValue);
+    if (arrayIndex >= 0) {
+      valElements[arrayIndex] = val;
+    } else {
+      if (key != null){
+        value = val;
+      }
+    }
+  }
+
+  @Override
+  public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName, int elementCount) {
+    valElements = new Object[elementCount];
+  }
+
+  @Override
+  public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+          String elementName) {
+    if (key != null) {
+      value = valElements;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMByteCodePrinter.java b/src/main/gov/nasa/jpf/jvm/JVMByteCodePrinter.java
new file mode 100644 (file)
index 0000000..f5cb578
--- /dev/null
@@ -0,0 +1,1096 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import java.io.PrintWriter;
+
+/**
+ * utility class that prints out bytecode in readable form
+ */
+public class JVMByteCodePrinter implements JVMByteCodeReader {
+
+  PrintWriter pw;
+  ClassFile cf; // need this to get the constpool entries
+
+  String prefix;
+
+  public JVMByteCodePrinter (PrintWriter pw, ClassFile cf, String prefix){
+    this.pw = pw;
+    this.cf = cf;
+    this.prefix = prefix;
+  }
+
+  @Override
+  public void aconst_null() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aconst_null");
+  }
+
+  @Override
+  public void aload(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "aload", localVarIndex);
+  }
+
+  @Override
+  public void aload_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aload_0");
+  }
+
+  @Override
+  public void aload_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aload_1");
+  }
+
+  @Override
+  public void aload_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aload_2");
+  }
+
+  @Override
+  public void aload_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aload_3");
+  }
+
+  @Override
+  public void aaload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aaload");
+  }
+
+  @Override
+  public void astore(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "astore", localVarIndex);
+  }
+
+  @Override
+  public void astore_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "astore_0");
+  }
+
+  @Override
+  public void astore_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "astore_1");
+  }
+
+  @Override
+  public void astore_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "astore_2");
+  }
+
+  @Override
+  public void astore_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "astore_3");
+  }
+
+  @Override
+  public void aastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "aastore");
+  }
+
+  @Override
+  public void areturn() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "areturn");
+  }
+
+  @Override
+  public void anewarray(int cpClassIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\")\n", prefix, cf.getPc(), "anewarray", cpClassIndex, cf.classNameAt(cpClassIndex));
+  }
+
+  @Override
+  public void arraylength() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "arraylength");
+  }
+
+  @Override
+  public void athrow() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "athrow");
+  }
+
+  @Override
+  public void baload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "baload");
+  }
+
+  @Override
+  public void bastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "bastore");
+  }
+
+  @Override
+  public void bipush(int b) {
+    pw.printf("%s%3d: %s %d\n", prefix, cf.getPc(), "bipush", b);
+  }
+
+  @Override
+  public void caload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "caload");
+  }
+
+  @Override
+  public void castore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "castore");
+  }
+
+  @Override
+  public void checkcast(int cpClassIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\")\n", prefix, cf.getPc(), "checkcast", cpClassIndex, cf.classNameAt(cpClassIndex));
+  }
+
+  @Override
+  public void d2f() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "d2f");
+  }
+
+  @Override
+  public void d2i() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "d2i");
+  }
+
+  @Override
+  public void d2l() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "d2l");
+  }
+
+  @Override
+  public void dadd() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dadd");
+  }
+
+  @Override
+  public void daload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "daload");
+  }
+
+  @Override
+  public void dastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dastore");
+  }
+
+  @Override
+  public void dcmpg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dcmpg");
+  }
+
+  @Override
+  public void dcmpl() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dcmpl");
+  }
+
+  @Override
+  public void dconst_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dconst_0");
+  }
+
+  @Override
+  public void dconst_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dcont_1");
+  }
+
+  @Override
+  public void ddiv() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ddiv");
+  }
+
+  @Override
+  public void dload(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "dload", localVarIndex);
+  }
+
+  @Override
+  public void dload_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dload_0");
+  }
+
+  @Override
+  public void dload_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dload_1");
+  }
+
+  @Override
+  public void dload_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dload_2");
+  }
+
+  @Override
+  public void dload_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dload_3");
+  }
+
+  @Override
+  public void dmul() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dmul");
+  }
+
+  @Override
+  public void dneg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dneg");
+  }
+
+  @Override
+  public void drem() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "drem");
+  }
+
+  @Override
+  public void dreturn() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dreturn");
+  }
+
+  @Override
+  public void dstore(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "dstore", localVarIndex);
+  }
+
+  @Override
+  public void dstore_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dstore_0");
+  }
+
+  @Override
+  public void dstore_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dstore_1");
+  }
+
+  @Override
+  public void dstore_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dstore_2");
+  }
+
+  @Override
+  public void dstore_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dstore_3");
+  }
+
+  @Override
+  public void dsub() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dsub");
+  }
+
+  @Override
+  public void dup() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup");
+  }
+
+  @Override
+  public void dup_x1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup_x1");
+  }
+
+  @Override
+  public void dup_x2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup_x2");
+  }
+
+  @Override
+  public void dup2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup2");
+  }
+
+  @Override
+  public void dup2_x1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup2_x1");
+  }
+
+  @Override
+  public void dup2_x2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "dup2_x2");
+  }
+
+  @Override
+  public void f2d() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "f2d");
+  }
+
+  @Override
+  public void f2i() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "f2i");
+  }
+
+  @Override
+  public void f2l() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "f2l");
+  }
+
+  @Override
+  public void fadd() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fadd");
+  }
+
+  @Override
+  public void faload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "faload");
+  }
+
+  @Override
+  public void fastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fastore");
+  }
+
+  @Override
+  public void fcmpg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fcmpg");
+  }
+
+  @Override
+  public void fcmpl() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fcmpl");
+  }
+
+  @Override
+  public void fconst_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fconst_0");
+  }
+
+  @Override
+  public void fconst_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fconst_1");
+  }
+
+  @Override
+  public void fconst_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fconst_2");
+  }
+
+  @Override
+  public void fdiv() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fdiv");
+  }
+
+  @Override
+  public void fload(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "fload", localVarIndex);
+  }
+
+  @Override
+  public void fload_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fload_0");
+  }
+
+  @Override
+  public void fload_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fload_1");
+  }
+
+  @Override
+  public void fload_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fload_2");
+  }
+
+  @Override
+  public void fload_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fload_3");
+  }
+
+  @Override
+  public void fmul() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fmul");
+  }
+
+  @Override
+  public void fneg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fneg");
+  }
+
+  @Override
+  public void frem() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "frem");
+  }
+
+  @Override
+  public void freturn() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "freturn");
+  }
+
+  @Override
+  public void fstore(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "fstore", localVarIndex);
+  }
+
+  @Override
+  public void fstore_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fstore_0");
+  }
+
+  @Override
+  public void fstore_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fstore_1");
+  }
+
+  @Override
+  public void fstore_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fstore_2");
+  }
+
+  @Override
+  public void fstore_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fstore_3");
+  }
+
+  @Override
+  public void fsub() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "fsub");
+  }
+
+  @Override
+  public void getfield(int cpFieldRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "getfield", cpFieldRefIndex,
+            cf.fieldClassNameAt(cpFieldRefIndex),
+            cf.fieldNameAt(cpFieldRefIndex),
+            cf.fieldDescriptorAt(cpFieldRefIndex));
+  }
+
+  @Override
+  public void getstatic(int cpFieldRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "getstatic", cpFieldRefIndex,
+            cf.fieldClassNameAt(cpFieldRefIndex),
+            cf.fieldNameAt(cpFieldRefIndex),
+            cf.fieldDescriptorAt(cpFieldRefIndex));
+  }
+
+  @Override
+  public void goto_(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "goto", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void goto_w(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "goto_w", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void i2b() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2b");
+  }
+
+  @Override
+  public void i2c() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2c");
+  }
+
+  @Override
+  public void i2d() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2d");
+  }
+
+  @Override
+  public void i2f() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2f");
+  }
+
+  @Override
+  public void i2l() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2l");
+  }
+
+  @Override
+  public void i2s() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "i2s");
+  }
+
+  @Override
+  public void iadd() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iadd");
+  }
+
+  @Override
+  public void iaload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iaload");
+  }
+
+  @Override
+  public void iand() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iand");
+  }
+
+  @Override
+  public void iastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iastore");
+  }
+
+  @Override
+  public void iconst_m1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_m1");
+  }
+
+  @Override
+  public void iconst_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_0");
+  }
+
+  @Override
+  public void iconst_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_1");
+  }
+
+  @Override
+  public void iconst_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_2");
+  }
+
+  @Override
+  public void iconst_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_3");
+  }
+
+  @Override
+  public void iconst_4() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_4");
+  }
+
+  @Override
+  public void iconst_5() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iconst_5");
+  }
+
+  @Override
+  public void idiv() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "idiv");
+  }
+
+  @Override
+  public void if_acmpeq(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_acmpeq", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_acmpne(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_acmpne", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmpeq(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmpeq", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmpne(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmpne", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmplt(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmplt", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmpge(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmpge", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmpgt(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmpgt", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void if_icmple(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "if_icmple", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifeq(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifeq", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifne(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifne", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void iflt(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "iflt", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifge(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifge", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifgt(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifgt", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifle(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifle", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifnonnull(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifnonnull", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void ifnull(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "ifnull", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void iinc(int localVarIndex, int incConstant) {
+    pw.printf("%s%3d: %s [%d] %+d\n", prefix, cf.getPc(), "iinc", localVarIndex, incConstant);
+  }
+
+  @Override
+  public void iload(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "iload", localVarIndex);
+  }
+
+  @Override
+  public void iload_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iload_0");
+  }
+
+  @Override
+  public void iload_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iload_1");
+  }
+
+  @Override
+  public void iload_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iload_2");
+  }
+
+  @Override
+  public void iload_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iload_3");
+  }
+
+  @Override
+  public void imul() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "imul");
+  }
+
+  @Override
+  public void ineg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ineg");
+  }
+
+  @Override
+  public void instanceof_(int cpClassIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\")\n", prefix, cf.getPc(), "instanceof", cpClassIndex, cf.classNameAt(cpClassIndex));
+  }
+
+  @Override
+  public void invokeinterface(int cpInterfaceMethodRefIndex, int count, int zero) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\") %d\n", prefix, cf.getPc(), "invokeinterface", cpInterfaceMethodRefIndex,
+            cf.methodClassNameAt(cpInterfaceMethodRefIndex),
+            cf.methodNameAt(cpInterfaceMethodRefIndex),
+            cf.methodDescriptorAt(cpInterfaceMethodRefIndex),
+            count);
+  }
+
+  @Override
+  public void invokespecial(int cpMethodRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "invokespecial", cpMethodRefIndex,
+            cf.methodClassNameAt(cpMethodRefIndex),
+            cf.methodNameAt(cpMethodRefIndex),
+            cf.methodDescriptorAt(cpMethodRefIndex));
+  }
+
+  @Override
+  public void invokestatic(int cpMethodRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "invokestatic", cpMethodRefIndex,
+            cf.methodClassNameAt(cpMethodRefIndex),
+            cf.methodNameAt(cpMethodRefIndex),
+            cf.methodDescriptorAt(cpMethodRefIndex));
+  }
+
+  @Override
+  public void invokevirtual(int cpMethodRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "invokevirtual", cpMethodRefIndex,
+            cf.methodClassNameAt(cpMethodRefIndex),
+            cf.methodNameAt(cpMethodRefIndex),
+            cf.methodDescriptorAt(cpMethodRefIndex));
+  }
+
+  @Override
+  public void invokedynamic (int cpInvokeDynamicIndex){
+    pw.printf("%s%3d: %s @%d\n", prefix, cf.getPc(), "invokedynamic", cpInvokeDynamicIndex);
+  }
+  
+  @Override
+  public void ior() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ior");
+  }
+
+  @Override
+  public void irem() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "irem");
+  }
+
+  @Override
+  public void ireturn() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ireturn");
+  }
+
+  @Override
+  public void ishl() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ishl");
+  }
+
+  @Override
+  public void ishr() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ishr");
+  }
+
+  @Override
+  public void istore(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "istore", localVarIndex);
+  }
+
+  @Override
+  public void istore_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "istore_0");
+  }
+
+  @Override
+  public void istore_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "istore_1");
+  }
+
+  @Override
+  public void istore_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "istore_2");
+  }
+
+  @Override
+  public void istore_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "istore_3");
+  }
+
+  @Override
+  public void isub() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "isub");
+  }
+
+  @Override
+  public void iushr() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "iushr");
+  }
+
+  @Override
+  public void ixor() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ixor");
+  }
+
+  @Override
+  public void jsr(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "jsr", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void jsr_w(int pcOffset) {
+    pw.printf("%s%3d: %s %+d (%d)\n", prefix, cf.getPc(), "jsr_w", pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void l2d() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "l2d");
+  }
+
+  @Override
+  public void l2f() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "l2f");
+  }
+
+  @Override
+  public void l2i() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "l2i");
+  }
+
+  @Override
+  public void ladd() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ladd");
+  }
+
+  @Override
+  public void laload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "laload");
+  }
+
+  @Override
+  public void land() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "land");
+  }
+
+  @Override
+  public void lastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lastore");
+  }
+
+  @Override
+  public void lcmp() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lcmp");
+  }
+
+  @Override
+  public void lconst_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lconst_0");
+  }
+
+  @Override
+  public void lconst_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lconst_1");
+  }
+
+  @Override
+  public void ldc_(int cpIntOrFloatOrStringIndex) {
+    pw.printf("%s%3d: %s @%d(%s)\n", prefix, cf.getPc(), "ldc", cpIntOrFloatOrStringIndex,
+            cf.getCpValue(cpIntOrFloatOrStringIndex));
+  }
+
+  @Override
+  public void ldc_w_(int cpIntOrFloatOrStringIndex) {
+    pw.printf("%s%3d: %s @%d(%s)\n", prefix, cf.getPc(), "ldc_w", cpIntOrFloatOrStringIndex,
+            cf.getCpValue(cpIntOrFloatOrStringIndex));
+  }
+
+  @Override
+  public void ldc2_w(int cpLongOrDoubleIndex) {
+    pw.printf("%s%3d: %s @%d(%s)\n", prefix, cf.getPc(), "ldc2_w", cpLongOrDoubleIndex,
+            cf.getCpValue(cpLongOrDoubleIndex));
+  }
+
+  @Override
+  public void ldiv() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "ldiv");
+  }
+
+  @Override
+  public void lload(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "lload", localVarIndex);
+  }
+
+  @Override
+  public void lload_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lload_0");
+  }
+
+  @Override
+  public void lload_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lload_1");
+  }
+
+  @Override
+  public void lload_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lload_2");
+  }
+
+  @Override
+  public void lload_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lload_3");
+  }
+
+  @Override
+  public void lmul() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lmul");
+  }
+
+  @Override
+  public void lneg() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lneg");
+  }
+
+  @Override
+  public void lookupswitch(int defaultPcOffset, int nEntries) {
+    pw.printf("%s%3d: %s default:%+d\n", prefix, cf.getPc(), "lookupswitch", defaultPcOffset);
+    cf.parseLookupSwitchEntries(this, nEntries);
+  }
+  @Override
+  public void lookupswitchEntry(int index, int match, int pcOffset){
+    pw.printf("%s      %d : %+d (%d)\n", prefix, match, pcOffset, (cf.getPc() + pcOffset));
+  }
+
+
+  @Override
+  public void lor() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lor");
+  }
+
+  @Override
+  public void lrem() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lrem");
+  }
+
+  @Override
+  public void lreturn() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lreturn");
+  }
+
+  @Override
+  public void lshl() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lshl");
+  }
+
+  @Override
+  public void lshr() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lshr");
+  }
+
+  @Override
+  public void lstore(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "lstore", localVarIndex);
+  }
+
+  @Override
+  public void lstore_0() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lstore_0");
+  }
+
+  @Override
+  public void lstore_1() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lstore_1");
+  }
+
+  @Override
+  public void lstore_2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lstore_2");
+  }
+
+  @Override
+  public void lstore_3() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lstore_3");
+  }
+
+  @Override
+  public void lsub() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lsub");
+  }
+
+  @Override
+  public void lushr() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lushr");
+  }
+
+  @Override
+  public void lxor() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "lxor");
+  }
+
+  @Override
+  public void monitorenter() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "monitorenter");
+  }
+
+  @Override
+  public void monitorexit() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "monitorexit");
+  }
+
+  @Override
+  public void multianewarray(int cpClassIndex, int dimensions) {
+    pw.printf("%s%3d: %s @%d(\"%s\") dim: %d\n", prefix, cf.getPc(), "multianewarray",
+            cpClassIndex, cf.classNameAt(cpClassIndex), dimensions);
+  }
+
+  @Override
+  public void new_(int cpClassIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\")\n", prefix, cf.getPc(), "new",
+            cpClassIndex, cf.classNameAt(cpClassIndex));
+  }
+
+  @Override
+  public void newarray(int typeCode) {
+    pw.printf("%s%3d: %s %s[]\n", prefix, cf.getPc(), "newarray", cf.getTypeName(typeCode));
+  }
+
+  @Override
+  public void nop() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "nop");
+  }
+
+  @Override
+  public void pop() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "pop");
+  }
+
+  @Override
+  public void pop2() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "pop2");
+  }
+
+  @Override
+  public void putfield(int cpFieldRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "putfield", cpFieldRefIndex,
+            cf.fieldClassNameAt(cpFieldRefIndex),
+            cf.fieldNameAt(cpFieldRefIndex),
+            cf.fieldDescriptorAt(cpFieldRefIndex));
+  }
+
+  @Override
+  public void putstatic(int cpFieldRefIndex) {
+    pw.printf("%s%3d: %s @%d(\"%s\",\"%s\",\"%s\")\n", prefix, cf.getPc(), "putstatic", cpFieldRefIndex,
+            cf.fieldClassNameAt(cpFieldRefIndex),
+            cf.fieldNameAt(cpFieldRefIndex),
+            cf.fieldDescriptorAt(cpFieldRefIndex));
+  }
+
+  @Override
+  public void ret(int localVarIndex) {
+    pw.printf("%s%3d: %s [%d]\n", prefix, cf.getPc(), "ret", localVarIndex);
+  }
+
+  @Override
+  public void return_() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "return");
+  }
+
+  @Override
+  public void saload() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "saload");
+  }
+
+  @Override
+  public void sastore() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "sastore");
+  }
+
+  @Override
+  public void sipush(int val) {
+    pw.printf("%s%3d: %s %d\n", prefix, cf.getPc(), "sipush", val);
+  }
+
+  @Override
+  public void swap() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "swap");
+  }
+
+  @Override
+  public void tableswitch(int defaultPcOffset, int low, int high) {
+    pw.printf("%s%3d: %s [%d..%d] default: %+d\n", prefix, cf.getPc(), "tableswitch", low, high, defaultPcOffset);
+    cf.parseTableSwitchEntries(this, low, high);
+  }
+  @Override
+  public void tableswitchEntry(int val, int pcOffset){
+    pw.printf("%s      %d: %+d (%d)\n", prefix, val, pcOffset, (cf.getPc() + pcOffset));
+  }
+
+  @Override
+  public void wide() {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "wide");
+  }
+
+  @Override
+  public void unknown(int bytecode) {
+    pw.printf("%s%3d: %s\n", prefix, cf.getPc(), "");
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMByteCodeReader.java b/src/main/gov/nasa/jpf/jvm/JVMByteCodeReader.java
new file mode 100644 (file)
index 0000000..0eb5796
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+/**
+ * interface to process bytecode
+ */
+public interface JVMByteCodeReader {
+
+  void aconst_null();
+  void aload(int localVarIndex);
+  void aload_0();
+  void aload_1();
+  void aload_2();
+  void aload_3();
+  void aaload();
+  void astore(int localVarIndex);
+  void astore_0();
+  void astore_1();
+  void astore_2();
+  void astore_3();
+  void aastore();
+  void areturn();
+  void anewarray(int cpClassIndex);
+  void arraylength();
+  void athrow();
+
+  void baload();
+  void bastore();
+  void bipush(int b);
+
+  void caload();
+  void castore();
+  void checkcast(int cpClassIndex);
+
+  void d2f();
+  void d2i();
+  void d2l();
+  void dadd();
+  void daload();
+  void dastore();
+  void dcmpg();
+  void dcmpl();
+  void dconst_0();
+  void dconst_1();
+  void ddiv();
+  void dload(int localVarIndex);
+  void dload_0();
+  void dload_1();
+  void dload_2();
+  void dload_3();
+  void dmul();
+  void dneg();
+  void drem();
+  void dreturn();
+  void dstore(int localVarIndex);
+  void dstore_0();
+  void dstore_1();
+  void dstore_2();
+  void dstore_3();
+  void dsub();
+  void dup();
+  void dup_x1();
+  void dup_x2();
+  void dup2();
+  void dup2_x1();
+  void dup2_x2();
+
+  void f2d();
+  void f2i();
+  void f2l();
+  void fadd();
+  void faload();
+  void fastore();
+  void fcmpg();
+  void fcmpl();
+  void fconst_0();
+  void fconst_1();
+  void fconst_2();
+  void fdiv();
+  void fload(int localVarIndex);
+  void fload_0();
+  void fload_1();
+  void fload_2();
+  void fload_3();
+  void fmul();
+  void fneg();
+  void frem();
+  void freturn();
+  void fstore(int localVarIndex);
+  void fstore_0();
+  void fstore_1();
+  void fstore_2();
+  void fstore_3();
+  void fsub();
+
+  void getfield(int cpFieldRefIndex);
+  void getstatic(int cpFieldRefIndex);
+  void goto_(int pcOffset);
+  void goto_w (int pcOffset);
+
+  void i2b();
+  void i2c();
+  void i2d();
+  void i2f();
+  void i2l();
+  void i2s();
+  void iadd();
+  void iaload();
+  void iand();
+  void iastore();
+  void iconst_m1();
+  void iconst_0();
+  void iconst_1();
+  void iconst_2();
+  void iconst_3();
+  void iconst_4();
+  void iconst_5();
+  void idiv();
+  void if_acmpeq(int pcOffset);
+  void if_acmpne(int pcOffset);
+  void if_icmpeq(int pcOffset);
+  void if_icmpne(int pcOffset);
+  void if_icmplt(int pcOffset);
+  void if_icmpge(int pcOffset);
+  void if_icmpgt(int pcOffset);
+  void if_icmple(int pcOffset);
+  void ifeq(int pcOffset);
+  void ifne(int pcOffset);
+  void iflt(int pcOffset);
+  void ifge(int pcOffset);
+  void ifgt(int pcOffset);
+  void ifle(int pcOffset);
+  void ifnonnull(int pcOffset);
+  void ifnull(int pcOffset);
+  void iinc(int localVarIndex, int incConstant);
+  void iload(int localVarIndex);
+  void iload_0();
+  void iload_1();
+  void iload_2();
+  void iload_3();
+  void imul();
+  void ineg();
+  void instanceof_(int cpClassIndex);
+  void invokeinterface (int cpInterfaceMethodRefIndex, int count, int zero);
+  void invokedynamic (int cpInvokeDynamicIndex);
+  void invokespecial (int cpMethodRefIndex);
+  void invokestatic (int cpMethodRefIndex);
+  void invokevirtual (int cpMethodRefIndex);
+  void ior();
+  void irem();
+  void ireturn();
+  void ishl();
+  void ishr();
+  void istore(int localVarIndex);
+  void istore_0();
+  void istore_1();
+  void istore_2();
+  void istore_3();
+  void isub();
+  void iushr();
+  void ixor();
+
+  void jsr(int pcOffset);
+  void jsr_w(int pcOffset);
+
+  void l2d();
+  void l2f();
+  void l2i();
+  void ladd();
+  void laload();
+  void land();
+  void lastore();
+  void lcmp();
+  void lconst_0();
+  void lconst_1();
+  void ldc_(int cpIntOrFloatOrStringIndex);
+  void ldc_w_(int cpIntOrFloatOrStringIndex);
+  void ldc2_w(int cpLongOrDoubleIndex);
+  void ldiv();
+  void lload(int localVarIndex);
+  void lload_0();
+  void lload_1();
+  void lload_2();
+  void lload_3();
+  void lmul();
+  void lneg();
+  void lookupswitch(int defaultPcOffset, int nEntries);
+  void lookupswitchEntry(int index, int match, int pcOffset);
+  void lor();
+  void lrem();
+  void lreturn();
+  void lshl();
+  void lshr();
+  void lstore(int localVarIndex);
+  void lstore_0();
+  void lstore_1();
+  void lstore_2();
+  void lstore_3();
+  void lsub();
+  void lushr();
+  void lxor();
+
+  void monitorenter();
+  void monitorexit();
+  void multianewarray(int cpClassIndex, int dimensions);
+
+  void new_(int cpClassIndex);
+  void newarray(int typeCode);
+  void nop();
+
+  void pop();
+  void pop2();
+  void putfield(int cpFieldRefIndex);
+  void putstatic(int cpFieldRefIndex);
+
+  void ret(int localVarIndex);
+  void return_();
+
+  void saload();
+  void sastore();
+  void sipush(int val);
+  void swap();
+
+  void tableswitch(int defaultPcOffset, int low, int high);
+  void tableswitchEntry(int value, int pcOffset);
+
+  void wide ();
+
+  void unknown(int bytecode);
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMByteCodeReaderAdapter.java b/src/main/gov/nasa/jpf/jvm/JVMByteCodeReaderAdapter.java
new file mode 100644 (file)
index 0000000..7b7ea3b
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.jvm.JVMByteCodeReader;
+
+/**
+ * empty implementation of a JVMByteCodeReader, to efficiently allow overriding
+ * single methods
+ */
+public class JVMByteCodeReaderAdapter implements JVMByteCodeReader {
+
+  @Override
+  public void aconst_null() {}
+  @Override
+  public void aload(int localVarIndex) {}
+  @Override
+  public void aload_0() {}
+  @Override
+  public void aload_1() {}
+  @Override
+  public void aload_2() {}
+  @Override
+  public void aload_3() {}
+  @Override
+  public void aaload() {}
+  @Override
+  public void astore(int localVarIndex) {}
+  @Override
+  public void astore_0() {}
+  @Override
+  public void astore_1() {}
+  @Override
+  public void astore_2() {}
+  @Override
+  public void astore_3() {}
+  @Override
+  public void aastore() {}
+  @Override
+  public void areturn() {}
+  @Override
+  public void anewarray(int cpClassIndex) {}
+  @Override
+  public void arraylength() {}
+  @Override
+  public void athrow() {}
+
+  @Override
+  public void baload() {}
+  @Override
+  public void bastore() {}
+  @Override
+  public void bipush(int b) {}
+
+  @Override
+  public void caload() {}
+  @Override
+  public void castore() {}
+  @Override
+  public void checkcast(int cpClassIndex) {}
+
+  @Override
+  public void d2f() {}
+  @Override
+  public void d2i() {}
+  @Override
+  public void d2l() {}
+  @Override
+  public void dadd() {}
+  @Override
+  public void daload() {}
+  @Override
+  public void dastore() {}
+  @Override
+  public void dcmpg() {}
+  @Override
+  public void dcmpl() {}
+  @Override
+  public void dconst_0() {}
+  @Override
+  public void dconst_1() {}
+  @Override
+  public void ddiv() {}
+  @Override
+  public void dload(int localVarIndex) {}
+  @Override
+  public void dload_0() {}
+  @Override
+  public void dload_1() {}
+  @Override
+  public void dload_2() {}
+  @Override
+  public void dload_3() {}
+  @Override
+  public void dmul() {}
+  @Override
+  public void dneg() {}
+  @Override
+  public void drem() {}
+  @Override
+  public void dreturn() {}
+  @Override
+  public void dstore(int localVarIndex) {}
+  @Override
+  public void dstore_0() {}
+  @Override
+  public void dstore_1() {}
+  @Override
+  public void dstore_2() {}
+  @Override
+  public void dstore_3() {}
+  @Override
+  public void dsub() {}
+  @Override
+  public void dup() {}
+  @Override
+  public void dup_x1() {}
+  @Override
+  public void dup_x2() {}
+  @Override
+  public void dup2() {}
+  @Override
+  public void dup2_x1() {}
+  @Override
+  public void dup2_x2() {}
+
+  @Override
+  public void f2d() {}
+  @Override
+  public void f2i() {}
+  @Override
+  public void f2l() {}
+  @Override
+  public void fadd() {}
+  @Override
+  public void faload() {}
+  @Override
+  public void fastore() {}
+  @Override
+  public void fcmpg() {}
+  @Override
+  public void fcmpl() {}
+  @Override
+  public void fconst_0() {}
+  @Override
+  public void fconst_1() {}
+  @Override
+  public void fconst_2() {}
+  @Override
+  public void fdiv() {}
+  @Override
+  public void fload(int localVarIndex) {}
+  @Override
+  public void fload_0() {}
+  @Override
+  public void fload_1() {}
+  @Override
+  public void fload_2() {}
+  @Override
+  public void fload_3() {}
+  @Override
+  public void fmul() {}
+  @Override
+  public void fneg() {}
+  @Override
+  public void frem() {}
+  @Override
+  public void freturn() {}
+  @Override
+  public void fstore(int localVarIndex) {}
+  @Override
+  public void fstore_0() {}
+  @Override
+  public void fstore_1() {}
+  @Override
+  public void fstore_2() {}
+  @Override
+  public void fstore_3() {}
+  @Override
+  public void fsub() {}
+
+  @Override
+  public void getfield(int cpFieldRefIndex) {}
+  @Override
+  public void getstatic(int cpFieldRefIndex) {}
+  @Override
+  public void goto_(int pcOffset) {}
+  @Override
+  public void goto_w (int pcOffset) {}
+
+  @Override
+  public void i2b() {}
+  @Override
+  public void i2c() {}
+  @Override
+  public void i2d() {}
+  @Override
+  public void i2f() {}
+  @Override
+  public void i2l() {}
+  @Override
+  public void i2s() {}
+  @Override
+  public void iadd() {}
+  @Override
+  public void iaload() {}
+  @Override
+  public void iand() {}
+  @Override
+  public void iastore() {}
+  @Override
+  public void iconst_m1() {}
+  @Override
+  public void iconst_0() {}
+  @Override
+  public void iconst_1() {}
+  @Override
+  public void iconst_2() {}
+  @Override
+  public void iconst_3() {}
+  @Override
+  public void iconst_4() {}
+  @Override
+  public void iconst_5() {}
+  @Override
+  public void idiv() {}
+  @Override
+  public void if_acmpeq(int pcOffset) {}
+  @Override
+  public void if_acmpne(int pcOffset) {}
+  @Override
+  public void if_icmpeq(int pcOffset) {}
+  @Override
+  public void if_icmpne(int pcOffset) {}
+  @Override
+  public void if_icmplt(int pcOffset) {}
+  @Override
+  public void if_icmpge(int pcOffset) {}
+  @Override
+  public void if_icmpgt(int pcOffset) {}
+  @Override
+  public void if_icmple(int pcOffset) {}
+  @Override
+  public void ifeq(int pcOffset) {}
+  @Override
+  public void ifne(int pcOffset) {}
+  @Override
+  public void iflt(int pcOffset) {}
+  @Override
+  public void ifge(int pcOffset) {}
+  @Override
+  public void ifgt(int pcOffset) {}
+  @Override
+  public void ifle(int pcOffset) {}
+  @Override
+  public void ifnonnull(int pcOffset) {}
+  @Override
+  public void ifnull(int pcOffset) {}
+  @Override
+  public void iinc(int localVarIndex, int incConstant) {}
+  @Override
+  public void iload(int localVarIndex) {}
+  @Override
+  public void iload_0() {}
+  @Override
+  public void iload_1() {}
+  @Override
+  public void iload_2() {}
+  @Override
+  public void iload_3() {}
+  @Override
+  public void imul() {}
+  @Override
+  public void ineg() {}
+  @Override
+  public void instanceof_(int cpClassIndex) {}
+  @Override
+  public void invokeinterface (int cpInterfaceMethodRefIndex, int count, int zero) {}
+  @Override
+  public void invokedynamic (int cpInvokeDynamicIndex) {}
+  @Override
+  public void invokespecial (int cpMethodRefIndex) {}
+  @Override
+  public void invokestatic (int cpMethodRefIndex) {}
+  @Override
+  public void invokevirtual (int cpMethodRefIndex) {}
+  @Override
+  public void ior() {}
+  @Override
+  public void irem() {}
+  @Override
+  public void ireturn() {}
+  @Override
+  public void ishl() {}
+  @Override
+  public void ishr() {}
+  @Override
+  public void istore(int localVarIndex) {}
+  @Override
+  public void istore_0() {}
+  @Override
+  public void istore_1() {}
+  @Override
+  public void istore_2() {}
+  @Override
+  public void istore_3() {}
+  @Override
+  public void isub() {}
+  @Override
+  public void iushr() {}
+  @Override
+  public void ixor() {}
+
+  @Override
+  public void jsr(int pcOffset) {}
+  @Override
+  public void jsr_w(int pcOffset) {}
+
+  @Override
+  public void l2d() {}
+  @Override
+  public void l2f() {}
+  @Override
+  public void l2i() {}
+  @Override
+  public void ladd() {}
+  @Override
+  public void laload() {}
+  @Override
+  public void land() {}
+  @Override
+  public void lastore() {}
+  @Override
+  public void lcmp() {}
+  @Override
+  public void lconst_0() {}
+  @Override
+  public void lconst_1() {}
+  @Override
+  public void ldc_(int cpIntOrFloatOrStringIndex) {}
+  @Override
+  public void ldc_w_(int cpIntOrFloatOrStringIndex) {}
+  @Override
+  public void ldc2_w(int cpLongOrDoubleIndex) {}
+  @Override
+  public void ldiv() {}
+  @Override
+  public void lload(int localVarIndex) {}
+  @Override
+  public void lload_0() {}
+  @Override
+  public void lload_1() {}
+  @Override
+  public void lload_2() {}
+  @Override
+  public void lload_3() {}
+  @Override
+  public void lmul() {}
+  @Override
+  public void lneg() {}
+  @Override
+  public void lookupswitch(int defaultPcOffset, int nEntries) {}
+  @Override
+  public void lookupswitchEntry(int index, int match, int pcOffset) {}
+  @Override
+  public void lor() {}
+  @Override
+  public void lrem() {}
+  @Override
+  public void lreturn() {}
+  @Override
+  public void lshl() {}
+  @Override
+  public void lshr() {}
+  @Override
+  public void lstore(int localVarIndex) {}
+  @Override
+  public void lstore_0() {}
+  @Override
+  public void lstore_1() {}
+  @Override
+  public void lstore_2() {}
+  @Override
+  public void lstore_3() {}
+  @Override
+  public void lsub() {}
+  @Override
+  public void lushr() {}
+  @Override
+  public void lxor() {}
+
+  @Override
+  public void monitorenter() {}
+  @Override
+  public void monitorexit() {}
+  @Override
+  public void multianewarray(int cpClassIndex, int dimensions) {}
+
+  @Override
+  public void new_(int cpClassIndex) {}
+  @Override
+  public void newarray(int typeCode) {}
+  @Override
+  public void nop() {}
+
+  @Override
+  public void pop() {}
+  @Override
+  public void pop2() {}
+  @Override
+  public void putfield(int cpFieldRefIndex) {}
+  @Override
+  public void putstatic(int cpFieldRefIndex) {}
+
+  @Override
+  public void ret(int localVarIndex) {}
+  @Override
+  public void return_() {}
+
+  @Override
+  public void saload() {}
+  @Override
+  public void sastore() {}
+  @Override
+  public void sipush(int val) {}
+  @Override
+  public void swap() {}
+
+  @Override
+  public void tableswitch(int defaultPcOffset, int low, int high) {}
+  @Override
+  public void tableswitchEntry(int value, int pcOffset) {}
+
+  @Override
+  public void wide () {}
+
+  @Override
+  public void unknown(int bytecode) {}
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMClassFileContainer.java b/src/main/gov/nasa/jpf/jvm/JVMClassFileContainer.java
new file mode 100644 (file)
index 0000000..94239e0
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.ClassFileContainer;
+import gov.nasa.jpf.vm.ClassFileMatch;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ClassParseException;
+
+/**
+ * ClassFileContainer that holds Java classfiles
+ */
+public abstract class JVMClassFileContainer extends ClassFileContainer {
+  
+  // the VM and container type specific info we need to instantiate a ClassInfo from this container
+  public class JVMClassFileMatch extends ClassFileMatch {
+    byte[] data;
+    
+    JVMClassFileMatch (String typeName, String url, byte[] data) {
+      super(typeName, url);
+      
+      this.data = data;
+    }
+    
+    @Override
+    public ClassFileContainer getContainer(){
+      return JVMClassFileContainer.this;
+    }
+    
+    public byte[] getData(){
+      return data;
+    }
+    
+    @Override
+    public JVMClassInfo createClassInfo (ClassLoaderInfo loader) throws ClassParseException {
+      JVMSystemClassLoaderInfo sysCli = (JVMSystemClassLoaderInfo)loader.getSystemClassLoader();
+      
+      JVMCodeBuilder cb = sysCli.getCodeBuilder(typeName);
+      ClassFile cf = new ClassFile(data);
+      
+      return new JVMClassInfo( typeName, loader, cf, url, cb);
+    }
+    
+    @Override
+    public AnnotationInfo createAnnotationInfo (ClassLoaderInfo loader) throws ClassParseException {
+      ClassFile cf = new ClassFile(data);
+      JVMAnnotationParser parser = new JVMAnnotationParser(cf);
+
+      return new AnnotationInfo(typeName, loader, parser);
+    }
+  }
+  
+  protected JVMClassFileContainer (String name, String url) {
+    super(name, url);
+  }
+  
+  @Override
+  public String getClassURL (String typeName){
+    return getURL() + typeName.replace('.', '/') + ".class";
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMClassInfo.java b/src/main/gov/nasa/jpf/jvm/JVMClassInfo.java
new file mode 100644 (file)
index 0000000..3cd1214
--- /dev/null
@@ -0,0 +1,950 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.*;
+
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+
+/**
+ * a ClassInfo that was created from a Java classfile
+ */
+public class JVMClassInfo extends ClassInfo {
+
+  /**
+   * this is the inner class that does the actual ClassInfo initialization from ClassFile. It is an inner class so that
+   * (a) it can set ClassInfo fields, (b) it can extend ClassFileReaderAdapter, and (c) we don't clutter JVMClassInfo with
+   * fields that are only temporarily used during parsing
+   */
+  class Initializer extends ClassFileReaderAdapter {
+    protected ClassFile cf;
+    protected JVMCodeBuilder cb;
+
+    public Initializer (ClassFile cf, JVMCodeBuilder cb) throws ClassParseException {
+      this.cf = cf;
+      this.cb = cb;
+      
+      cf.parse(this);
+    }
+
+    @Override
+    public void setClass (ClassFile cf, String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
+      JVMClassInfo.this.setClass(clsName, superClsName, flags, cpCount);
+    }
+
+    @Override
+    public void setClassAttribute (ClassFile cf, int attrIndex, String name, int attrLength) {
+      if (name == ClassFile.SOURCE_FILE_ATTR) {
+        cf.parseSourceFileAttr(this, null);
+
+      } else if (name == ClassFile.SIGNATURE_ATTR) {
+        cf.parseSignatureAttr(this, JVMClassInfo.this);
+
+      } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
+        cf.parseAnnotationsAttr(this, JVMClassInfo.this);
+
+      } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
+        //cf.parseAnnotationsAttr(this, ClassInfo.this);
+        
+      } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
+        cf.parseTypeAnnotationsAttr(this, JVMClassInfo.this);
+        
+      } else if (name == ClassFile.INNER_CLASSES_ATTR) {
+        cf.parseInnerClassesAttr(this, JVMClassInfo.this);
+
+      } else if (name == ClassFile.ENCLOSING_METHOD_ATTR) {
+        cf.parseEnclosingMethodAttr(this, JVMClassInfo.this);
+        
+      } else if (name == ClassFile.BOOTSTRAP_METHOD_ATTR) {
+        cf.parseBootstrapMethodAttr(this, JVMClassInfo.this);
+        
+      }
+    }
+    
+    @Override
+    public void setBootstrapMethodCount (ClassFile cf, Object tag, int count) {
+      bootstrapMethods = new BootstrapMethodInfo[count];
+    }
+    
+    @Override
+    public void setBootstrapMethod (ClassFile cf, Object tag, int idx, int refKind, String cls, String mth, String descriptor, int[] cpArgs) {    
+   
+      int lambdaRefKind = cf.mhRefTypeAt(cpArgs[1]);
+      
+      int mrefIdx = cf.mhMethodRefIndexAt(cpArgs[1]);
+      String clsName = cf.methodClassNameAt(mrefIdx).replace('/', '.');
+      ClassInfo eclosingLambdaCls;
+      
+      if(!clsName.equals(JVMClassInfo.this.getName())) {
+        eclosingLambdaCls = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+      } else {
+        eclosingLambdaCls = JVMClassInfo.this;
+      }
+      
+      assert (eclosingLambdaCls!=null);
+      
+      String mthName = cf.methodNameAt(mrefIdx);
+      String signature = cf.methodDescriptorAt(mrefIdx);
+      
+      MethodInfo lambdaBody = eclosingLambdaCls.getMethod(mthName + signature, false);
+      
+      String samDescriptor = cf.methodTypeDescriptorAt(cpArgs[2]);
+            
+      if(lambdaBody!=null) {
+        bootstrapMethods[idx] = new BootstrapMethodInfo(lambdaRefKind, JVMClassInfo.this, lambdaBody, samDescriptor);
+      }
+    }
+    
+   //--- inner/enclosing classes 
+    @Override
+    public void setInnerClassCount (ClassFile cf, Object tag, int classCount) {
+      innerClassNames = new String[classCount];
+    }
+
+    @Override
+    public void setInnerClass (ClassFile cf, Object tag, int innerClsIndex,
+            String outerName, String innerName, String innerSimpleName, int accessFlags) {
+      // Ok, this is a total mess - some names are in dot notation, others use '/'
+      // and to make it even more confusing, some InnerClass attributes refer NOT
+      // to the currently parsed class, so we have to check if we are the outerName,
+      // but then 'outerName' can also be null instead of our own name.
+      // Oh, and there are also InnerClass attributes that have their own name as inner names
+      // (see java/lang/String$CaseInsensitiveComparator or ...System and java/lang/System$1 for instance)
+      if (outerName != null) {
+        outerName = Types.getClassNameFromTypeName(outerName);
+      }
+
+      innerName = Types.getClassNameFromTypeName(innerName);
+      if (!innerName.equals(name)) {
+        innerClassNames[innerClsIndex] = innerName;
+
+      } else {
+        // this refers to ourself, and can be a force fight with setEnclosingMethod
+        if (outerName != null) { // only set if this is a direct member, otherwise taken from setEnclosingMethod
+          setEnclosingClass(outerName);
+        }
+      }
+    }
+
+    @Override
+    public void setEnclosingMethod (ClassFile cf, Object tag, String enclosingClassName, String enclosingMethodName, String descriptor) {
+      setEnclosingClass(enclosingClassName);
+
+      if (enclosingMethodName != null) {
+        JVMClassInfo.this.setEnclosingMethod(enclosingMethodName + descriptor);
+      }
+    }
+
+    @Override
+    public void setInnerClassesDone (ClassFile cf, Object tag) {
+      // we have to check if we allocated too many - see the mess above
+      for (int i = 0; i < innerClassNames.length; i++) {
+        innerClassNames = Misc.stripNullElements(innerClassNames);
+      }
+    }
+
+    //--- source file
+    @Override
+    public void setSourceFile (ClassFile cf, Object tag, String fileName) {
+      JVMClassInfo.this.setSourceFile(fileName);
+    }
+    
+    //--- interfaces
+    @Override
+    public void setInterfaceCount (ClassFile cf, int ifcCount) {
+      interfaceNames = new String[ifcCount];
+    }
+
+    @Override
+    public void setInterface (ClassFile cf, int ifcIndex, String ifcName) {
+      interfaceNames[ifcIndex] = Types.getClassNameFromTypeName(ifcName);
+    }
+
+    //--- fields
+    // unfortunately they are stored together in the ClassFile, i.e. we 
+    // have to split them up once we are done
+    
+    protected FieldInfo[] fields;
+    protected FieldInfo curFi; // need to cache for attributes
+
+    @Override
+    public void setFieldCount (ClassFile cf, int fieldCount) {
+      if (fieldCount > 0){
+        fields = new FieldInfo[fieldCount];
+      } else {
+        fields = null;
+      }
+    }
+
+    @Override
+    public void setField (ClassFile cf, int fieldIndex, int accessFlags, String name, String descriptor) {
+      FieldInfo fi = FieldInfo.create(name, descriptor, accessFlags);
+      fields[fieldIndex] = fi;
+      curFi = fi; // for attributes
+    }
+
+    @Override
+    public void setFieldAttribute (ClassFile cf, int fieldIndex, int attrIndex, String name, int attrLength) {
+      if (name == ClassFile.SIGNATURE_ATTR) {
+        cf.parseSignatureAttr(this, curFi);
+
+      } else if (name == ClassFile.CONST_VALUE_ATTR) {
+        cf.parseConstValueAttr(this, curFi);
+
+      } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
+        cf.parseAnnotationsAttr(this, curFi);
+
+      } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
+        cf.parseTypeAnnotationsAttr(this, curFi);
+        
+      } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
+        //cf.parseAnnotationsAttr(this, curFi);
+      }
+    }
+
+    @Override
+    public void setConstantValue (ClassFile cf, Object tag, Object constVal) {
+      curFi.setConstantValue(constVal);
+    }
+
+    @Override
+    public void setFieldsDone (ClassFile cf) {
+      setFields(fields);
+    }
+  //--- declaredMethods
+    protected MethodInfo curMi;
+
+    @Override
+    public void setMethodCount (ClassFile cf, int methodCount) {
+      methods = new LinkedHashMap<String, MethodInfo>();
+    }
+
+    @Override
+    public void setMethod (ClassFile cf, int methodIndex, int accessFlags, String name, String signature) {
+      MethodInfo mi = MethodInfo.create(name, signature, accessFlags);
+      curMi = mi;
+    }
+    
+    @Override
+    public void setMethodDone (ClassFile cf, int methodIndex){
+      curMi.setLocalVarAnnotations();
+
+      JVMClassInfo.this.setMethod(curMi);
+    }
+
+    @Override
+    public void setMethodAttribute (ClassFile cf, int methodIndex, int attrIndex, String name, int attrLength) {
+      if (name == ClassFile.CODE_ATTR) {
+        cf.parseCodeAttr(this, curMi);
+
+      } else if (name == ClassFile.SIGNATURE_ATTR) {
+        cf.parseSignatureAttr(this, curMi);
+
+      } else if (name == ClassFile.EXCEPTIONS_ATTR) {
+        cf.parseExceptionAttr(this, curMi);
+
+      } else if (name == ClassFile.RUNTIME_VISIBLE_ANNOTATIONS_ATTR) {
+        cf.parseAnnotationsAttr(this, curMi);
+
+      } else if (name == ClassFile.RUNTIME_INVISIBLE_ANNOTATIONS_ATTR) {
+        //cf.parseAnnotationsAttr(this, curMi);
+      } else if (name == ClassFile.RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
+        cf.parseParameterAnnotationsAttr(this, curMi);
+
+      } else if (name == ClassFile.RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS_ATTR) {
+        //cf.parseParameterAnnotationsAttr(this, curMi);
+        
+      } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR) {
+        cf.parseTypeAnnotationsAttr(this, curMi);
+      }      
+      
+    }
+
+    //--- current methods throws list
+    protected String[] exceptions;
+
+    @Override
+    public void setExceptionCount (ClassFile cf, Object tag, int exceptionCount) {
+      exceptions = new String[exceptionCount];
+    }
+
+    @Override
+    public void setException (ClassFile cf, Object tag, int exceptionIndex, String exceptionType) {
+      exceptions[exceptionIndex] = Types.getClassNameFromTypeName(exceptionType);
+    }
+
+    @Override
+    public void setExceptionsDone (ClassFile cf, Object tag) {
+      curMi.setThrownExceptions(exceptions);
+    }
+
+    //--- current method exception handlers
+    protected ExceptionHandler[] handlers;
+
+    @Override
+    public void setExceptionHandlerTableCount (ClassFile cf, Object tag, int exceptionTableCount) {
+      handlers = new ExceptionHandler[exceptionTableCount];
+    }
+
+    @Override
+    public void setExceptionHandler (ClassFile cf, Object tag, int handlerIndex,
+            int startPc, int endPc, int handlerPc, String catchType) {
+      ExceptionHandler xh = new ExceptionHandler(catchType, startPc, endPc, handlerPc);
+      handlers[handlerIndex] = xh;
+    }
+
+    @Override
+    public void setExceptionHandlerTableDone (ClassFile cf, Object tag) {
+      curMi.setExceptionHandlers(handlers);
+    }
+
+    //--- current method code  
+    @Override
+    public void setCode (ClassFile cf, Object tag, int maxStack, int maxLocals, int codeLength) {
+      curMi.setMaxLocals(maxLocals);
+      curMi.setMaxStack(maxStack);
+
+      cb.reset(cf, curMi);
+
+      cf.parseBytecode(cb, tag, codeLength);
+      cb.installCode();
+    }
+
+    @Override
+    public void setCodeAttribute (ClassFile cf, Object tag, int attrIndex, String name, int attrLength) {
+      if (name == ClassFile.LINE_NUMBER_TABLE_ATTR) {
+        cf.parseLineNumberTableAttr(this, tag);
+
+      } else if (name == ClassFile.LOCAL_VAR_TABLE_ATTR) {
+        cf.parseLocalVarTableAttr(this, tag);
+        
+      } else if (name == ClassFile.RUNTIME_VISIBLE_TYPE_ANNOTATIONS_ATTR){
+        cf.parseTypeAnnotationsAttr(this, tag);
+      }
+    }
+
+    //--- current method line numbers
+    protected int[] lines, startPcs;
+
+    @Override
+    public void setLineNumberTableCount (ClassFile cf, Object tag, int lineNumberCount) {
+      lines = new int[lineNumberCount];
+      startPcs = new int[lineNumberCount];
+    }
+
+    @Override
+    public void setLineNumber (ClassFile cf, Object tag, int lineIndex, int lineNumber, int startPc) {
+      lines[lineIndex] = lineNumber;
+      startPcs[lineIndex] = startPc;
+    }
+
+    @Override
+    public void setLineNumberTableDone (ClassFile cf, Object tag) {
+      curMi.setLineNumbers(lines, startPcs);
+    }
+    
+    //--- current method local variables
+    protected LocalVarInfo[] localVars;
+
+    @Override
+    public void setLocalVarTableCount (ClassFile cf, Object tag, int localVarCount) {
+      localVars = new LocalVarInfo[localVarCount];
+    }
+
+    @Override
+    public void setLocalVar (ClassFile cf, Object tag, int localVarIndex,
+            String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex) {
+      LocalVarInfo lvi = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex);
+      localVars[localVarIndex] = lvi;
+    }
+
+    @Override
+    public void setLocalVarTableDone (ClassFile cf, Object tag) {
+      curMi.setLocalVarTable(localVars);
+    }
+    
+    //--- annotations
+    protected AnnotationInfo[] annotations;
+    protected AnnotationInfo curAi;
+    protected AnnotationInfo[][] parameterAnnotations;
+    protected Object[] values;
+
+    //--- declaration annotations
+    
+    @Override
+    public void setAnnotationCount (ClassFile cf, Object tag, int annotationCount) {
+      annotations = new AnnotationInfo[annotationCount];
+    }
+
+    @Override
+    public void setAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
+      if (tag instanceof InfoObject) {
+        curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+        annotations[annotationIndex] = curAi;
+      }
+    }
+    
+    @Override
+    public void setAnnotationsDone (ClassFile cf, Object tag) {
+      if (tag instanceof InfoObject) {
+        ((InfoObject) tag).addAnnotations(annotations);
+      }
+    }
+
+    @Override
+    public void setParameterCount (ClassFile cf, Object tag, int parameterCount) {
+      parameterAnnotations = new AnnotationInfo[parameterCount][];
+    }
+
+    @Override
+    public void setParameterAnnotationCount (ClassFile cf, Object tag, int paramIndex, int annotationCount) {
+      annotations = new AnnotationInfo[annotationCount];
+      parameterAnnotations[paramIndex] = annotations;
+    }
+
+    @Override
+    public void setParameterAnnotation (ClassFile cf, Object tag, int annotationIndex, String annotationType) {
+      curAi = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      annotations[annotationIndex] = curAi;
+    }
+
+    @Override
+    public void setParametersDone (ClassFile cf, Object tag) {
+      curMi.setParameterAnnotations(parameterAnnotations);
+    }
+    
+    //--- Java 8 type annotations    
+    
+    @Override
+    public void setTypeAnnotationCount(ClassFile cf, Object tag, int annotationCount){
+      annotations = new AnnotationInfo[annotationCount];
+    }
+
+    @Override
+    public void setTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                           int typeIndex, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new TypeParameterAnnotationInfo(base, targetType, typePath, typeIndex);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setSuperTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                       int superTypeIdx, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new SuperTypeAnnotationInfo(base, targetType, typePath, superTypeIdx);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setTypeParameterBoundAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
+                                       int typeIndex, int boundIndex, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new TypeParameterBoundAnnotationInfo(base, targetType, typePath, typeIndex, boundIndex);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setTypeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType,
+                                  short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new TypeAnnotationInfo(base, targetType, typePath);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setFormalParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                             int paramIndex, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new FormalParameterAnnotationInfo(base, targetType, typePath, paramIndex);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setThrowsAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                    int throwsTypeIdx, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new ThrowsAnnotationInfo(base, targetType, typePath, throwsTypeIdx);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setVariableAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                      long[] scopeEntries, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      VariableAnnotationInfo vai = new VariableAnnotationInfo(base, targetType, typePath, scopeEntries);
+      curAi = vai;
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setExceptionParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                                int exceptionIndex, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi= new ExceptionParameterAnnotationInfo(base, targetType, typePath, exceptionIndex);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setBytecodeAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                      int offset, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new BytecodeAnnotationInfo(base, targetType, typePath, offset);
+      annotations[annotationIndex] = curAi;
+    }
+    @Override
+    public void setBytecodeTypeParameterAnnotation(ClassFile cf, Object tag, int annotationIndex, int targetType, 
+                                             int offset, int typeArgIdx, short[] typePath, String annotationType){
+      AnnotationInfo base = getResolvedAnnotationInfo(Types.getClassNameFromTypeName(annotationType));
+      curAi = new BytecodeTypeParameterAnnotationInfo(base, targetType, typePath, offset, typeArgIdx);
+      annotations[annotationIndex] = curAi;
+    }
+
+    @Override
+    public void setTypeAnnotationsDone(ClassFile cf, Object tag) {
+      if (tag instanceof InfoObject) {
+        int len = annotations.length;
+        AbstractTypeAnnotationInfo[] tais = new AbstractTypeAnnotationInfo[annotations.length];
+        for (int i=0; i<len; i++){
+          tais[i] = (AbstractTypeAnnotationInfo)annotations[i];
+        }
+        
+        // we can get them in batches (e.g. VariableTypeAnnos from code attrs and ReturnTypeAnnos from method attrs
+        ((InfoObject) tag).addTypeAnnotations( tais);
+      }
+    }
+
+    //--- AnnotationInfo values entries
+    @Override
+    public void setAnnotationValueCount (ClassFile cf, Object tag, int annotationIndex, int nValuePairs) {
+      // if we have values, we need to clone the defined annotation so that we can overwrite entries
+      curAi = curAi.cloneForOverriddenValues();
+      annotations[annotationIndex] = curAi;
+    }
+    
+    @Override
+    public void setPrimitiveAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+            String elementName, int arrayIndex, Object val) {
+      if (arrayIndex >= 0) {
+        values[arrayIndex] = val;
+      } else {
+        curAi.setClonedEntryValue(elementName, val);
+      }
+    }
+
+    @Override
+    public void setStringAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+            String elementName, int arrayIndex, String val) {
+      if (arrayIndex >= 0) {
+        values[arrayIndex] = val;
+      } else {
+        curAi.setClonedEntryValue(elementName, val);
+      }
+    }
+
+    @Override
+    public void setClassAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName,
+            int arrayIndex, String typeName) {
+      Object val = AnnotationInfo.getClassValue(typeName);
+      if (arrayIndex >= 0) {
+        values[arrayIndex] = val;
+      } else {
+        curAi.setClonedEntryValue(elementName, val);
+      }
+    }
+
+    @Override
+    public void setEnumAnnotationValue (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+            String elementName, int arrayIndex, String enumType, String enumValue) {
+      Object val = AnnotationInfo.getEnumValue(enumType, enumValue);
+      if (arrayIndex >= 0) {
+        values[arrayIndex] = val;
+      } else {
+        curAi.setClonedEntryValue(elementName, val);
+      }
+    }
+
+    @Override
+    public void setAnnotationValueElementCount (ClassFile cf, Object tag, int annotationIndex, int valueIndex,
+            String elementName, int elementCount) {
+      values = new Object[elementCount];
+    }
+
+    @Override
+    public void setAnnotationValueElementsDone (ClassFile cf, Object tag, int annotationIndex, int valueIndex, String elementName) {
+      curAi.setClonedEntryValue(elementName, values);
+    }
+
+    //--- common attrs
+    @Override
+    public void setSignature (ClassFile cf, Object tag, String signature) {
+      if (tag instanceof GenericSignatureHolder) {
+        ((GenericSignatureHolder) tag).setGenericSignature(signature);
+      }
+    }
+  }
+
+  // since nested class init locking can explode the state space, we make it optional and controllable
+  protected static boolean nestedInit;
+  protected static StringSetMatcher includeNestedInit;
+  protected static StringSetMatcher excludeNestedInit;
+
+  protected static boolean init (Config config){
+    nestedInit = config.getBoolean("jvm.nested_init", false);
+    if (nestedInit){
+      includeNestedInit =  StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.include"));
+      excludeNestedInit = StringSetMatcher.getNonEmpty(config.getStringArray("jvm.nested_init.exclude"));
+    }
+
+    return true;
+  }
+
+  JVMClassInfo (String name, ClassLoaderInfo cli, ClassFile cf, String srcUrl, JVMCodeBuilder cb) throws ClassParseException {
+    super( name, cli, srcUrl);
+    
+    new Initializer( cf, cb); // we just need the ctor
+    
+    resolveAndLink();
+  }
+  
+  
+  //--- for annotation classinfos
+  
+  // called on the annotation classinfo
+  @Override
+  protected ClassInfo createAnnotationProxy (String proxyName){
+    return new JVMClassInfo (this, proxyName, classLoader, null);
+  }
+  
+  // concrete proxy ctor
+  protected JVMClassInfo (ClassInfo ciAnnotation, String proxyName, ClassLoaderInfo cli, String url) {
+    super( ciAnnotation, proxyName, cli, url);
+  }
+
+  /**
+   * This is called on the functional interface type. It creates a synthetic type which 
+   * implements the functional interface and contains a method capturing the behavior 
+   * of the lambda expression.
+   */
+  @Override
+  protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
+    return new JVMClassInfo(this, bootstrapMethod, name, samUniqueName, fieldTypesName);
+  }
+  
+  protected JVMClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
+    super(funcInterface, bootstrapMethod, name, fieldTypesName);
+    
+    // creating a method corresponding to the single abstract method of the functional interface
+    methods = new HashMap<String, MethodInfo>();
+    
+    MethodInfo fiMethod = funcInterface.getInterfaceAbstractMethod();
+    int modifiers = fiMethod.getModifiers() & (~Modifier.ABSTRACT);
+    int nLocals = fiMethod.getArgumentsSize();
+    int nOperands = this.nInstanceFields + nLocals;
+
+    MethodInfo mi = new MethodInfo(fiMethod.getName(), fiMethod.getSignature(), modifiers, nLocals, nOperands);
+    mi.linkToClass(this);
+    
+    methods.put(mi.getUniqueName(), mi);
+    
+    setLambdaDirectCallCode(mi, bootstrapMethod);
+    
+    try {
+      resolveAndLink();
+    } catch (ClassParseException e) {
+      // we do not even get here - this a synthetic class, and at this point
+      // the interfaces are already loaded.
+    }
+  }
+
+  /**
+   * perform initialization of this class and its not-yet-initialized superclasses (top down),
+   * which includes calling clinit() methods
+   *
+   * This is overridden here to model a questionable yet consequential behavior of hotspot, which
+   * is holding derived class locks when initializing base classes. The generic implementation in
+   * ClassInfo uses non-nested locks (i.e. A.clinit() only synchronizes on A.class) and hence cannot
+   * produce the same static init deadlocks as hotspot. In order to catch such defects we implement
+   * nested locking here.
+   *
+   * The main difference is that the generic implementation only pushes DCSFs for required clinits
+   * and otherwise doesn't lock anything. Here, we create one static init specific DCSF which wraps
+   * all clinits in nested monitorenter/exits. We create this even if there is no clinit so that we
+   * mimic hotspot locking.
+   *
+   * Note this scheme also enables us to get rid of the automatic clinit sync (they don't have
+   * a 0x20 sync modifier in classfiles)
+   *
+   * @return true if client needs to re-execute because we pushed DirectCallStackFrames
+   */
+  @Override
+  public boolean initializeClass(ThreadInfo ti) {
+    if (needsInitialization(ti)) {
+      if (nestedInit && StringSetMatcher.isMatch(name, includeNestedInit, excludeNestedInit)) {
+        registerClass(ti); // this is recursively upwards
+        int nOps = 2 * (getNumberOfSuperClasses() + 1); // this is just an upper bound for the number of operands we need
+
+        MethodInfo miInitialize = new MethodInfo("[initializeClass]", "()V", Modifier.STATIC, 0, nOps);
+        JVMDirectCallStackFrame frame = new JVMDirectCallStackFrame(miInitialize, null);
+        JVMCodeBuilder cb = getSystemCodeBuilder(null, miInitialize);
+
+        addClassInit(ti, frame, cb); // this is recursively upwards until we hit a initialized superclass
+        cb.directcallreturn();
+        cb.installCode();
+
+        // this is normally initialized in the ctor, but at that point we don't have the code yet
+        frame.setPC(miInitialize.getFirstInsn());
+
+        ti.pushFrame(frame);
+        return true; // client has to re-execute, we pushed a stackframe
+
+
+      } else { // use generic initialization without nested locks (directly calling clinits)
+        return super.initializeClass(ti);
+      }
+
+    } else {
+      return false; // nothing to do
+    }
+  }
+
+  protected void addClassInit (ThreadInfo ti, JVMDirectCallStackFrame frame, JVMCodeBuilder cb){
+    int clsObjRef = getClassObjectRef();
+
+    frame.pushRef(clsObjRef);
+    cb.monitorenter();
+
+    if (superClass != null && superClass.needsInitialization(ti)) {
+      ((JVMClassInfo) superClass).addClassInit(ti, frame, cb);      // go recursive
+    }
+
+    if (getMethod("<clinit>()V", false) != null) { // do we have a clinit
+      cb.invokeclinit(this);
+    } else {
+      cb.finishclinit(this);
+      // we can't just do call ci.setInitialized() since that has to be deferred
+    }
+
+    frame.pushRef(clsObjRef);
+    cb.monitorexit();
+  }
+
+  //--- call processing
+  
+  protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi){
+    JVMSystemClassLoaderInfo sysCl = (JVMSystemClassLoaderInfo) ClassLoaderInfo.getCurrentSystemClassLoader();
+    JVMCodeBuilder cb = sysCl.getSystemCodeBuilder(cf, mi);
+    
+    return cb;
+  }
+  
+  /**
+   * to be called from super proxy ctor
+   * this needs to be in the VM specific ClassInfo because we need to create code
+   */
+  @Override
+  protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
+    JVMCodeBuilder cb = getSystemCodeBuilder(null, pmi);
+
+    cb.aload(0);
+    cb.getfield( pmi.getName(), name, pmi.getReturnType());
+    if (fi.isReference()) {
+      cb.areturn();
+    } else {
+      if (fi.getStorageSize() == 1) {
+        cb.ireturn();
+      } else {
+        cb.lreturn();
+      }
+    }
+
+    cb.installCode();
+  }
+  
+  @Override
+  protected void setDirectCallCode (MethodInfo miDirectCall, MethodInfo miCallee){
+    JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
+    
+    String calleeName = miCallee.getName();
+    String calleeSig = miCallee.getSignature();
+
+    if (miCallee.isStatic()){
+      if (miCallee.isClinit()) {
+        cb.invokeclinit(this);
+      } else {
+        cb.invokestatic( name, calleeName, calleeSig);
+      }
+    } else if (name.equals("<init>") || miCallee.isPrivate()){
+      cb.invokespecial( name, calleeName, calleeSig);
+    } else {
+      cb.invokevirtual( name, calleeName, calleeSig);
+    }
+
+    cb.directcallreturn();
+    
+    cb.installCode();
+  }
+  
+  @Override
+  protected void setNativeCallCode (NativeMethodInfo miNative){
+    JVMCodeBuilder cb = getSystemCodeBuilder(null, miNative);
+    
+    cb.executenative(miNative);
+    cb.nativereturn();
+    
+    cb.installCode();
+  }
+  
+  @Override
+  protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
+    JVMCodeBuilder cb = getSystemCodeBuilder(null, miStub);
+    
+    cb.runStart( miStub);
+    cb.invokevirtual( name, miRun.getName(), miRun.getSignature());
+    cb.directcallreturn();
+    
+    cb.installCode();    
+  }
+  
+  /**
+   * This method creates the body of the function object method that captures the 
+   * lambda behavior.
+   */
+  @Override
+  protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod) {
+    
+    MethodInfo miCallee = bootstrapMethod.getLambdaBody();
+    String samSignature = bootstrapMethod.getSamDescriptor();
+    JVMCodeBuilder cb = getSystemCodeBuilder(null, miDirectCall);
+    
+    String calleeName = miCallee.getName();
+    String calleeSig = miCallee.getSignature();
+    
+    ClassInfo callerCi = miDirectCall.getClassInfo();
+    
+    // loading free variables, which are used in the body of the lambda 
+    // expression and captured by the lexical scope. These variables  
+    // are stored by the fields of the synthetic function object class
+    int n = callerCi.getNumberOfInstanceFields();
+    for(int i=0; i<n; i++) {
+      cb.aload(0);
+      FieldInfo fi = callerCi.getInstanceField(i);
+      
+      cb.getfield(fi.getName(), callerCi.getName(), Types.getTypeSignature(fi.getSignature(), false));
+    }
+
+    // adding bytecode instructions to load input parameters of the lambda expression
+    n = miDirectCall.getArgumentsSize();
+    for(int i=1; i<n; i++) {
+      cb.aload(i);
+    }
+    
+    String calleeClass = miCallee.getClassName(); 
+    
+    // adding the bytecode instruction to invoke lambda method
+    switch (bootstrapMethod.getLambdaRefKind()) {
+    case ClassFile.REF_INVOKESTATIC:
+      cb.invokestatic(calleeClass, calleeName, calleeSig);
+      break;
+    case ClassFile.REF_INVOKEINTERFACE:
+      cb.invokeinterface(calleeClass, calleeName, calleeSig);
+      break;
+    case ClassFile.REF_INVOKEVIRTUAL:
+      cb.invokevirtual(calleeClass, calleeName, calleeSig);
+      break;
+    case ClassFile.REF_NEW_INVOKESPECIAL:
+      cb.new_(calleeClass);
+      cb.invokespecial(calleeClass, calleeName, calleeSig);
+      break;
+    case ClassFile.REF_INVOKESPECIAL:
+      cb.invokespecial(calleeClass, calleeName, calleeSig);
+      break;
+    }
+    
+    String returnType = Types.getReturnTypeSignature(samSignature);
+    int  len = returnType.length();
+    char c = returnType.charAt(0);
+
+    // adding a return statement for function object method
+    if (len == 1) {
+      switch (c) {
+      case 'B':
+      case 'I':
+      case 'C':
+      case 'Z':
+      case 'S':
+        cb.ireturn();
+        break;
+      case 'D':
+        cb.dreturn();
+        break;
+      case 'J':
+        cb.lreturn();
+        break;
+      case 'F':
+        cb.freturn();
+        break;
+      case 'V':
+        cb.return_();
+        break;
+      }
+    } else {
+      cb.areturn();
+    }
+    
+    cb.installCode();
+  }
+  
+  // create a stack frame that has properly initialized arguments
+  @Override
+  public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
+    
+    if (callee.isMJI()){
+      NativeMethodInfo nativeCallee = (NativeMethodInfo) callee;
+      JVMNativeStackFrame calleeFrame = new JVMNativeStackFrame( nativeCallee);
+      calleeFrame.setArguments( ti);
+      return calleeFrame; 
+      
+    } else {
+      JVMStackFrame calleeFrame = new JVMStackFrame( callee);
+      calleeFrame.setCallArguments( ti);
+      return calleeFrame;      
+    }
+  }
+  
+  @Override
+  public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo miCallee, int nLocals){
+    int nOperands = miCallee.getNumberOfCallerStackSlots();
+    
+    MethodInfo miDirect = new MethodInfo(miCallee, nLocals, nOperands);
+    setDirectCallCode( miDirect, miCallee);
+    
+    return new JVMDirectCallStackFrame( miDirect, miCallee);
+  }
+  
+  /**
+   * while this is a normal DirectCallStackFrame, it has different code which has to be created here 
+   */
+  @Override
+  public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
+    MethodInfo miDirect = new MethodInfo( miRun, 0, 1);
+    setRunStartCode( miDirect, miRun);
+    
+    return new JVMDirectCallStackFrame( miDirect, miRun);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java b/src/main/gov/nasa/jpf/jvm/JVMCodeBuilder.java
new file mode 100644 (file)
index 0000000..e10bc2b
--- /dev/null
@@ -0,0 +1,1326 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.Invocation;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.bytecode.LookupSwitchInstruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativeMethodInfo;
+import gov.nasa.jpf.vm.bytecode.TableSwitchInstruction;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * a special JVMByteCodeReader implementation that builds code arrays for
+ * MethodInfos, setting index and pc on the fly
+ */
+public class JVMCodeBuilder implements JVMByteCodeReader {
+
+  protected JVMInstructionFactory insnFactory;
+  
+  protected ClassFile cf;
+  protected MethodInfo mi;
+
+  // have to cache these to set switch entries
+  // <2do> these should use interface types to avoid hardwiring our own instruction classes
+  protected TableSwitchInstruction tableswitchInsn;
+  protected LookupSwitchInstruction lookupswitchInsn;
+
+  protected ArrayList<Instruction> code;
+
+  protected int pc; // bytecode position within method code
+  protected int idx; // instruction index within MethodInfo
+
+  // flag to remember wide immediate operand modification
+  boolean isWide;
+  
+  //--- for testing purposes
+  protected JVMCodeBuilder (JVMInstructionFactory ifact){
+    this.code = new ArrayList<Instruction>(64);
+    this.insnFactory = ifact;
+  }
+
+  
+  
+  // this is dangerous - it enables reuse of CodeBuilders, but
+  // you better make sure this does not get recursive or is used concurrently
+  public void reset (ClassFile classFile, MethodInfo targetMethod){
+    this.cf = classFile;
+    this.mi = targetMethod;
+
+    pc = 0;
+    idx = 0;
+    isWide = false;
+
+    tableswitchInsn = null;
+    lookupswitchInsn = null;
+
+    code.clear();
+  }
+  
+  protected void add(Instruction insn){
+    insn.setMethodInfo(mi);
+    insn.setLocation(idx++, pc);
+    code.add(insn);
+  }
+
+  public void installCode(){
+    Instruction[] a = code.toArray( new Instruction[code.size()]);
+    mi.setCode(a);
+  }
+
+  public int getCodeSize(){
+    return code.size();
+  }
+
+  //--- the factory methods
+
+  @Override public void aconst_null() {
+    add( insnFactory.aconst_null());
+    pc++;
+  }
+
+  @Override public void aload(int localVarIndex) {
+    add( insnFactory.aload(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void aload_0() {
+    add( insnFactory.aload(0));
+    pc++;
+  }
+
+  @Override public void aload_1() {
+    add( insnFactory.aload(1));
+    pc++;
+  }
+
+  @Override public void aload_2() {
+    add( insnFactory.aload(2));
+    pc++;
+  }
+
+  @Override public void aload_3() {
+    add( insnFactory.aload(3));
+    pc++;
+  }
+
+  @Override public void aaload() {
+    add( insnFactory.aaload());
+    pc++;
+  }
+
+  @Override public void astore(int localVarIndex) {
+    add( insnFactory.astore(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void astore_0() {
+    add( insnFactory.astore(0));
+    pc++;
+  }
+
+  @Override public void astore_1() {
+    add( insnFactory.astore(1));
+    pc++;
+  }
+
+  @Override public void astore_2() {
+    add( insnFactory.astore(2));
+    pc++;
+  }
+
+  @Override public void astore_3() {
+    add( insnFactory.astore(3));
+    pc++;
+  }
+
+  @Override public void aastore() {
+    add( insnFactory.aastore());
+    pc++;
+  }
+
+  @Override public void areturn() {
+    add( insnFactory.areturn());
+    pc++;
+  }
+
+  @Override public void anewarray(int cpClassIndex) {
+    String clsName = cf.classNameAt(cpClassIndex);
+    add( insnFactory.anewarray(clsName));
+    pc+=3;
+  }
+
+  @Override public void arraylength() {
+    add( insnFactory.arraylength());
+    pc++;
+  }
+
+  @Override public void athrow() {
+    add( insnFactory.athrow());
+    pc++;
+  }
+
+  @Override public void baload() {
+    add( insnFactory.baload());
+    pc++;
+  }
+
+  @Override public void bastore() {
+    add( insnFactory.bastore());
+    pc++;
+  }
+
+  @Override public void bipush(int b) {
+    add( insnFactory.bipush(b));
+    pc+=2;
+  }
+
+  @Override public void caload() {
+    add( insnFactory.caload());
+    pc++;
+  }
+
+  @Override public void castore() {
+    add( insnFactory.castore());
+    pc++;
+  }
+
+  @Override public void checkcast(int cpClassIndex) {
+    String clsName = cf.classNameAt(cpClassIndex);
+    add( insnFactory.checkcast(clsName));
+    pc+=3;
+  }
+
+  @Override public void d2f() {
+    add( insnFactory.d2f());
+    pc++;
+  }
+
+  @Override public void d2i() {
+    add( insnFactory.d2i());
+    pc++;
+  }
+
+  @Override public void d2l() {
+    add( insnFactory.d2l());
+    pc++;
+  }
+
+  @Override public void dadd() {
+    add( insnFactory.dadd());
+    pc++;
+  }
+
+  @Override public void daload() {
+    add( insnFactory.daload());
+    pc++;
+  }
+
+  @Override public void dastore() {
+    add( insnFactory.dastore());
+    pc++;
+  }
+
+  @Override public void dcmpg() {
+    add( insnFactory.dcmpg());
+    pc++;
+  }
+
+  @Override public void dcmpl() {
+    add( insnFactory.dcmpl());
+    pc++;
+  }
+
+  @Override public void dconst_0() {
+    add( insnFactory.dconst_0());
+    pc++;
+  }
+
+  @Override public void dconst_1() {
+    add( insnFactory.dconst_1());
+    pc++;
+  }
+
+  @Override public void ddiv() {
+    add( insnFactory.ddiv());
+    pc++;
+  }
+
+  @Override public void dload(int localVarIndex) {
+    add( insnFactory.dload(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void dload_0() {
+    add( insnFactory.dload(0));
+    pc++;
+  }
+
+  @Override public void dload_1() {
+    add( insnFactory.dload(1));
+    pc++;
+  }
+
+  @Override public void dload_2() {
+    add( insnFactory.dload(2));
+    pc++;
+  }
+
+  @Override public void dload_3() {
+    add( insnFactory.dload(3));
+    pc++;
+  }
+
+  @Override public void dmul() {
+    add( insnFactory.dmul());
+    pc++;
+  }
+
+  @Override public void dneg() {
+    add( insnFactory.dneg());
+    pc++;
+  }
+
+  @Override public void drem() {
+    add( insnFactory.drem());
+    pc++;
+  }
+
+  @Override public void dreturn() {
+    add( insnFactory.dreturn());
+    pc++;
+  }
+
+  @Override public void dstore(int localVarIndex) {
+    add( insnFactory.dstore(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void dstore_0() {
+    add( insnFactory.dstore(0));
+    pc++;
+  }
+
+  @Override public void dstore_1() {
+    add( insnFactory.dstore(1));
+    pc++;
+  }
+
+  @Override public void dstore_2() {
+    add( insnFactory.dstore(2));
+    pc++;
+  }
+
+  @Override public void dstore_3() {
+    add( insnFactory.dstore(3));
+    pc++;
+  }
+
+  @Override public void dsub() {
+    add( insnFactory.dsub());
+    pc++;
+  }
+
+  @Override public void dup() {
+    add( insnFactory.dup());
+    pc++;
+  }
+
+  @Override public void dup_x1() {
+    add( insnFactory.dup_x1());
+    pc++;
+  }
+
+  @Override public void dup_x2() {
+    add( insnFactory.dup_x2());
+    pc++;
+  }
+
+  @Override public void dup2() {
+    add( insnFactory.dup2());
+    pc++;
+  }
+
+  @Override public void dup2_x1() {
+    add( insnFactory.dup2_x1());
+    pc++;
+  }
+
+  @Override public void dup2_x2() {
+    add( insnFactory.dup2_x2());
+    pc++;
+  }
+
+  @Override public void f2d() {
+    add( insnFactory.f2d());
+    pc++;
+  }
+
+  @Override public void f2i() {
+    add( insnFactory.f2i());
+    pc++;
+  }
+
+  @Override public void f2l() {
+    add( insnFactory.f2l());
+    pc++;
+  }
+
+  @Override public void fadd() {
+    add( insnFactory.fadd());
+    pc++;
+  }
+
+  @Override public void faload() {
+    add( insnFactory.faload());
+    pc++;
+  }
+
+  @Override public void fastore() {
+    add( insnFactory.fastore());
+    pc++;
+  }
+
+  @Override public void fcmpg() {
+    add( insnFactory.fcmpg());
+    pc++;
+  }
+
+  @Override public void fcmpl() {
+    add( insnFactory.fcmpl());
+    pc++;
+  }
+
+  @Override public void fconst_0() {
+    add( insnFactory.fconst_0());
+    pc++;
+  }
+
+  @Override public void fconst_1() {
+    add( insnFactory.fconst_1());
+    pc++;
+  }
+
+  @Override public void fconst_2() {
+    add( insnFactory.fconst_2());
+    pc++;
+  }
+
+  @Override public void fdiv() {
+    add( insnFactory.fdiv());
+    pc++;
+  }
+
+  @Override public void fload(int localVarIndex) {
+    add( insnFactory.fload(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void fload_0() {
+    add( insnFactory.fload(0));
+    pc++;
+  }
+
+  @Override public void fload_1() {
+    add( insnFactory.fload(1));
+    pc++;
+  }
+
+  @Override public void fload_2() {
+    add( insnFactory.fload(2));
+    pc++;
+  }
+
+  @Override public void fload_3() {
+    add( insnFactory.fload(3));
+    pc++;
+  }
+
+  @Override public void fmul() {
+    add( insnFactory.fmul());
+    pc++;
+  }
+
+  @Override public void fneg() {
+    add( insnFactory.fneg());
+    pc++;
+  }
+
+  @Override public void frem() {
+    add( insnFactory.frem());
+    pc++;
+  }
+
+  @Override public void freturn() {
+    add( insnFactory.freturn());
+    pc++;
+  }
+
+  @Override public void fstore(int localVarIndex) {
+    add( insnFactory.fstore(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void fstore_0() {
+    add( insnFactory.fstore(0));
+    pc++;
+  }
+
+  @Override public void fstore_1() {
+    add( insnFactory.fstore(1));
+    pc++;
+  }
+
+  @Override public void fstore_2() {
+    add( insnFactory.fstore(2));
+    pc++;
+  }
+
+  @Override public void fstore_3() {
+    add( insnFactory.fstore(3));
+    pc++;
+  }
+
+  @Override public void fsub() {
+    add( insnFactory.fsub());
+    pc++;
+  }
+
+  @Override public void getfield(int cpFieldRefIndex) {
+    String fieldName = cf.fieldNameAt(cpFieldRefIndex);
+    String clsName = cf.fieldClassNameAt(cpFieldRefIndex);
+    String fieldDescriptor = cf.fieldDescriptorAt(cpFieldRefIndex);
+
+    add( insnFactory.getfield(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+  public void getfield(String fieldName, String clsName, String fieldDescriptor){
+    add( insnFactory.getfield(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+
+  @Override public void getstatic(int cpFieldRefIndex) {
+    String fieldName = cf.fieldNameAt(cpFieldRefIndex);
+    String clsName = cf.fieldClassNameAt(cpFieldRefIndex);
+    String fieldDescriptor = cf.fieldDescriptorAt(cpFieldRefIndex);
+
+    add( insnFactory.getstatic(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+  public void getstatic(String fieldName, String clsName, String fieldDescriptor){
+    add( insnFactory.getstatic(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+
+  @Override public void goto_(int pcOffset) {
+    add( insnFactory.goto_(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void goto_w(int pcOffset) {
+    add( insnFactory.goto_w(pc + pcOffset));
+    pc+=5;
+  }
+
+  @Override public void i2b() {
+    add( insnFactory.i2b());
+    pc++;
+  }
+
+  @Override public void i2c() {
+    add( insnFactory.i2c());
+    pc++;
+  }
+
+  @Override public void i2d() {
+    add( insnFactory.i2d());
+    pc++;
+  }
+
+  @Override public void i2f() {
+    add( insnFactory.i2f());
+    pc++;
+  }
+
+  @Override public void i2l() {
+    add( insnFactory.i2l());
+    pc++;
+  }
+
+  @Override public void i2s() {
+    add( insnFactory.i2s());
+    pc++;
+  }
+
+  @Override public void iadd() {
+    add( insnFactory.iadd());
+    pc++;
+  }
+
+  @Override public void iaload() {
+    add( insnFactory.iaload());
+    pc++;
+  }
+
+  @Override public void iand() {
+    add( insnFactory.iand());
+    pc++;
+  }
+
+  @Override public void iastore() {
+    add( insnFactory.iastore());
+    pc++;
+  }
+
+  @Override public void iconst_m1() {
+    add( insnFactory.iconst_m1());
+    pc++;
+  }
+
+  @Override public void iconst_0() {
+    add( insnFactory.iconst_0());
+    pc++;
+  }
+
+  @Override public void iconst_1() {
+    add( insnFactory.iconst_1());
+    pc++;
+  }
+
+  @Override public void iconst_2() {
+    add( insnFactory.iconst_2());
+    pc++;
+  }
+
+  @Override public void iconst_3() {
+    add( insnFactory.iconst_3());
+    pc++;
+  }
+
+  @Override public void iconst_4() {
+    add( insnFactory.iconst_4());
+    pc++;
+  }
+
+  @Override public void iconst_5() {
+    add( insnFactory.iconst_5());
+    pc++;
+  }
+
+  @Override public void idiv() {
+    add( insnFactory.idiv());
+    pc++;
+  }
+
+  @Override public void if_acmpeq(int pcOffset) {
+    add( insnFactory.if_acmpeq(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_acmpne(int pcOffset) {
+    add( insnFactory.if_acmpne(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmpeq(int pcOffset) {
+    add( insnFactory.if_icmpeq(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmpne(int pcOffset) {
+    add( insnFactory.if_icmpne(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmplt(int pcOffset) {
+    add( insnFactory.if_icmplt(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmpge(int pcOffset) {
+    add( insnFactory.if_icmpge(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmpgt(int pcOffset) {
+    add( insnFactory.if_icmpgt(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void if_icmple(int pcOffset) {
+    add( insnFactory.if_icmple(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifeq(int pcOffset) {
+    add( insnFactory.ifeq(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifne(int pcOffset) {
+    add( insnFactory.ifne(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void iflt(int pcOffset) {
+    add( insnFactory.iflt(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifge(int pcOffset) {
+    add( insnFactory.ifge(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifgt(int pcOffset) {
+    add( insnFactory.ifgt(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifle(int pcOffset) {
+    add( insnFactory.ifle(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifnonnull(int pcOffset) {
+    add( insnFactory.ifnonnull(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void ifnull(int pcOffset) {
+    add( insnFactory.ifnull(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void iinc(int localVarIndex, int incConstant) {
+    add( insnFactory.iinc(localVarIndex, incConstant));
+    pc+=3;
+    if (isWide){
+      pc+=2;
+      isWide = false;
+    }
+  }
+
+  @Override public void iload(int localVarIndex) {
+    add( insnFactory.iload(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void iload_0() {
+    add( insnFactory.iload(0));
+    pc++;
+  }
+
+  @Override public void iload_1() {
+    add( insnFactory.iload(1));
+    pc++;
+  }
+
+  @Override public void iload_2() {
+    add( insnFactory.iload(2));
+    pc++;
+  }
+
+  @Override public void iload_3() {
+    add( insnFactory.iload(3));
+    pc++;
+  }
+
+  @Override public void imul() {
+    add( insnFactory.imul());
+    pc++;
+  }
+
+  @Override public void ineg() {
+    add( insnFactory.ineg());
+    pc++;
+  }
+
+  @Override public void instanceof_(int cpClassIndex) {
+    String clsName = cf.classNameAt(cpClassIndex);
+    add( insnFactory.instanceof_(clsName));
+    pc+=3;
+  }
+
+  @Override public void invokeinterface(int cpInterfaceMethodRefIndex, int count, int zero) {
+    String clsName = cf.interfaceMethodClassNameAt(cpInterfaceMethodRefIndex);
+    String methodName = cf.interfaceMethodNameAt(cpInterfaceMethodRefIndex);
+    String methodSignature = cf.interfaceMethodDescriptorAt(cpInterfaceMethodRefIndex);
+
+    add( insnFactory.invokeinterface(clsName, methodName, methodSignature));
+    pc+=5;
+  }
+  public void invokeinterface(String clsName, String methodName, String methodSignature){
+    add( insnFactory.invokeinterface(clsName, methodName, methodSignature));
+    pc+=5;
+  }
+
+  @Override
+  public void invokedynamic (int cpInvokeDynamicIndex){
+    int bootstrapMethodIndex = cf.bootstrapMethodIndex(cpInvokeDynamicIndex);
+    String samMethodName = cf.samMethodNameAt(cpInvokeDynamicIndex);
+    String callSiteDescriptor = cf.callSiteDescriptor(cpInvokeDynamicIndex);
+    add( insnFactory.invokedynamic(bootstrapMethodIndex, samMethodName, callSiteDescriptor));
+    pc+=5;
+  }
+  
+  @Override public void invokespecial(int cpMethodRefIndex) {
+    String clsName = cf.methodClassNameAt(cpMethodRefIndex);
+    String methodName = cf.methodNameAt(cpMethodRefIndex);
+    String methodSignature = cf.methodDescriptorAt(cpMethodRefIndex);
+
+    add( insnFactory.invokespecial(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+  public void invokespecial(String clsName, String methodName, String methodSignature){
+    add( insnFactory.invokespecial(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+
+  @Override public void invokestatic(int cpMethodRefIndex) {
+    String clsName = cf.methodClassNameAt(cpMethodRefIndex);
+    String methodName = cf.methodNameAt(cpMethodRefIndex);
+    String methodSignature = cf.methodDescriptorAt(cpMethodRefIndex);
+
+    add( insnFactory.invokestatic(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+  public void invokestatic(String clsName, String methodName, String methodSignature){
+    add( insnFactory.invokestatic(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+
+  @Override public void invokevirtual(int cpMethodRefIndex) {
+    String clsName = cf.methodClassNameAt(cpMethodRefIndex);
+    String methodName = cf.methodNameAt(cpMethodRefIndex);
+    String methodSignature = cf.methodDescriptorAt(cpMethodRefIndex);
+
+    add( insnFactory.invokevirtual(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+  public void invokevirtual(String clsName, String methodName, String methodSignature){
+    add( insnFactory.invokevirtual(clsName, methodName, methodSignature));
+    pc+=3;
+  }
+
+  @Override public void ior() {
+    add( insnFactory.ior());
+    pc++;
+  }
+
+  @Override public void irem() {
+    add( insnFactory.irem());
+    pc++;
+  }
+
+  @Override public void ireturn() {
+    add( insnFactory.ireturn());
+    pc++;
+  }
+
+  @Override public void ishl() {
+    add( insnFactory.ishl());
+    pc++;
+  }
+
+  @Override public void ishr() {
+    add( insnFactory.ishr());
+    pc++;
+  }
+
+  @Override public void istore(int localVarIndex) {
+    add( insnFactory.istore(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void istore_0() {
+    add( insnFactory.istore(0));
+    pc++;
+  }
+
+  @Override public void istore_1() {
+    add( insnFactory.istore(1));
+    pc++;
+  }
+
+  @Override public void istore_2() {
+    add( insnFactory.istore(2));
+    pc++;
+  }
+
+  @Override public void istore_3() {
+    add( insnFactory.istore(3));
+    pc++;
+  }
+
+  @Override public void isub() {
+    add( insnFactory.isub());
+    pc++;
+  }
+
+  @Override public void iushr() {
+    add( insnFactory.iushr());
+    pc++;
+  }
+
+  @Override public void ixor() {
+    add( insnFactory.ixor());
+    pc++;
+  }
+
+  @Override public void jsr(int pcOffset) {
+    add( insnFactory.jsr(pc + pcOffset));
+    pc+=3;
+  }
+
+  @Override public void jsr_w(int pcOffset) {
+    add( insnFactory.jsr_w(pc + pcOffset));
+    pc+=5;
+  }
+
+  @Override public void l2d() {
+    add( insnFactory.l2d());
+    pc++;
+  }
+
+  @Override public void l2f() {
+    add( insnFactory.l2f());
+    pc++;
+  }
+
+  @Override public void l2i() {
+    add( insnFactory.l2i());
+    pc++;
+  }
+
+  @Override public void ladd() {
+    add( insnFactory.ladd());
+    pc++;
+  }
+
+  @Override public void laload() {
+    add( insnFactory.laload());
+    pc++;
+  }
+
+  @Override public void land() {
+    add( insnFactory.land());
+    pc++;
+  }
+
+  @Override public void lastore() {
+    add( insnFactory.lastore());
+    pc++;
+  }
+
+  @Override public void lcmp() {
+    add( insnFactory.lcmp());
+    pc++;
+  }
+
+  @Override public void lconst_0() {
+    add( insnFactory.lconst_0());
+    pc++;
+  }
+
+  @Override public void lconst_1() {
+    add( insnFactory.lconst_1());
+    pc++;
+  }
+
+  @Override public void ldc_(int cpIntOrFloatOrStringOrClassIndex) {
+    Object v = cf.getCpValue(cpIntOrFloatOrStringOrClassIndex);
+    switch (cf.getCpTag(cpIntOrFloatOrStringOrClassIndex)){
+      case ClassFile.CONSTANT_INTEGER:
+        add( insnFactory.ldc((Integer)v)); break;
+      case ClassFile.CONSTANT_FLOAT:
+        add( insnFactory.ldc((Float)v)); break;
+      case ClassFile.CONSTANT_STRING:
+        add( insnFactory.ldc((String)v, false)); break;
+      case ClassFile.CONSTANT_CLASS:
+        add( insnFactory.ldc((String)v, true)); break;
+    }
+    pc+=2;
+  }
+
+  @Override public void ldc_w_(int cpIntOrFloatOrStringOrClassIndex) {
+    Object v = cf.getCpValue(cpIntOrFloatOrStringOrClassIndex);
+    switch (cf.getCpTag(cpIntOrFloatOrStringOrClassIndex)){
+      case ClassFile.CONSTANT_INTEGER:
+        add( insnFactory.ldc_w((Integer) v)); break;
+      case ClassFile.CONSTANT_FLOAT:
+        add( insnFactory.ldc_w((Float) v)); break;
+      case ClassFile.CONSTANT_STRING:
+        add( insnFactory.ldc_w((String) v, false)); break;
+      case ClassFile.CONSTANT_CLASS:
+        add( insnFactory.ldc_w((String) v, true)); break;
+    }
+    pc+=3;
+  }
+
+  @Override public void ldc2_w(int cpLongOrDoubleIndex) {
+    Object v = cf.getCpValue(cpLongOrDoubleIndex);
+    if (v instanceof Long){
+      add( insnFactory.ldc2_w((Long)v));
+    } else {
+      add( insnFactory.ldc2_w((Double)v));
+    }
+    pc+=3;
+  }
+
+  @Override public void ldiv() {
+    add( insnFactory.ldiv());
+    pc++;
+  }
+
+  @Override public void lload(int localVarIndex) {
+    add( insnFactory.lload(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void lload_0() {
+    add( insnFactory.lload(0));
+    pc++;
+  }
+
+  @Override public void lload_1() {
+    add( insnFactory.lload(1));
+    pc++;
+  }
+
+  @Override public void lload_2() {
+    add( insnFactory.lload(2));
+    pc++;
+  }
+
+  @Override public void lload_3() {
+    add( insnFactory.lload(3));
+    pc++;
+  }
+
+  @Override public void lmul() {
+    add( insnFactory.lmul());
+    pc++;
+  }
+
+  @Override public void lneg() {
+    add( insnFactory.lneg());
+    pc++;
+  }
+
+
+  @Override public void lookupswitch(int defaultPcOffset, int nEntries) {
+    Instruction insn = insnFactory.lookupswitch(pc + defaultPcOffset, nEntries);
+    add( insn);
+
+    lookupswitchInsn = (LookupSwitchInstruction)insn;
+
+    if (cf != null){
+      cf.parseLookupSwitchEntries(this, nEntries);
+    }
+
+    pc = ((pc+4)>>2)<<2; // opcode and padding
+    pc += 8 + nEntries*8; // arguments and lookup table
+  }
+  @Override public void lookupswitchEntry(int index, int match, int pcOffset) {
+    lookupswitchInsn.setTarget(index, match, pc + pcOffset);
+  }
+
+  @Override public void lor() {
+    add( insnFactory.lor());
+    pc++;
+  }
+
+  @Override public void lrem() {
+    add( insnFactory.lrem());
+    pc++;
+  }
+
+  @Override public void lreturn() {
+    add( insnFactory.lreturn());
+    pc++;
+  }
+
+  @Override public void lshl() {
+    add( insnFactory.lshl());
+    pc++;
+  }
+
+  @Override public void lshr() {
+    add( insnFactory.lshr());
+    pc++;
+  }
+
+  @Override public void lstore(int localVarIndex) {
+    add( insnFactory.lstore(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void lstore_0() {
+    add( insnFactory.lstore(0));
+    pc++;
+  }
+
+  @Override public void lstore_1() {
+    add( insnFactory.lstore(1));
+    pc++;
+  }
+
+  @Override public void lstore_2() {
+    add( insnFactory.lstore(2));
+    pc++;
+  }
+
+  @Override public void lstore_3() {
+    add( insnFactory.lstore(3));
+    pc++;
+  }
+
+  @Override public void lsub() {
+    add( insnFactory.lsub());
+    pc++;
+  }
+
+  @Override public void lushr() {
+    add( insnFactory.lushr());
+    pc++;
+  }
+
+  @Override public void lxor() {
+    add( insnFactory.lxor());
+    pc++;
+  }
+
+  @Override public void monitorenter() {
+    add( insnFactory.monitorenter());
+    pc++;
+  }
+
+  @Override public void monitorexit() {
+    add( insnFactory.monitorexit());
+    pc++;
+  }
+
+  @Override public void multianewarray(int cpClassIndex, int dimensions) {
+    add( insnFactory.multianewarray(cf.classNameAt(cpClassIndex), dimensions));
+    pc+=4;
+  }
+
+  @Override public void new_(int cpClassIndex) {
+    add( insnFactory.new_(cf.classNameAt(cpClassIndex)));
+    pc+=3;
+  }
+  public void new_(String className) {
+    add( insnFactory.new_(className));
+    pc+=3;
+  }
+
+  @Override public void newarray(int typeCode) {
+    add( insnFactory.newarray(typeCode));
+    pc+=2;
+  }
+
+  @Override public void nop() {
+    add( insnFactory.nop());
+    pc++;
+  }
+
+  @Override public void pop() {
+    add( insnFactory.pop());
+    pc++;
+  }
+
+  @Override public void pop2() {
+    add( insnFactory.pop2());
+    pc++;
+  }
+
+  @Override public void putfield(int cpFieldRefIndex) {
+    String fieldName = cf.fieldNameAt(cpFieldRefIndex);
+    String clsName = cf.fieldClassNameAt(cpFieldRefIndex);
+    String fieldDescriptor = cf.fieldDescriptorAt(cpFieldRefIndex);
+
+    add( insnFactory.putfield(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+  public void putfield(String fieldName, String clsName, String fieldDescriptor){
+    add( insnFactory.putfield(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+
+
+  @Override public void putstatic(int cpFieldRefIndex) {
+    String fieldName = cf.fieldNameAt(cpFieldRefIndex);
+    String clsName = cf.fieldClassNameAt(cpFieldRefIndex);
+    String fieldDescriptor = cf.fieldDescriptorAt(cpFieldRefIndex);
+
+    add( insnFactory.putstatic(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+  public void putstatic(String fieldName, String clsName, String fieldDescriptor){
+    add( insnFactory.putstatic(fieldName, clsName, fieldDescriptor));
+    pc+=3;
+  }
+
+
+  @Override public void ret(int localVarIndex) {
+    add( insnFactory.ret(localVarIndex));
+    pc+=2;
+    if (isWide){
+      pc++;
+      isWide = false;
+    }
+  }
+
+  @Override public void return_() {
+    add( insnFactory.return_());
+    pc++;
+  }
+
+  @Override public void saload() {
+    add( insnFactory.saload());
+    pc++;
+  }
+
+  @Override public void sastore() {
+    add( insnFactory.sastore());
+    pc++;
+  }
+
+  @Override public void sipush(int val) {
+    add( insnFactory.sipush(val));
+    pc+=3;
+  }
+
+  @Override public void swap() {
+    add( insnFactory.swap());
+    pc++;
+  }
+
+  @Override public void tableswitch(int defaultPcOffset, int low, int high) {
+    Instruction insn = insnFactory.tableswitch(pc + defaultPcOffset, low, high);
+    add( insn);
+    
+    tableswitchInsn = (TableSwitchInstruction)insn;
+
+    if (cf != null){
+      cf.parseTableSwitchEntries(this, low, high);
+    }
+
+    pc = ((pc+4)>>2)<<2; // opcode and padding
+    pc+=12 + (high-low+1)*4; // the fixed args and jump table
+  }
+
+  @Override public void tableswitchEntry(int value, int pcOffset) {
+    tableswitchInsn.setTarget(value, pc + pcOffset);
+  }
+
+  @Override public void wide() {
+    add( insnFactory.wide());
+    pc++;
+    isWide = true;
+  }
+
+  @Override public void unknown(int bytecode) {
+    throw new JPFException("unknown bytecode: " + Integer.toHexString(bytecode));
+  }
+
+
+  //--- the JPF specific ones (only used in synthetic methods)
+  public void invokecg(List<Invocation> invokes) {
+    add (insnFactory.invokecg(invokes));
+    pc++;
+  }
+
+  public void invokeclinit(ClassInfo ci) {
+    add( insnFactory.invokeclinit(ci));
+    pc++;
+  }
+
+  public void finishclinit (ClassInfo ci){
+    add (insnFactory.finishclinit(ci));
+    pc++;
+  }
+
+  public void directcallreturn(){
+    add( insnFactory.directcallreturn());
+    pc++;
+  }
+
+  public void executenative(NativeMethodInfo mi){
+    add( insnFactory.executenative(mi));
+    pc++;
+  }
+
+  public void nativereturn(){
+    add( insnFactory.nativereturn());
+    pc++;
+  }
+
+  public void runStart (MethodInfo mi){
+    add( insnFactory.runstart(mi));
+    pc++;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMDirectCallStackFrame.java b/src/main/gov/nasa/jpf/jvm/JVMDirectCallStackFrame.java
new file mode 100644 (file)
index 0000000..6a27dba
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.vm.DirectCallStackFrame;
+import gov.nasa.jpf.vm.MethodInfo;
+
+/**
+ * a direct call stackframe that supports JVM calling conventions
+ */
+public class JVMDirectCallStackFrame extends DirectCallStackFrame {
+  
+  JVMDirectCallStackFrame (MethodInfo miDirectCall, MethodInfo callee){
+    super( miDirectCall, callee);
+  }
+
+  //--- return value handling
+  
+  @Override
+  public int getResult(){
+    return pop();
+  }
+  
+  @Override
+  public int getReferenceResult(){
+    return pop();
+  }
+  
+  @Override
+  public long getLongResult(){
+    return popLong();
+  }
+
+  @Override
+  public Object getResultAttr(){
+    return getOperandAttr();
+  }
+  
+  @Override
+  public Object getLongResultAttr(){
+    return getLongOperandAttr();
+  }
+
+  @Override
+  public void setExceptionReference (int exRef){
+    clearOperandStack();
+    pushRef( exRef);
+  }
+  
+  @Override
+  public int getExceptionReference(){
+    return pop();
+  }
+
+  @Override
+  public void setExceptionReferenceAttribute (Object attr){
+    setOperandAttr(attr);
+  }
+  
+  @Override
+  public Object getExceptionReferenceAttribute (){
+    return getOperandAttr();
+  }
+  
+  //--- direct call argument initialization
+  // NOTE - we don't support out-of-order arguments yet, i.e. the slotIdx is ignored
+  
+  
+  @Override
+  public int setArgument (int slotIdx, int v, Object attr){
+    push(v);
+    if (attr != null){
+      setOperandAttr(attr);
+    }
+    
+    return slotIdx+1;
+  }
+  
+  @Override
+  public int setReferenceArgument (int slotIdx, int ref, Object attr){
+    pushRef(ref);
+    if (attr != null){
+      setOperandAttr(attr);
+    }
+    
+    return slotIdx+1;
+  }
+  
+  @Override
+  public int setLongArgument (int slotIdx, long v, Object attr){
+    pushLong(v);
+    if (attr != null){
+      setLongOperandAttr(attr);
+    } 
+    
+    return slotIdx+2;
+  }
+  
+  //--- DirectCallStackFrame methods don't have arguments
+  
+  @Override
+  public void setArgumentLocal (int argIdx, int v, Object attr){
+    throw new UnsupportedOperationException("direct call methods don't have arguments");
+  }
+  @Override
+  public void setReferenceArgumentLocal (int argIdx, int v, Object attr){
+    throw new UnsupportedOperationException("direct call methods don't have arguments");
+  }
+  @Override
+  public void setLongArgumentLocal (int argIdx, long v, Object attr){
+    throw new UnsupportedOperationException("direct call methods don't have arguments");
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMInstructionFactory.java b/src/main/gov/nasa/jpf/jvm/JVMInstructionFactory.java
new file mode 100644 (file)
index 0000000..7711b9c
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.Invocation;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativeMethodInfo;
+
+import java.util.List;
+
+/**
+ * interface for bytecode creation
+ *
+ * this deliberately uses the abstract abstract public Instruction as return type to allow different instruction hierarchies in
+ * extensions.
+ *
+ * This shouldn't impose runtime overhead since mandatory parameters are now passed in as factory method arguments. The only
+ * drawback is that the compiler cannot check for abstract public Instruction class typos, but that seems less important than
+ * allowing extension specific abstract public Instruction class hierarchies
+ *
+ * <2do> there are still direct references of LOOKUPSWITCH, TABLESWITCH. Once these are removed, .jvm does not assume a particular
+ * abstract public Instruction hierarchy
+ */
+public abstract class JVMInstructionFactory implements Cloneable {
+
+  protected static JVMInstructionFactory singleton;
+  
+  public static JVMInstructionFactory getFactory(){
+    return singleton;
+  }
+  
+  protected JVMInstructionFactory(){
+    // we should check the singleton first
+    singleton = this;
+  }
+  
+
+  //--- the factory methods
+  abstract public Instruction aconst_null ();
+
+  abstract public Instruction aload (int localVarIndex);
+
+  abstract public Instruction aload_0 ();
+
+  abstract public Instruction aload_1 ();
+
+  abstract public Instruction aload_2 ();
+
+  abstract public Instruction aload_3 ();
+
+  abstract public Instruction aaload ();
+
+  abstract public Instruction astore (int localVarIndex);
+
+  abstract public Instruction astore_0 ();
+
+  abstract public Instruction astore_1 ();
+
+  abstract public Instruction astore_2 ();
+
+  abstract public Instruction astore_3 ();
+
+  abstract public Instruction aastore ();
+
+  abstract public Instruction areturn ();
+
+  abstract public Instruction anewarray (String clsName);
+
+  abstract public Instruction arraylength ();
+
+  abstract public Instruction athrow ();
+
+  abstract public Instruction baload ();
+
+  abstract public Instruction bastore ();
+
+  abstract public Instruction bipush (int b);
+
+  abstract public Instruction caload ();
+
+  abstract public Instruction castore ();
+
+  abstract public Instruction checkcast (String clsName);
+
+  abstract public Instruction d2f ();
+
+  abstract public Instruction d2i ();
+
+  abstract public Instruction d2l ();
+
+  abstract public Instruction dadd ();
+
+  abstract public Instruction daload ();
+
+  abstract public Instruction dastore ();
+
+  abstract public Instruction dcmpg ();
+
+  abstract public Instruction dcmpl ();
+
+  abstract public Instruction dconst_0 ();
+
+  abstract public Instruction dconst_1 ();
+
+  abstract public Instruction ddiv ();
+
+  abstract public Instruction dload (int localVarIndex);
+
+  abstract public Instruction dload_0 ();
+
+  abstract public Instruction dload_1 ();
+
+  abstract public Instruction dload_2 ();
+
+  abstract public Instruction dload_3 ();
+
+  abstract public Instruction dmul ();
+
+  abstract public Instruction dneg ();
+
+  abstract public Instruction drem ();
+
+  abstract public Instruction dreturn ();
+
+  abstract public Instruction dstore (int localVarIndex);
+
+  abstract public Instruction dstore_0 ();
+
+  abstract public Instruction dstore_1 ();
+
+  abstract public Instruction dstore_2 ();
+
+  abstract public Instruction dstore_3 ();
+
+  abstract public Instruction dsub ();
+
+  abstract public Instruction dup ();
+
+  abstract public Instruction dup_x1 ();
+
+  abstract public Instruction dup_x2 ();
+
+  abstract public Instruction dup2 ();
+
+  abstract public Instruction dup2_x1 ();
+
+  abstract public Instruction dup2_x2 ();
+
+  abstract public Instruction f2d ();
+
+  abstract public Instruction f2i ();
+
+  abstract public Instruction f2l ();
+
+  abstract public Instruction fadd ();
+
+  abstract public Instruction faload ();
+
+  abstract public Instruction fastore ();
+
+  abstract public Instruction fcmpg ();
+
+  abstract public Instruction fcmpl ();
+
+  abstract public Instruction fconst_0 ();
+
+  abstract public Instruction fconst_1 ();
+
+  abstract public Instruction fconst_2 ();
+
+  abstract public Instruction fdiv ();
+
+  abstract public Instruction fload (int localVarIndex);
+
+  abstract public Instruction fload_0 ();
+
+  abstract public Instruction fload_1 ();
+
+  abstract public Instruction fload_2 ();
+
+  abstract public Instruction fload_3 ();
+
+  abstract public Instruction fmul ();
+
+  abstract public Instruction fneg ();
+
+  abstract public Instruction frem ();
+
+  abstract public Instruction freturn ();
+
+  abstract public Instruction fstore (int localVarIndex);
+
+  abstract public Instruction fstore_0 ();
+
+  abstract public Instruction fstore_1 ();
+
+  abstract public Instruction fstore_2 ();
+
+  abstract public Instruction fstore_3 ();
+
+  abstract public Instruction fsub ();
+
+  abstract public Instruction getfield (String fieldName, String clsName, String fieldDescriptor);
+
+  abstract public Instruction getstatic (String fieldName, String clsName, String fieldDescriptor);
+
+  abstract public Instruction goto_ (int targetPc);
+
+  abstract public Instruction goto_w (int targetPc);
+
+  abstract public Instruction i2b ();
+
+  abstract public Instruction i2c ();
+
+  abstract public Instruction i2d ();
+
+  abstract public Instruction i2f ();
+
+  abstract public Instruction i2l ();
+
+  abstract public Instruction i2s ();
+
+  abstract public Instruction iadd ();
+
+  abstract public Instruction iaload ();
+
+  abstract public Instruction iand ();
+
+  abstract public Instruction iastore ();
+
+  abstract public Instruction iconst_m1 ();
+
+  abstract public Instruction iconst_0 ();
+
+  abstract public Instruction iconst_1 ();
+
+  abstract public Instruction iconst_2 ();
+
+  abstract public Instruction iconst_3 ();
+
+  abstract public Instruction iconst_4 ();
+
+  abstract public Instruction iconst_5 ();
+
+  abstract public Instruction idiv ();
+
+  abstract public Instruction if_acmpeq (int targetPc);
+
+  abstract public Instruction if_acmpne (int targetPc);
+
+  abstract public Instruction if_icmpeq (int targetPc);
+
+  abstract public Instruction if_icmpne (int targetPc);
+
+  abstract public Instruction if_icmplt (int targetPc);
+
+  abstract public Instruction if_icmpge (int targetPc);
+
+  abstract public Instruction if_icmpgt (int targetPc);
+
+  abstract public Instruction if_icmple (int targetPc);
+
+  abstract public Instruction ifeq (int targetPc);
+
+  abstract public Instruction ifne (int targetPc);
+
+  abstract public Instruction iflt (int targetPc);
+
+  abstract public Instruction ifge (int targetPc);
+
+  abstract public Instruction ifgt (int targetPc);
+
+  abstract public Instruction ifle (int targetPc);
+
+  abstract public Instruction ifnonnull (int targetPc);
+
+  abstract public Instruction ifnull (int targetPc);
+
+  abstract public Instruction iinc (int localVarIndex, int incConstant);
+
+  abstract public Instruction iload (int localVarIndex);
+
+  abstract public Instruction iload_0 ();
+
+  abstract public Instruction iload_1 ();
+
+  abstract public Instruction iload_2 ();
+
+  abstract public Instruction iload_3 ();
+
+  abstract public Instruction imul ();
+
+  abstract public Instruction ineg ();
+
+  abstract public Instruction instanceof_ (String clsName);
+
+  abstract public Instruction invokeinterface (String clsName, String methodName, String methodSignature);
+
+  abstract public Instruction invokespecial (String clsName, String methodName, String methodSignature);
+
+  abstract public Instruction invokestatic (String clsName, String methodName, String methodSignature);
+
+  abstract public Instruction invokevirtual (String clsName, String methodName, String methodSignature);
+  
+  abstract public Instruction invokedynamic (int bootstrapIndex, String samMethodName, String functionType);
+
+  abstract public Instruction ior ();
+
+  abstract public Instruction irem ();
+
+  abstract public Instruction ireturn ();
+
+  abstract public Instruction ishl ();
+
+  abstract public Instruction ishr ();
+
+  abstract public Instruction istore (int localVarIndex);
+
+  abstract public Instruction istore_0 ();
+
+  abstract public Instruction istore_1 ();
+
+  abstract public Instruction istore_2 ();
+
+  abstract public Instruction istore_3 ();
+
+  abstract public Instruction isub ();
+
+  abstract public Instruction iushr ();
+
+  abstract public Instruction ixor ();
+
+  abstract public Instruction jsr (int targetPc);
+
+  abstract public Instruction jsr_w (int targetPc);
+
+  abstract public Instruction l2d ();
+
+  abstract public Instruction l2f ();
+
+  abstract public Instruction l2i ();
+
+  abstract public Instruction ladd ();
+
+  abstract public Instruction laload ();
+
+  abstract public Instruction land ();
+
+  abstract public Instruction lastore ();
+
+  abstract public Instruction lcmp ();
+
+  abstract public Instruction lconst_0 ();
+
+  abstract public Instruction lconst_1 ();
+
+  abstract public Instruction ldc (int v);
+
+  abstract public Instruction ldc (float v);
+
+  abstract public Instruction ldc (String v, boolean isClass);
+
+  abstract public Instruction ldc_w (int v);
+
+  abstract public Instruction ldc_w (float v);
+
+  abstract public Instruction ldc_w (String v, boolean isClass);
+
+  abstract public Instruction ldc2_w (long v);
+
+  abstract public Instruction ldc2_w (double v);
+
+  abstract public Instruction ldiv ();
+
+  abstract public Instruction lload (int localVarIndex);
+
+  abstract public Instruction lload_0 ();
+
+  abstract public Instruction lload_1 ();
+
+  abstract public Instruction lload_2 ();
+
+  abstract public Instruction lload_3 ();
+
+  abstract public Instruction lmul ();
+
+  abstract public Instruction lneg ();
+
+  abstract public Instruction lookupswitch (int defaultTargetPc, int nEntries);
+
+  abstract public Instruction lor ();
+
+  abstract public Instruction lrem ();
+
+  abstract public Instruction lreturn ();
+
+  abstract public Instruction lshl ();
+
+  abstract public Instruction lshr ();
+
+  abstract public Instruction lstore (int localVarIndex);
+
+  abstract public Instruction lstore_0 ();
+
+  abstract public Instruction lstore_1 ();
+
+  abstract public Instruction lstore_2 ();
+
+  abstract public Instruction lstore_3 ();
+
+  abstract public Instruction lsub ();
+
+  abstract public Instruction lushr ();
+
+  abstract public Instruction lxor ();
+
+  abstract public Instruction monitorenter ();
+
+  abstract public Instruction monitorexit ();
+
+  abstract public Instruction multianewarray (String clsName, int dimensions);
+
+  abstract public Instruction new_ (String clsName);
+
+  abstract public Instruction newarray (int typeCode);
+
+  abstract public Instruction nop ();
+
+  abstract public Instruction pop ();
+
+  abstract public Instruction pop2 ();
+
+  abstract public Instruction putfield (String fieldName, String clsName, String fieldDescriptor);
+
+  abstract public Instruction putstatic (String fieldName, String clsName, String fieldDescriptor);
+
+  abstract public Instruction ret (int localVarIndex);
+
+  abstract public Instruction return_ ();
+
+  abstract public Instruction saload ();
+
+  abstract public Instruction sastore ();
+
+  abstract public Instruction sipush (int val);
+
+  abstract public Instruction swap ();
+
+  abstract public Instruction tableswitch (int defaultTargetPc, int low, int high);
+
+  abstract public Instruction wide ();
+
+  //--- the JPF specific ones (only used in synthetic methods)
+  abstract public Instruction invokecg (List<Invocation> invokes);
+
+  abstract public Instruction invokeclinit (ClassInfo ci);
+
+  abstract public Instruction directcallreturn ();
+
+  abstract public Instruction executenative (NativeMethodInfo mi);
+
+  abstract public Instruction nativereturn ();
+
+  // this is never part of MethodInfo stored code
+  abstract public Instruction runstart (MethodInfo miRun);
+
+  abstract public Instruction finishclinit (ClassInfo ci);
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMNativeStackFrame.java b/src/main/gov/nasa/jpf/jvm/JVMNativeStackFrame.java
new file mode 100644 (file)
index 0000000..7daed55
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.vm.NativeMethodInfo;
+import gov.nasa.jpf.vm.NativeStackFrame;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+/**
+ * a NativeStackFrame used for calling NativeMethods from Java bytecode
+ */
+public class JVMNativeStackFrame extends NativeStackFrame {
+
+  public JVMNativeStackFrame (NativeMethodInfo callee){
+    super(callee);
+  }
+  
+  public void setArguments (ThreadInfo ti){
+    StackFrame callerFrame = ti.getTopFrame(); // we are not going to modify it
+    NativeMethodInfo nmi = (NativeMethodInfo) mi;
+    int      nArgs = nmi.getNumberOfArguments();
+    byte[]   argTypes = nmi.getArgumentTypes();
+
+    Object[] a = new Object[nArgs+2];
+
+    int      stackOffset;
+    int      i, j, k;
+    int      ival;
+    long     lval;
+
+    for (i = 0, stackOffset = 0, j = nArgs + 1, k = nArgs - 1;
+         i < nArgs;
+         i++, j--, k--) {
+      switch (argTypes[k]) {
+      case Types.T_BOOLEAN:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = Boolean.valueOf(Types.intToBoolean(ival));
+
+        break;
+
+      case Types.T_BYTE:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = Byte.valueOf((byte) ival);
+
+        break;
+
+      case Types.T_CHAR:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = Character.valueOf((char) ival);
+
+        break;
+
+      case Types.T_SHORT:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = new Short((short) ival);
+
+        break;
+
+      case Types.T_INT:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = new Integer(ival);
+
+        break;
+
+      case Types.T_LONG:
+        lval = callerFrame.peekLong(stackOffset);
+        stackOffset++; // 2 stack words
+        a[j] = new Long(lval);
+
+        break;
+
+      case Types.T_FLOAT:
+        ival = callerFrame.peek(stackOffset);
+        a[j] = new Float(Types.intToFloat(ival));
+
+        break;
+
+      case Types.T_DOUBLE:
+        lval = callerFrame.peekLong(stackOffset);
+        stackOffset++; // 2 stack words
+        a[j] = new Double(Types.longToDouble(lval));
+
+        break;
+
+      default:
+        // NOTE - we have to store T_REFERENCE as an Integer, because
+        // it shows up in our native method as an 'int'
+        ival = callerFrame.peek(stackOffset);
+        a[j] = new Integer(ival);
+      }
+
+      stackOffset++;
+    }
+
+    //--- set  our standard MJI header arguments
+    a[0] = ti.getMJIEnv();
+    
+    if (nmi.isStatic()) {
+      a[1] = new Integer( nmi.getClassInfo().getClassObjectRef());
+    } else {
+      int thisRef = callerFrame.getCalleeThis(nmi);
+      a[1] = new Integer( thisRef);
+      
+      setThis(thisRef);
+    }
+
+    setArgs(a);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JVMStackFrame.java b/src/main/gov/nasa/jpf/jvm/JVMStackFrame.java
new file mode 100644 (file)
index 0000000..d03eda8
--- /dev/null
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.FixedBitSet;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * a stackframe that is used for executing Java bytecode, supporting both
+ * locals and an operand stack. This is essentially the JVm stack machine
+ * implementation
+ */
+public class JVMStackFrame extends StackFrame {
+
+  public JVMStackFrame (MethodInfo callee){
+    super( callee);
+  }
+  
+  /**
+   * creates callerSlots dummy Stackframe for testing of operand/local operations
+   * NOTE - TESTING ONLY! this does not have callerSlots MethodInfo
+   */
+  protected JVMStackFrame (int nLocals, int nOperands){
+    super( nLocals, nOperands);
+  }
+  
+  /**
+   * this sets up arguments from a bytecode caller 
+   */
+  protected void setCallArguments (ThreadInfo ti){
+    StackFrame caller = ti.getTopFrame();
+    MethodInfo miCallee = mi;
+    int nArgSlots = miCallee.getArgumentsSize();
+    
+    if (nArgSlots > 0){
+      int[] calleeSlots = slots;
+      FixedBitSet calleeRefs = isRef;
+      int[] callerSlots = caller.getSlots();
+      FixedBitSet callerRefs = caller.getReferenceMap();
+
+      for (int i = 0, j = caller.getTopPos() - nArgSlots + 1; i < nArgSlots; i++, j++) {
+        calleeSlots[i] = callerSlots[j];
+        if (callerRefs.get(j)) {
+          calleeRefs.set(i);
+        }
+        Object a = caller.getSlotAttr(j);
+        if (a != null) {
+          setSlotAttr(i, a);
+        }
+      }
+
+      if (!miCallee.isStatic()) {
+        thisRef = calleeSlots[0];
+      }
+    }
+  }
+
+  @Override
+  public void setExceptionReference (int exRef){
+    clearOperandStack();
+    pushRef( exRef);
+  }
+  
+  //--- these are for setting up arguments from a VM / listener caller
+
+  /*
+   * to be used to initialize locals of a stackframe (only required for explicit construction without a caller,
+   * otherwise the Stackframe ctor/invoke insn will take care of copying the values from its caller)
+   */
+  @Override
+  public void setArgumentLocal (int idx, int v, Object attr){
+    setLocalVariable( idx, v);
+    if (attr != null){
+      setLocalAttr( idx, attr);
+    }
+  }
+  @Override
+  public void setReferenceArgumentLocal (int idx, int ref, Object attr){
+    setLocalReferenceVariable( idx, ref);
+    if (attr != null){
+      setLocalAttr( idx, attr);
+    }
+  }
+  @Override
+  public void setLongArgumentLocal (int idx, long v, Object attr){
+    setLongLocalVariable( idx, v);
+    if (attr != null){
+      setLocalAttr( idx, attr);
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/jvm/JVMSystemClassLoaderInfo.java b/src/main/gov/nasa/jpf/jvm/JVMSystemClassLoaderInfo.java
new file mode 100644 (file)
index 0000000..29c40c1
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.vm.ClassFileContainer;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.SystemClassLoaderInfo;
+import gov.nasa.jpf.vm.VM;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * a SystemClassLoaderInfo that reads standard Java classfiles from *.class and *.jar files, and creates code using a concrete
+ * value JVM instruction set
+ */
+public class JVMSystemClassLoaderInfo extends SystemClassLoaderInfo {
+
+  static JPFLogger log = JPF.getLogger("class");
+  protected JVMCodeBuilder defaultCodeBuilder;
+
+  public JVMSystemClassLoaderInfo (VM vm, int appId) {
+    super(vm, appId);
+
+    defaultCodeBuilder = createDefaultCodeBuilder(config, appId);
+
+    JVMClassInfo.init(config);
+
+    // now we can notify
+    vm.registerClassLoader(this);
+  }
+
+  /**
+   * override this if you need a different default CodeBuilder
+   */
+  protected JVMCodeBuilder createDefaultCodeBuilder (Config config, int appId) {
+    String key = config.getIndexableKey("jvm.insn_factory.class", appId);
+    JVMInstructionFactory insnFactory = config.getEssentialInstance(key, JVMInstructionFactory.class);
+    return new JVMCodeBuilder(insnFactory);
+  }
+
+  @Override
+  protected ClassFileContainer createClassFileContainer (String spec) {
+    int i = spec.indexOf(".jar");
+    if (i > 0) {
+      // its a jar
+      int j = i + 4;
+      int len = spec.length();
+      String jarPath;
+      String pathPrefix = null;
+      File jarFile;
+      if (j == len) {
+        // no path prefix, plain jar
+        jarPath = spec;
+      } else {
+        if (spec.charAt(j) == '/') {
+          pathPrefix = spec.substring(j);
+          jarPath = spec.substring(0, j);
+        } else {
+          return null;
+        }
+      }
+      jarFile = new File(jarPath);
+      if (jarFile.isFile()) {
+        try {
+          return new JarClassFileContainer(jarFile, pathPrefix);
+        } catch (IOException ix) {
+          return null;
+        }
+      } else {
+        return null;
+      }
+
+    } else {
+      // a dir
+      File dir = new File(spec);
+      if (dir.isDirectory()) {
+        return new DirClassFileContainer(dir);
+      } else {
+        return null;
+      }
+    }
+  }
+
+  protected void addSystemBootClassPath () {
+    String v = System.getProperty("sun.boot.class.path");
+    if (v != null) {
+      for (String pn : v.split(File.pathSeparator)) {
+        if (pn != null && !pn.isEmpty()) {
+          ClassFileContainer cfc = createClassFileContainer(pn);
+          if (cfc != null) {
+            cp.addClassFileContainer(cfc);
+          }
+        }
+      }
+    } else {
+      // Hmm, maybe we are not executing on OpenJDK
+    }
+  }
+
+  /**
+   * this is the main method to create the ClassPath, which is called from the ctor
+   */
+  @Override
+  protected void initializeSystemClassPath (VM vm, int appId) {
+    Config conf = vm.getConfig();
+    File[] pathElements;
+
+    // explicit "classpath[.id]" settings have precedence
+    pathElements = getPathElements(conf, "classpath", appId);
+    if (pathElements != null) {
+      for (File f : pathElements) {
+        addClassPathElement(f.getAbsolutePath());
+      }
+    }
+
+    // we optionally append boot_classpath
+    pathElements = getPathElements(conf, "vm.boot_classpath", appId);
+    if (pathElements != null) {
+      for (File f : pathElements) {
+        if (f.getName().equals("<system>")) {
+          addSystemBootClassPath();
+        } else {
+          addClassPathElement( f.getAbsolutePath());
+        }
+      }
+    }
+
+    log.info("collected system classpath: ", cp);
+  }
+
+  /**
+   * override this if you have different CodeBuilders for different types
+   * NOTE - this CodeBuilder is not completely initialized yet, clients still have to call startMethod(mi) on it
+   */
+  protected JVMCodeBuilder getCodeBuilder (String clsName) {
+    return defaultCodeBuilder;
+  }
+
+  /**
+   * used for automatically created code such as AnnotationProxies, direct calls, native calls and run starts
+   * NOTE - this cannot be called recursively or concurrently
+   */
+  protected JVMCodeBuilder getSystemCodeBuilder (ClassFile cf, MethodInfo mi) {
+    defaultCodeBuilder.reset(cf, mi);
+    return defaultCodeBuilder;
+  }
+  
+  @Override
+  protected ClassInfo createClassInfo (String clsName, String url, byte[] data, ClassLoaderInfo definingLoader) throws ClassParseException {
+    ClassFile cf = new ClassFile(data);
+    JVMCodeBuilder cb = getCodeBuilder(clsName);
+    ClassInfo ci = new JVMClassInfo(clsName, definingLoader, cf, url, cb);
+    setAttributes(ci);
+    
+    return ci;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/JarClassFileContainer.java b/src/main/gov/nasa/jpf/jvm/JarClassFileContainer.java
new file mode 100644 (file)
index 0000000..bace55a
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.vm.ClassFileMatch;
+import gov.nasa.jpf.vm.ClassParseException;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+/**
+ * a ClassFileContainer that loads classes from jar files
+ */
+public class JarClassFileContainer extends JVMClassFileContainer {
+  protected JarFile jar;
+  protected String pathPrefix; // optional
+
+  static String getContainerUrl (File file){
+    try {
+      return "jar:" + file.toURI().toURL().toString() + "!/";
+    } catch (MalformedURLException x) {
+      return "jar:" + file.getAbsolutePath() + "!/";
+    }
+  }
+
+  public JarClassFileContainer (File file) throws IOException {
+    super(file.getPath(), getContainerUrl(file));
+    jar = new JarFile(file);
+  }
+
+  public JarClassFileContainer (File file, String pathPrefix) throws IOException {
+    super(getPath(file, pathPrefix), getContainerUrl(file));
+
+    jar = new JarFile(file);
+    this.pathPrefix = getNormalizedPathPrefix(pathPrefix);
+  }
+  
+  /**
+   * make sure the return value ends with '/', and does NOT start with '/'. If
+   * the supplied pathPrefix only contains '/' or an empty string, return null
+   */
+  static String getNormalizedPathPrefix(String pathPrefix){
+    if (pathPrefix != null){
+      int len = pathPrefix.length();
+      if (len > 0){
+        if (pathPrefix.charAt(0) == '/'){
+          if (len == 1){
+            return null; // no need for storing a single '/' prefix
+          } else {
+            pathPrefix = pathPrefix.substring(1); // skip the heading '/'
+            len--;
+          }
+        }
+        
+        if (pathPrefix.charAt(len-1) != '/'){
+          pathPrefix += '/';
+        }
+        
+        return pathPrefix;
+        
+      } else {
+        return null; // empty prefix
+      }
+    } else {
+      return null; // null prefix
+    }
+  }
+
+  /**
+   * return our string representation of the complete spec, which is
+   * 
+   *   <jar-pathname>/pathPrefix
+   */
+  static String getPath(File file, String pathPrefix){
+    String pn = file.getPath();
+   
+    if (pathPrefix != null){
+      int len = pathPrefix.length();
+      if (len > 0){
+        if (pathPrefix.charAt(0) == '/'){
+          if (len == 1){
+            return pn; // no need to store a single '/'
+          }
+        } else {
+          pn += '/';
+        }
+        
+        pn += pathPrefix;
+      }
+    }
+    
+    return pn;
+  }
+    
+  @Override
+  public ClassFileMatch getMatch(String clsName) throws ClassParseException {
+    String pn = clsName.replace('.', '/') + ".class";
+    
+    if (pathPrefix != null){
+      pn = pathPrefix + pn;
+    }
+    
+    JarEntry e = jar.getJarEntry(pn);
+
+    if (e != null) {
+      InputStream is = null;
+      try {
+        long len = e.getSize();
+        if (len > Integer.MAX_VALUE) {
+          error("classfile too big: " + e.getName());
+        }
+
+        is = jar.getInputStream(e);
+
+        byte[] data = new byte[(int) len];
+        FileUtils.getContents(is, data);
+
+        return new JVMClassFileMatch(clsName, getClassURL(clsName), data);
+
+      } catch (IOException iox) {
+        error("error reading jar entry " + e.getName());
+
+      } finally {
+        if (is != null) {
+          try {
+            is.close();
+          } catch (IOException iox) {
+            error("cannot close input stream for file " + e.getName());
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/AALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/AALOAD.java
new file mode 100644 (file)
index 0000000..c5ac463
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load reference from array
+ * ..., arrayref, index  => ..., value
+ */
+public class AALOAD extends ArrayLoadInstruction {
+
+  @Override
+  public boolean isReferenceArray() {
+    return true;
+  }
+  
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    int value = ei.getReferenceElement(index);
+    frame.pushRef( value);
+  }
+
+
+  @Override
+  protected boolean isReference () {
+    return true;
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x32;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/AASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/AASTORE.java
new file mode 100644 (file)
index 0000000..da2ac8c
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.util.InstructionState;
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Store into reference array
+ * ..., arrayref, index, value  => ...
+ */
+public class AASTORE extends ArrayStoreInstruction {
+
+  int value;
+
+  @Override
+  public boolean isReferenceArray() {
+    return true;
+  }
+  
+  @Override
+  protected void popValue(StackFrame frame){
+    value = frame.pop();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setReferenceElement(index, value);
+  }
+  
+  /**
+   * overridden because AASTORE can cause ArrayStoreExceptions and exposure CGs 
+   */
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int refValue = frame.peek();
+    int idx = frame.peek(1);
+    int aref = frame.peek(2);
+    
+    value = aref;
+    index = idx;
+    
+    if (aref == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException");
+    }
+    
+    ElementInfo eiArray = ti.getModifiableElementInfo(aref);
+        
+    if (!ti.isFirstStepInsn()){ // we only need to check this once
+      Instruction xInsn = checkArrayStoreException(ti, frame, eiArray);
+      if (xInsn != null){
+        return xInsn;
+      }
+    }
+    
+    boolean checkExposure = false;
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedArrayCG(ti, this, eiArray, idx)){
+      checkExposure = true;
+      eiArray = scheduler.updateArraySharedness(ti, eiArray, index);
+      if (scheduler.setsSharedArrayCG(ti, this, eiArray, idx)){
+        return this;
+      }
+    }
+
+    // check if this gets re-executed from an exposure CG
+    if (frame.getAndResetFrameAttr(InstructionState.class) == null){
+      try {
+        Object attr = frame.getOperandAttr();
+        eiArray.checkArrayBounds(idx);
+        eiArray.setReferenceElement(idx, refValue);
+        eiArray.setElementAttrNoClone(idx, attr);
+        
+      } catch (ArrayIndexOutOfBoundsExecutiveException ex) { // at this point, the AIOBX is already processed
+        return ex.getInstruction();
+      }
+
+      if (checkExposure) {
+        if (refValue != MJIEnv.NULL) {
+          ElementInfo eiExposed = ti.getElementInfo(refValue);
+          if (scheduler.setsSharedObjectExposureCG(ti, this, eiArray, null, eiArray)) {
+            frame.addFrameAttr( InstructionState.processed);
+            return this;
+          }
+        }
+      }
+    }
+    
+    frame.pop(3);
+    
+    return getNext(ti);      
+  }
+
+  protected Instruction checkArrayStoreException(ThreadInfo ti, StackFrame frame, ElementInfo ei){
+    ClassInfo c = ei.getClassInfo();
+    int refVal = frame.peek();
+    
+    if (refVal != MJIEnv.NULL) { // no checks for storing 'null'
+      ClassInfo elementCi = ti.getClassInfo(refVal);
+      ClassInfo arrayElementCi = c.getComponentClassInfo();
+      if (!elementCi.isInstanceOf(arrayElementCi)) {
+        String exception = "java.lang.ArrayStoreException";
+        String exceptionDescription = elementCi.getName();
+        return ti.createAndThrowException(exception, exceptionDescription);
+      }
+    }
+
+    return null;
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x53;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ACONST_NULL.java b/src/main/gov/nasa/jpf/jvm/bytecode/ACONST_NULL.java
new file mode 100644 (file)
index 0000000..7a86cea
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push null
+ * ... => ..., null
+ */
+public class ACONST_NULL extends Instruction implements JVMInstruction {
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushRef(MJIEnv.NULL);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x01;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/ALOAD.java
new file mode 100644 (file)
index 0000000..df997f1
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Load reference from local variable
+ * ... => ..., objectref
+ */
+public class ALOAD extends JVMLocalVariableInstruction {
+
+  public ALOAD(int index){
+    super(index);
+  }
+
+  /**
+   * for explicit construction
+   */
+  public void setIndex (int index){
+    this.index = index;    
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLocal(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1; // immediate; opcode
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x2a;
+      case 1: return 0x2b;
+      case 2: return 0x2c;
+      case 3: return 0x2d;
+    }
+
+    return 0x19;  // ? wide versions
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "aload";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ANEWARRAY.java b/src/main/gov/nasa/jpf/jvm/bytecode/ANEWARRAY.java
new file mode 100644 (file)
index 0000000..8f13285
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Create new array of reference
+ * ..., count => ..., arrayref
+ */
+public class ANEWARRAY extends NewArrayInstruction {
+
+  public ANEWARRAY (String typeDescriptor){
+    type = Types.getTypeSignature(typeDescriptor, true);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    // resolve the component class first
+    String compType = Types.getTypeName(type);
+    if(Types.isReferenceSignature(type)) {
+      try {
+        ti.resolveReferencedClass(compType);
+      } catch(LoadOnJPFRequired lre) {
+        return ti.getPC();
+      }
+    }
+
+    // there is no clinit for array classes, but we still have  to create a class object
+    // since its a builtin class, we also don't have to bother with NoClassDefFoundErrors
+    String clsName = "[" + type;
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+      ci.setInitialized();
+    }
+
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    arrayLength = frame.pop();
+    if (arrayLength < 0){
+      return ti.createAndThrowException("java.lang.NegativeArraySizeException");
+    }
+
+    Heap heap = ti.getHeap();
+    if (heap.isOutOfMemory()) { // simulate OutOfMemoryError
+      return ti.createAndThrowException("java.lang.OutOfMemoryError",
+                                        "trying to allocate new " +
+                                          Types.getTypeName(type) +
+                                        "[" + arrayLength + "]");
+    }
+
+    ElementInfo eiArray = heap.newArray(type, arrayLength, ti);
+    int aRef = eiArray.getObjectRef();
+    
+    // pushes the object reference on the top stack frame
+    frame.push(aRef, true);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength () {
+    return 3; // opcode, index1, index2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xBD;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ARETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/ARETURN.java
new file mode 100644 (file)
index 0000000..b703a93
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ReturnValueInstruction;
+
+
+/**
+ * Return reference from method
+ * ..., objectref  => [empty]
+ */
+public class ARETURN extends JVMReturnInstruction implements ReturnValueInstruction {
+  int ret;
+  
+  @Override
+  public int getReturnTypeSize() {
+    return 1;
+  }
+  
+  @Override
+  public int getValueSlot (StackFrame frame){
+    return frame.getTopPos();
+  }
+  
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return frame.getOperandAttr();
+  }
+  
+  @Override
+  protected void getAndSaveReturnValue (StackFrame frame) {
+    ret = frame.pop();
+  }
+  
+  @Override
+  protected void pushReturnValue (StackFrame frame) {
+    frame.pushRef(ret);
+  }
+
+  public int getReturnValue () {
+    return ret;
+  }
+  
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    if (!isCompleted(ti)) { // we have to pull it from the operand stack
+      StackFrame frame = ti.getTopFrame();
+      ret = frame.peek();
+    }
+    
+    if (ret == MJIEnv.NULL) {
+      return null;
+    } else {
+      return ti.getElementInfo(ret);
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xB0;
+  }
+    
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ARRAYLENGTH.java b/src/main/gov/nasa/jpf/jvm/bytecode/ARRAYLENGTH.java
new file mode 100644 (file)
index 0000000..4e02761
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Get length of array 
+ * ..., arrayref => ..., length
+ */
+public class ARRAYLENGTH extends Instruction implements JVMInstruction {
+    
+  protected int arrayRef;
+
+  /**
+   * only makes sense from an executeInstruction() or instructionExecuted() listener,
+   * it is undefined outside of insn exec notifications
+   */
+  public int getArrayRef (ThreadInfo ti){
+    if (ti.isPreExec()){
+      return peekArrayRef(ti);
+    } else {
+      return arrayRef;
+    }
+  }
+
+  public ElementInfo peekArrayElementInfo (ThreadInfo ti){
+    int aref = getArrayRef(ti);
+    return ti.getElementInfo(aref);
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    arrayRef = frame.pop();
+
+    if (arrayRef == MJIEnv.NULL){
+      return ti.createAndThrowException("java.lang.NullPointerException",
+                                        "array length of null object");
+    }
+
+    ElementInfo ei = ti.getElementInfo(arrayRef);
+    frame.push(ei.arrayLength(), false);
+
+    return getNext(ti);
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xBE;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  protected int peekArrayRef (ThreadInfo ti) {
+    return ti.getTopFrame().peek();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/ASTORE.java
new file mode 100644 (file)
index 0000000..14b4f21
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Store reference into local variable
+ * ..., objref => ...
+ */
+public class ASTORE extends JVMLocalVariableInstruction implements StoreInstruction {
+
+  public ASTORE(int index){
+    super(index);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.storeOperand(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1; // immediate; opcode
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x4b;
+      case 1: return 0x4c;
+      case 2: return 0x4d;
+      case 3: return 0x4e;
+    }
+
+    return 0x3A;  // ? wide versions ?
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "astore";
+  }
+  
+  
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ATHROW.java b/src/main/gov/nasa/jpf/jvm/bytecode/ATHROW.java
new file mode 100644 (file)
index 0000000..7e8ff58
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Throw exception or error
+ * ..., objectref => objectref
+ */
+public class ATHROW extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int objref = frame.pop();
+
+    if (objref == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException");
+    }
+
+    return ti.throwException(objref);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xBF;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ArrayLoadInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/ArrayLoadInstruction.java
new file mode 100644 (file)
index 0000000..f0187d7
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * abstraction for all array load instructions
+ *
+ * ..., array, index => ..., value
+ */
+public abstract class ArrayLoadInstruction extends JVMArrayElementInstruction implements JVMInstruction {
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    index = frame.peek();
+    arrayRef = frame.peek(1); // ..,arrayRef,idx
+    if (arrayRef == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException");
+    }
+    ElementInfo eiArray = ti.getElementInfo(arrayRef);
+
+    indexOperandAttr = peekIndexAttr(ti);
+    arrayOperandAttr = peekArrayAttr(ti);
+
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedArrayCG( ti, this, eiArray, index)){ // don't modify the frame before this
+      eiArray = scheduler.updateArraySharedness(ti, eiArray, index);
+      if (scheduler.setsSharedArrayCG( ti, this, eiArray, index)){
+        return this;
+      }
+    }
+    
+    frame.pop(2); // now we can pop index and array reference
+    
+    try {
+      push(frame, eiArray, index);
+
+      Object elementAttr = eiArray.getElementAttr(index);
+      if (elementAttr != null) {
+        if (getElementSize() == 1) {
+          frame.setOperandAttr(elementAttr);
+        } else {
+          frame.setLongOperandAttr(elementAttr);
+        }
+      }
+      
+      return getNext(ti);
+      
+    } catch (ArrayIndexOutOfBoundsExecutiveException ex) {
+      return ex.getInstruction();
+    }
+  }
+
+  protected boolean isReference () {
+    return false;
+  }
+
+  /**
+   * only makes sense pre-exec
+   */
+  @Override
+  public int peekArrayRef (ThreadInfo ti){
+    return ti.getTopFrame().peek(1);
+  }
+
+  @Override
+  public Object peekArrayAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr(1);
+  }
+
+  // wouldn't really be required for loads, but this is a general
+  // ArrayInstruction API
+  @Override
+  public int peekIndex (ThreadInfo ti){
+    return ti.getTopFrame().peek();
+  }
+
+  @Override
+  public Object peekIndexAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr();
+  }
+
+  protected abstract void push (StackFrame frame, ElementInfo e, int index)
+                throws ArrayIndexOutOfBoundsExecutiveException;
+
+  
+  @Override
+  public boolean isRead() {
+    return true;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+ }
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ArrayStoreInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/ArrayStoreInstruction.java
new file mode 100644 (file)
index 0000000..a0b5f32
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * abstraction for all array store instructions
+ *
+ *  ... array, index, <value> => ...
+ */
+public abstract class ArrayStoreInstruction extends JVMArrayElementInstruction implements StoreInstruction, JVMInstruction {
+
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int idx = peekIndex(ti);
+    int aref = peekArrayRef(ti); // need to be polymorphic, could be LongArrayStore
+    ElementInfo eiArray = ti.getElementInfo(aref);
+
+    arrayOperandAttr = peekArrayAttr(ti);
+    indexOperandAttr = peekIndexAttr(ti);
+
+    if (!ti.isFirstStepInsn()){ // first execution, top half
+      //--- runtime exceptions
+      if (aref == MJIEnv.NULL) {
+        return ti.createAndThrowException("java.lang.NullPointerException");
+      }
+    
+      //--- shared access CG
+      Scheduler scheduler = ti.getScheduler();
+      if (scheduler.canHaveSharedArrayCG(ti, this, eiArray, idx)){
+        eiArray = scheduler.updateArraySharedness(ti, eiArray, idx);
+        if (scheduler.setsSharedArrayCG(ti, this, eiArray, idx)){
+          return this;
+        }
+      }
+    }
+    
+    try {
+      setArrayElement(ti, frame, eiArray); // this pops operands
+    } catch (ArrayIndexOutOfBoundsExecutiveException ex) { // at this point, the AIOBX is already processed
+      return ex.getInstruction();
+    }
+
+    return getNext(ti);
+  }
+
+  protected void setArrayElement (ThreadInfo ti, StackFrame frame, ElementInfo eiArray) throws ArrayIndexOutOfBoundsExecutiveException {
+    int esize = getElementSize();
+    Object attr = esize == 1 ? frame.getOperandAttr() : frame.getLongOperandAttr();
+    
+    popValue(frame);
+    index = frame.pop();
+    // don't set 'arrayRef' before we do the CG checks (would kill loop optimization)
+    arrayRef = frame.pop();
+
+    eiArray = eiArray.getModifiableInstance();
+    setField(eiArray, index);
+    eiArray.setElementAttrNoClone(index,attr); // <2do> what if the value is the same but not the attr?
+  }
+  
+  /**
+   * this is for pre-exec use
+   */
+  @Override
+  public int peekArrayRef(ThreadInfo ti) {
+    return ti.getTopFrame().peek(2);
+  }
+
+  @Override
+  public int peekIndex(ThreadInfo ti){
+    return ti.getTopFrame().peek(1);
+  }
+
+  // override in LongArrayStoreInstruction
+  @Override
+  public Object  peekArrayAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr(2);
+  }
+
+  @Override
+  public Object peekIndexAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr(1);
+  }
+
+
+  protected abstract void popValue(StackFrame frame);
+  protected abstract void setField (ElementInfo e, int index)
+                    throws ArrayIndexOutOfBoundsExecutiveException;
+
+
+  @Override
+  public boolean isRead() {
+    return false;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/BALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/BALOAD.java
new file mode 100644 (file)
index 0000000..f10901d
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.BooleanArrayFields;
+import gov.nasa.jpf.vm.ByteArrayFields;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Load byte or boolean from array
+ * ..., arrayref, index => ..., value
+ */
+public class BALOAD extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+
+    int value = 0;
+    Fields f = ei.getFields();
+    if (f instanceof ByteArrayFields){
+      value = ei.getByteElement(index);
+    } else if (f instanceof BooleanArrayFields){
+      value = ei.getBooleanElement(index) ? 1 : 0;
+    }
+
+    frame.push( value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x33;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/BASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/BASTORE.java
new file mode 100644 (file)
index 0000000..9590212
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.BooleanArrayFields;
+import gov.nasa.jpf.vm.ByteArrayFields;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Store into byte or boolean array
+ * ..., arrayref, index, value  => ...
+ */
+public class BASTORE extends ArrayStoreInstruction {
+
+  byte value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = (byte)frame.pop();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+
+    Fields f = ei.getFields();
+
+    if (f instanceof ByteArrayFields){
+      ei.setByteElement(index, value);
+
+    } else if (f instanceof BooleanArrayFields){
+      ei.setBooleanElement(index, value != 0 ? true : false);
+    }
+
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x54;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+  
+  public byte getValue(){
+    return value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/BIPUSH.java b/src/main/gov/nasa/jpf/jvm/bytecode/BIPUSH.java
new file mode 100644 (file)
index 0000000..4f63d86
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push byte
+ * ... => ..., value
+ */
+public class BIPUSH extends Instruction implements JVMInstruction {
+  private int value;
+
+  public BIPUSH() {} // this is going away
+
+  public BIPUSH(int value){
+    this.value = value;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    frame.push(value);
+
+    return getNext(ti);
+  }
+  
+  public int getValue(){
+         return value;
+  }
+  
+  @Override
+  public int getLength() {
+    return 2; // opcode, byte
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x10;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/CALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/CALOAD.java
new file mode 100644 (file)
index 0000000..12984e1
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Load char from array
+ * ..., arrayref, index => ..., value
+ */
+public class CALOAD extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo e, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    e.checkArrayBounds(index);
+    frame.push( e.getCharElement(index), isReference());
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x34;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/CASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/CASTORE.java
new file mode 100644 (file)
index 0000000..ddb2918
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Store into char array
+ * ..., arrayref, index, value  => ...
+ */
+public class CASTORE extends ArrayStoreInstruction {
+
+  char value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = (char)frame.pop();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setCharElement(index, value);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x55;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/CHECKCAST.java b/src/main/gov/nasa/jpf/jvm/bytecode/CHECKCAST.java
new file mode 100644 (file)
index 0000000..b121da9
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Check whether object is of given type
+ * ..., objectref => ..., objectref
+ */
+public class CHECKCAST extends Instruction implements JVMInstruction {
+  String type;
+
+  public CHECKCAST() {} // this is going away
+
+  public CHECKCAST(String typeName){
+    type = Types.getClassNameFromTypeName(typeName);
+  }
+
+  public String getTypeName() {
+    return type;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getTopFrame();
+    int objref = frame.peek();
+
+    if (objref == MJIEnv.NULL) {
+       // we can cast 'null' to anything
+
+    } else {
+      boolean isValid = false;
+
+      if(Types.isReferenceSignature(type)) {
+        String t;
+        if(Types.isArray(type)) {
+          // retrieve the component terminal
+          t = Types.getComponentTerminal(type);
+        } else {
+          t = type;
+        }
+
+        // resolve the referenced class
+        try {
+          ti.resolveReferencedClass(t);
+        } catch(LoadOnJPFRequired lre) {
+          return ti.getPC();
+        }
+      }
+
+      ElementInfo e = ti.getElementInfo(objref);
+      ClassInfo eci = e.getClassInfo();
+
+      if (type.charAt(0) == '['){  // cast between array types
+        if (eci.isArray()) {
+          // check if the element types are compatible
+          ClassInfo cci = eci.getComponentClassInfo();
+          isValid = cci.isInstanceOf(type.substring(1));
+        }
+
+      } else { // non-array types
+        isValid = e.getClassInfo().isInstanceOf(type);
+      }
+
+      if (!isValid) {
+        return ti.createAndThrowException("java.lang.ClassCastException",
+                e.getClassInfo().getName() + " cannot be cast to " + type);
+      }
+    }
+
+    return getNext(ti);
+  }
+
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xC0;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/D2F.java b/src/main/gov/nasa/jpf/jvm/bytecode/D2F.java
new file mode 100644 (file)
index 0000000..d7a5497
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert double to float
+ * ..., value => ..., result
+ */
+public class D2F extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v = frame.popDouble();
+    frame.pushFloat( (float)v);
+    
+    return getNext(ti);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x90;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/D2I.java b/src/main/gov/nasa/jpf/jvm/bytecode/D2I.java
new file mode 100644 (file)
index 0000000..a13b46e
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert double to int
+ * ..., value => ..., result
+ */
+public class D2I extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v = frame.popDouble();
+    frame.push( (int)v);
+    
+    return getNext(ti);
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x8E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/D2L.java b/src/main/gov/nasa/jpf/jvm/bytecode/D2L.java
new file mode 100644 (file)
index 0000000..f28f37f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert double to long
+ * ..., value => ..., result
+ */
+public class D2L extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v = frame.popDouble();
+    frame.pushLong( (long) v);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x8F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DADD.java b/src/main/gov/nasa/jpf/jvm/bytecode/DADD.java
new file mode 100644 (file)
index 0000000..76cc41c
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Add double
+ * ..., value1, value2 => ..., result
+ */
+public class DADD extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    double r = v1 + v2;
+
+    frame.pushDouble(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x63;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/DALOAD.java
new file mode 100644 (file)
index 0000000..3cf2a4e
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load double from array
+ * ..., arrayref, index => ..., value
+ */
+public class DALOAD extends LongArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    double value = ei.getDoubleElement(index);
+    frame.pushLong( Double.doubleToLongBits(value));
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x31;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/DASTORE.java
new file mode 100644 (file)
index 0000000..25ac2bc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Store into double array
+ * ..., arrayref, index, value => ...
+ */
+public class DASTORE extends LongArrayStoreInstruction {
+
+  double value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = Double.longBitsToDouble(frame.popLong());
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setDoubleElement(index, value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x52;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DCMPG.java b/src/main/gov/nasa/jpf/jvm/bytecode/DCMPG.java
new file mode 100644 (file)
index 0000000..ca11dd1
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+/**
+ * Compare double
+ * ..., value1, value2 => ..., result
+ */
+public class DCMPG extends DoubleCompareInstruction {
+
+  @Override
+  protected int conditionValue(double v1, double v2) {
+    if (Double.isNaN(v1) || Double.isNaN(v2)) {
+      return 1;
+    } else if (v1 == v2) {
+      return 0;
+    } else if (v2 > v1) {
+      return 1;
+    } else {
+      return -1;
+    }
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x98;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DCMPL.java b/src/main/gov/nasa/jpf/jvm/bytecode/DCMPL.java
new file mode 100644 (file)
index 0000000..09f97a4
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+
+/**
+ * Compare double
+ * ..., value1, value2 => ..., result
+ */
+public class DCMPL extends DoubleCompareInstruction {
+
+
+  @Override
+  protected int conditionValue(double v1, double v2) {
+    if (Double.isNaN(v1) || Double.isNaN(v2)) {
+      return -1;
+    } else if (v1 == v2) {
+      return 0;
+    } else if (v2 > v1) {
+      return 1;
+    } else {
+      return -1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x97;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DCONST.java b/src/main/gov/nasa/jpf/jvm/bytecode/DCONST.java
new file mode 100644 (file)
index 0000000..eb7660c
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push double
+ * ... => ..., <d>
+ */
+public class DCONST extends Instruction implements JVMInstruction {
+  private double value;
+
+  public DCONST() {} // this is going away
+
+  public DCONST (double d){
+    value = d;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.pushDouble(value);
+
+    return getNext(ti);
+  }
+  
+  public double getValue(){
+         return value;
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x0E;  // ? DCONST_0 0x0E , DCONST_1 0x0F
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DDIV.java b/src/main/gov/nasa/jpf/jvm/bytecode/DDIV.java
new file mode 100644 (file)
index 0000000..4c20b68
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Divide double
+ * ..., value1, value2 => ..., result
+ */
+public class DDIV extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    double r = v2 / v1;
+    
+    frame.pushDouble(r);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x6F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DIRECTCALLRETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/DIRECTCALLRETURN.java
new file mode 100644 (file)
index 0000000..4561426
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
+
+/**
+ * this is used to return from a DirectCallStackFrame
+ *
+ * Note that it is NOT a ReturnInstruction, in case listeners monitor these
+ * and expect corresponding InvokeInstructions. Although this would seem intuitive, it
+ * would be pointless to derive because the ReturnInstruction.enter() does
+ * a lot of things we would have to cut off, i.e. it would require more effort
+ * to undo this (no sync, no return value, no pc advance on the returned-to
+ * stackframe etc.)
+ *
+ * However, having a dedicated direct call return instruction makes sense so
+ * that the ReturnInstruction of the called method does not have to handle
+ * direct calls specifically
+ */
+public class DIRECTCALLRETURN extends ReturnInstruction implements JVMInstruction  {
+
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 261;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    if (ti.getStackDepth() == 1){ // thread exit point
+      // this can execute several times because of the different locks involved
+    
+      if (!ti.exit()){
+        return this; // repeat, we couldn't get the lock
+      } else {
+        return null;
+      }      
+      
+    } else {
+      // pop the current frame but do not advance the new top frame, and do
+      // not touch its operand stack
+    
+      StackFrame frame = ti.popDirectCallFrame();
+      return frame.getPC();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DLOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/DLOAD.java
new file mode 100644 (file)
index 0000000..3e2df69
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Load double from local variable
+ * ... => ..., value
+ */
+public class DLOAD extends JVMLocalVariableInstruction {
+
+  public DLOAD (int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLongLocal(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2;  // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x26;
+      case 1: return 0x27;
+      case 2: return 0x28;
+      case 3: return 0x29;
+    }
+    
+    return 0x18; // wide version?
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "dload";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DMUL.java b/src/main/gov/nasa/jpf/jvm/bytecode/DMUL.java
new file mode 100644 (file)
index 0000000..a8232a6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Multiply double
+ * ..., value1, value2 => ..., result
+ */
+public class DMUL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    double r = v2 * v1;
+    
+    frame.pushDouble(r);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x6B;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DNEG.java b/src/main/gov/nasa/jpf/jvm/bytecode/DNEG.java
new file mode 100644 (file)
index 0000000..5eb4ec3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Negate double
+ * ..., value => ..., result
+ */
+public class DNEG extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v = frame.popDouble();
+    frame.pushDouble(-v);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x77;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DREM.java b/src/main/gov/nasa/jpf/jvm/bytecode/DREM.java
new file mode 100644 (file)
index 0000000..1548f9f
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Remainder double
+ * ..., value1, value2 => ..., result
+ */
+public class DREM extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    if (v1 == 0){
+      return ti.createAndThrowException("java.lang.ArithmeticException","division by zero");
+    }
+    
+    double r = v2 % v1;
+    
+    frame.pushDouble(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x73;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DRETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/DRETURN.java
new file mode 100644 (file)
index 0000000..ec536d1
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Return double from method
+ * ..., value => [empty]
+ */
+public class DRETURN extends LongReturn {
+  
+
+  public double getReturnValue() {
+    return Types.longToDouble(ret);
+  }
+
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    if (!isCompleted(ti)) { // we have to pull it from the operand stack
+      StackFrame frame = ti.getTopFrame();
+      ret = frame.peekLong();
+    }
+
+    return new Double(Types.longToDouble(ret));
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xAF;
+  }
+
+  @Override
+  public String toString() {
+    return "dreturn " + mi.getFullName();
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DSTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/DSTORE.java
new file mode 100644 (file)
index 0000000..6f30fd0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * Store double into local variable
+ * ..., value => ...
+ */
+public class DSTORE extends JVMLocalVariableInstruction implements StoreInstruction  {
+
+  public DSTORE(int localVarIndex) {
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.storeLongOperand(index);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 2; // opcode, index
+  }
+
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x47;
+      case 1: return 0x48;
+      case 2: return 0x49;
+      case 3: return 0x4a;
+    }
+
+    return 0x39; // ?? wide
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "dstore";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DSUB.java b/src/main/gov/nasa/jpf/jvm/bytecode/DSUB.java
new file mode 100644 (file)
index 0000000..ef34877
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Subtract double
+ * ..., value1, value2 => ..., result
+ */
+public class DSUB extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    double r = v2 - v1;
+    
+    frame.pushDouble(r);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x67;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP.java
new file mode 100644 (file)
index 0000000..e5426f8
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * duplicate topmost stack entry
+ * .., value -> .., value, value
+ */
+public class DUP extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo th) {
+    StackFrame frame = th.getModifiableTopFrame();
+    
+    frame.dup();
+
+    return getNext(th);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x59;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP2.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP2.java
new file mode 100644 (file)
index 0000000..13263b7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Duplicate the top one or two operand stack values
+ * ..., value2, value1 => ..., value2, value1, value2, value1
+ */
+public class DUP2 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.dup2();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5C;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X1.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X1.java
new file mode 100644 (file)
index 0000000..55c9d2c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * DOCUMENT ME!
+ */
+public class DUP2_X1 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.dup2_x1();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5D;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X2.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP2_X2.java
new file mode 100644 (file)
index 0000000..25f5816
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Duplicate the top operand stack value and insert two or three values down
+ * ... A B C D => ... C D.A B C D
+ */
+public class DUP2_X2 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    frame.dup2_x2();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP_X1.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP_X1.java
new file mode 100644 (file)
index 0000000..14e8da7
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Duplicate the top operand stack value and insert two values down
+ * ..., value2, value1 => ..., value1, value2, value1
+ */
+public class DUP_X1 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    frame.dup_x1();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5A;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DUP_X2.java b/src/main/gov/nasa/jpf/jvm/bytecode/DUP_X2.java
new file mode 100644 (file)
index 0000000..d27a01c
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * DOCUMENT ME!
+ */
+public class DUP_X2 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    frame.dup_x2();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5B;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/DoubleCompareInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/DoubleCompareInstruction.java
new file mode 100644 (file)
index 0000000..5e8d978
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * base class for double double compare instructions
+ */
+public abstract class DoubleCompareInstruction extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    double v1 = frame.popDouble();
+    double v2 = frame.popDouble();
+    
+    int condVal = conditionValue(v1, v2);
+    
+    frame.push( condVal);
+    
+    return getNext(ti);
+  }
+
+  protected abstract int conditionValue (double v1, double v2);
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/EXECUTENATIVE.java b/src/main/gov/nasa/jpf/jvm/bytecode/EXECUTENATIVE.java
new file mode 100644 (file)
index 0000000..843b934
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativeMethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.lang.reflect.Method;
+
+/**
+ * this is a synthetic instruction to (re-)execute native methods
+ *
+ * Note that StackFrame and lock handling has to occur from within
+ * the corresponding NativeMethodInfo
+ */
+public class EXECUTENATIVE extends Instruction implements JVMInstruction {
+
+  // unfortunately we can't null this in cleanupTransients(), but it is
+  // a potential leak for stored traces
+  protected NativeMethodInfo executedMethod;
+
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 259;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+
+  public EXECUTENATIVE (){}
+
+  public EXECUTENATIVE (NativeMethodInfo mi){
+    executedMethod = mi;
+  }
+
+  public void setExecutedMethod (NativeMethodInfo mi){
+    executedMethod = mi;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+
+    // we don't have to enter/leave or push/pop a frame, that's all done
+    // in NativeMethodInfo.execute()
+    // !! don't re-enter if this is reexecuted !!
+    return executedMethod.executeNative(ti);
+  }
+
+  public MethodInfo getExecutedMethod() {
+    return executedMethod;
+  }
+
+  public String getExecutedMethodName(){
+    return executedMethod.getName();
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("executenative");
+
+    if (executedMethod != null){
+      Method m = executedMethod.getMethod();
+      sb.append(' ');
+      sb.append( m.getDeclaringClass().getSimpleName());
+      sb.append('.');
+      sb.append( m.getName());
+    }
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/F2D.java b/src/main/gov/nasa/jpf/jvm/bytecode/F2D.java
new file mode 100644 (file)
index 0000000..fcca517
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert float to double
+ * ..., value  => ..., result
+ */
+public class F2D extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    float f = frame.popFloat();
+    
+    frame.pushDouble(f);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x8D;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/F2I.java b/src/main/gov/nasa/jpf/jvm/bytecode/F2I.java
new file mode 100644 (file)
index 0000000..6c88637
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert float to int
+ * ..., value => ..., result
+ */
+public class F2I extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    float v = frame.popFloat();
+    
+    frame.push((int)v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x8B;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/F2L.java b/src/main/gov/nasa/jpf/jvm/bytecode/F2L.java
new file mode 100644 (file)
index 0000000..00ebb5f
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert float to long
+ * ..., value => ..., result
+ */
+public class F2L extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    float v = frame.popFloat();
+    
+    frame.pushLong((long)v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x8C;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FADD.java b/src/main/gov/nasa/jpf/jvm/bytecode/FADD.java
new file mode 100644 (file)
index 0000000..6089f63
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Add float
+ * ..., value1, value2 => ..., result
+ */
+public class FADD extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    float r = v1 + v2;
+    frame.push( Types.floatToInt(r), false);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x62;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/FALOAD.java
new file mode 100644 (file)
index 0000000..aa87d79
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load float from array
+ * ..., arrayref, index => ..., value
+ */
+public class FALOAD extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    float value = ei.getFloatElement(index);
+    frame.push( Float.floatToIntBits(value));
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x30;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/FASTORE.java
new file mode 100644 (file)
index 0000000..ee2dace
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Store into float array
+ * ..., arrayref, index, value => ...
+ */
+public class FASTORE extends ArrayStoreInstruction {
+
+  float value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = Float.intBitsToFloat(frame.pop());
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setFloatElement(index, value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x51;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FCMPG.java b/src/main/gov/nasa/jpf/jvm/bytecode/FCMPG.java
new file mode 100644 (file)
index 0000000..823b599
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Compare float
+ * ..., value1, value2 => ..., result
+ */
+public class FCMPG extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    frame.push(conditionValue(v1, v2), false);
+
+    return getNext(ti);
+  }
+  
+  protected int conditionValue(float v1, float v2) {
+      if (Float.isNaN(v1) || Float.isNaN(v2)) {
+          return 1;
+      } else if (v1 == v2) {
+          return 0;
+      } else if (v2 > v1) {
+          return 1;
+      } else {
+          return -1;
+      }
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x96;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FCMPL.java b/src/main/gov/nasa/jpf/jvm/bytecode/FCMPL.java
new file mode 100644 (file)
index 0000000..91db159
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Compare float
+ * ..., value1, value2 => ..., result
+ */
+public class FCMPL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    int condVal = conditionValue(v1, v2);
+    
+    frame.push(condVal);
+
+    return getNext(ti);
+  }
+  
+  protected int conditionValue(float v1, float v2) {
+    if (Float.isNaN(v1) || Float.isNaN(v2)) {
+      return -1;
+    } else if (v1 == v2) {
+        return 0;
+    } else if (v2 > v1) {
+        return 1;
+    } else {
+        return -1;
+    }      
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x95;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FCONST.java b/src/main/gov/nasa/jpf/jvm/bytecode/FCONST.java
new file mode 100644 (file)
index 0000000..3259e8f
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push float
+ * ... => ..., <f>
+ */
+public class FCONST extends Instruction implements JVMInstruction {
+  protected float value;
+
+
+  public FCONST(){} // this is going away
+
+  public FCONST(float f){
+    value = f;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushFloat(value);
+
+    return getNext(ti);
+  }
+
+  public float getValue(){
+         return value;
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x0B; // ?? FCONST_0, _1, _2
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FDIV.java b/src/main/gov/nasa/jpf/jvm/bytecode/FDIV.java
new file mode 100644 (file)
index 0000000..7b7fd34
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * divide float
+ * ..., value1, value2 => ..., result
+ */
+public class FDIV extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    float r = v2 / v1;
+    frame.push(Types.floatToInt(r), false);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x6E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FINISHCLINIT.java b/src/main/gov/nasa/jpf/jvm/bytecode/FINISHCLINIT.java
new file mode 100644 (file)
index 0000000..add2baf
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * artificial bytecode to defer class status changes of classes that don't have
+ * a <clinit>(), simulating native VM specific behavior that involves nested locking
+ * during class init
+ */
+public class FINISHCLINIT extends Instruction {
+
+  protected ClassInfo ciInit;
+
+  public FINISHCLINIT (ClassInfo ci){
+    ciInit = ci;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    ciInit.setInitialized();
+    return getNext(ti);
+  }
+
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 262;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FLOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/FLOAD.java
new file mode 100644 (file)
index 0000000..a501de5
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Load float from local variable
+ * ... => ..., value
+ */
+public class FLOAD extends JVMLocalVariableInstruction {
+
+  public FLOAD(int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLocal(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x22;
+      case 1: return 0x23;
+      case 2: return 0x24;
+      case 3: return 0x25;
+    }
+
+    return 0x17; // ?? wide version
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "fload";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FMUL.java b/src/main/gov/nasa/jpf/jvm/bytecode/FMUL.java
new file mode 100644 (file)
index 0000000..34075e2
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Multiply float
+ * ..., value1, value2 => ..., result
+ */
+public class FMUL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    float r = v1 * v2;    
+    frame.push(Types.floatToInt(r), false);
+
+    return getNext(ti);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x6A;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FNEG.java b/src/main/gov/nasa/jpf/jvm/bytecode/FNEG.java
new file mode 100644 (file)
index 0000000..7fc7314
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Negate float
+ * ..., value  => ..., result
+ */
+public class FNEG extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    float v = frame.popFloat();
+    
+    frame.pushFloat( -v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x76;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FREM.java b/src/main/gov/nasa/jpf/jvm/bytecode/FREM.java
new file mode 100644 (file)
index 0000000..69f9c8e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Remainder float
+ * ..., value1, value2 => ..., result
+ */
+public class FREM extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+
+    if (v1 == 0){
+      return ti.createAndThrowException("java.lang.ArithmeticException","division by zero");
+    }
+
+    float r = v2 % v1;
+    
+    frame.pushFloat(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x72;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FRETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/FRETURN.java
new file mode 100644 (file)
index 0000000..4609a76
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Return float from method
+ * ..., value => [empty]
+ */
+public class FRETURN extends JVMReturnInstruction {
+
+  float ret;
+  
+  @Override
+  public int getReturnTypeSize() {
+    return 1;
+  }
+
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return frame.getOperandAttr();
+  }
+  
+  @Override
+  protected void getAndSaveReturnValue (StackFrame frame) {
+    ret = frame.popFloat();
+  }
+  
+  @Override
+  protected void pushReturnValue (StackFrame frame) {
+    frame.pushFloat(ret);
+  }
+  
+  public float getReturnValue () {
+    return ret;
+  }
+  
+  @Override
+  public Float getReturnValue (ThreadInfo ti) {
+    if (!isCompleted(ti)) { // we have to pull it from the operand stack
+      StackFrame frame = ti.getTopFrame();
+      ret = frame.peekFloat();
+    }
+    
+    return new Float(ret);
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xAE;
+  }
+  
+  @Override
+  public String toString() {
+    return "freturn " + mi.getFullName();
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FSTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/FSTORE.java
new file mode 100644 (file)
index 0000000..617f205
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Store float into local variable
+ * ..., value => ...
+ */
+public class FSTORE extends JVMLocalVariableInstruction implements StoreInstruction {
+
+  public FSTORE(int localVarIndex) {
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.storeOperand(index);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+    case 0: return 0x43;
+    case 1: return 0x44;
+    case 2: return 0x45;
+    case 3: return 0x46;
+    }
+    
+    return 0x38;  // ?? wide
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "fstore";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/FSUB.java b/src/main/gov/nasa/jpf/jvm/bytecode/FSUB.java
new file mode 100644 (file)
index 0000000..f9291f4
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Subtract float
+ * ..., value1, value2 => ..., result
+ */
+public class FSUB extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    float v1 = frame.popFloat();
+    float v2 = frame.popFloat();
+    
+    float r = v2 - v1;
+    
+    frame.pushFloat(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x66;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/GETFIELD.java b/src/main/gov/nasa/jpf/jvm/bytecode/GETFIELD.java
new file mode 100644 (file)
index 0000000..5627b12
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ReadInstruction;
+
+
+/**
+ * Fetch field from object
+ * ..., objectref => ..., value
+ */
+public class GETFIELD extends JVMInstanceFieldInstruction implements ReadInstruction {
+
+  public GETFIELD (String fieldName, String classType, String fieldDescriptor){
+    super(fieldName, classType, fieldDescriptor);
+  }
+  
+  
+  @Override
+  public int getObjectSlot (StackFrame frame){
+    return frame.getTopPos();
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int objRef = frame.peek(); // don't pop yet, we might re-enter
+    lastThis = objRef;
+
+    //--- check for obvious exceptions
+    if (objRef == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException",
+              "referencing field '" + fname + "' on null object");
+    }
+
+    ElementInfo eiFieldOwner = ti.getElementInfo(objRef);
+    FieldInfo fieldInfo = getFieldInfo();
+    if (fieldInfo == null) {
+      return ti.createAndThrowException("java.lang.NoSuchFieldError",
+              "referencing field '" + fname + "' in " + eiFieldOwner);
+    }
+
+    //--- check for potential transition breaks (be aware everything above gets re-executed)
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedObjectCG( ti, this, eiFieldOwner, fieldInfo)){
+      eiFieldOwner = scheduler.updateObjectSharedness( ti, eiFieldOwner, fieldInfo);
+      if (scheduler.setsSharedObjectCG( ti, this, eiFieldOwner, fieldInfo)){
+        return this; // re-execute
+      }
+    }
+    
+    frame.pop(); // Ok, now we can remove the object ref from the stack
+    Object fieldAttr = eiFieldOwner.getFieldAttr(fieldInfo);
+
+    // We could encapsulate the push in ElementInfo, but not the GET, so we keep it at the same level
+    if (fieldInfo.getStorageSize() == 1) { // 1 slotter
+      int ival = eiFieldOwner.get1SlotField(fieldInfo);
+      lastValue = ival;
+      
+      if (fieldInfo.isReference()){
+        frame.pushRef(ival);
+        
+      } else {
+        frame.push(ival);
+      }
+      
+      if (fieldAttr != null) {
+        frame.setOperandAttr(fieldAttr);
+      }
+
+    } else {  // 2 slotter
+      long lval = eiFieldOwner.get2SlotField(fieldInfo);
+      lastValue = lval;
+
+      frame.pushLong( lval);
+      if (fieldAttr != null) {
+        frame.setLongOperandAttr(fieldAttr);
+      }
+    }
+
+    return getNext(ti);
+  }
+
+  @Override
+  public ElementInfo peekElementInfo (ThreadInfo ti) {
+    StackFrame frame = ti.getTopFrame();
+    int objRef = frame.peek();
+    ElementInfo ei = ti.getElementInfo(objRef);
+    return ei;
+  }
+
+  @Override
+  public boolean isMonitorEnterPrologue(){
+    return GetHelper.isMonitorEnterPrologue(mi, insnIndex);
+  }
+  
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xB4;
+  }
+
+  @Override
+  public boolean isRead() {
+    return true;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java b/src/main/gov/nasa/jpf/jvm/bytecode/GETSTATIC.java
new file mode 100644 (file)
index 0000000..7b1aecb
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ReadInstruction;
+
+
+/**
+ * Get static fieldInfo from class
+ * ..., => ..., value 
+ */
+public class GETSTATIC extends JVMStaticFieldInstruction  implements ReadInstruction {
+
+  public GETSTATIC(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    FieldInfo fieldInfo;
+
+    //--- check if this causes a class load by a user defined classloader
+    try {
+      fieldInfo = getFieldInfo();
+    } catch (LoadOnJPFRequired lre) {
+      return ti.getPC();
+    }
+    
+    if (fieldInfo == null) {
+      return ti.createAndThrowException("java.lang.NoSuchFieldError",
+              (className + '.' + fname));
+    }
+
+    //--- check if this has to trigger class initialization
+    ClassInfo ciField = fieldInfo.getClassInfo();
+    if (!mi.isClinit(ciField) && ciField.initializeClass(ti)) {
+      // note - this returns the next insn in the topmost clinit that just got pushed
+      return ti.getPC();
+    }
+    ElementInfo eiFieldOwner = ciField.getStaticElementInfo();
+
+    //--- check if this breaks the transition
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedClassCG( ti, this, eiFieldOwner, fieldInfo)){
+      eiFieldOwner = scheduler.updateClassSharedness(ti, eiFieldOwner, fieldInfo);
+      if (scheduler.setsSharedClassCG( ti, this, eiFieldOwner, fieldInfo)){
+        return this; // re-execute
+      }
+    }
+        
+    Object fieldAttr = eiFieldOwner.getFieldAttr(fieldInfo);
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    if (size == 1) {
+      int ival = eiFieldOwner.get1SlotField(fieldInfo);
+      lastValue = ival;
+
+      if (fieldInfo.isReference()) {
+        frame.pushRef(ival);
+      } else {
+        frame.push(ival);
+      }
+      
+      if (fieldAttr != null) {
+        frame.setOperandAttr(fieldAttr);
+      }
+
+    } else {
+      long lval = eiFieldOwner.get2SlotField(fieldInfo);
+      lastValue = lval;
+      
+      frame.pushLong(lval);
+      
+      if (fieldAttr != null) {
+        frame.setLongOperandAttr(fieldAttr);
+      }
+    }
+        
+    return getNext(ti);
+  }
+  
+  @Override
+  public boolean isMonitorEnterPrologue(){
+    return GetHelper.isMonitorEnterPrologue(mi, insnIndex);
+  }
+  
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xB2;
+  }
+
+  @Override
+  public boolean isRead() {
+    return true;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/GOTO.java b/src/main/gov/nasa/jpf/jvm/bytecode/GOTO.java
new file mode 100644 (file)
index 0000000..51afe35
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Branch always
+ * No change
+ *
+ * <2do> store this as code insnIndex, not as bytecode position
+ */
+public class GOTO extends Instruction implements JVMInstruction {
+  protected int targetPosition;
+  Instruction target;
+
+  public GOTO (int targetPosition){
+    this.targetPosition = targetPosition;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    if (isBackJump() && ti.maxTransitionLengthExceeded()){
+      // this is a rather simplistic attempt to terminate the search in
+      // endless loops that do not change program state.
+      // <2do> this does not handle nested loops yet
+      if (ti.breakTransition("MAX_TRANSITION_LENGTH")){
+        return this; // re-execute after giving state matching a chance to prune the search
+      }
+    }
+    
+    return getTarget();
+  }
+
+  @Override
+  public boolean isBackJump () {
+    return (targetPosition <= position);
+  }
+  
+  public Instruction getTarget() {
+    if (target == null) {
+      target = mi.getInstructionAt(targetPosition);
+    }
+    return target;
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, bb1, bb2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xA7;
+  }
+  
+  @Override
+  public String toString () {
+    return getMnemonic() + " " + targetPosition;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    GOTO clone = null;
+
+    try {
+      clone = (GOTO) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+
+      clone.target = null;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/GOTO_W.java b/src/main/gov/nasa/jpf/jvm/bytecode/GOTO_W.java
new file mode 100644 (file)
index 0000000..47828a2
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+public class GOTO_W extends GOTO {
+
+  public GOTO_W(int targetPos){
+    super(targetPos);
+  }
+
+  @Override
+  public int getLength() {
+    return 5; // opcode, bb1, bb2, bb3, bb4
+  }
+  
+  @Override
+  public int getByteCode() {
+    return 0xc8;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/GetHelper.java b/src/main/gov/nasa/jpf/jvm/bytecode/GetHelper.java
new file mode 100644 (file)
index 0000000..2ae43b4
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+
+/**
+ * helper class to factor out common GET code
+ * 
+ * <2do> This is going to be moved into a Java 8 interface with default methods
+ */
+public class GetHelper {
+
+  /**
+   * do a little bytecode pattern analysis on the fly, to find out if a
+   * GETFIELD or GETSTATIC is just part of a "..synchronized (obj) {..} .."
+   * pattern, which usually translates into the following pattern:
+   *   ...
+   *   getfield / getstatic
+   *   dup
+   *   [astore]
+   *   monitorenter
+   *   ...
+   *
+   * If it does, there is no need to break the transition since the object
+   * reference is not used for anything that can cause violations between
+   * the get and the monitorenter.
+   *
+   * <2do> We might want to extend this in the future to also cover sync on
+   * local vars, like "Object o = myField; synchronized(o){..}..", but then
+   * the check becomes more expensive since we get interspersed aload/astore
+   * insns, and some of the locals could be used outside the sync block. Not
+   * sure if it buys much on the bottom line
+   *   
+   * <2do> this relies on javac code patterns. The dup/astore could
+   * lead to subsequent use of the object reference w/o corresponding get/putfield
+   * insns (if it's not a volatile), but this access would be either a call
+   * or a get/putfield on a share object, i.e. would be checked separately 
+   */
+  protected static boolean isMonitorEnterPrologue(MethodInfo mi, int insnIndex){
+    Instruction[] code = mi.getInstructions();
+    int off = insnIndex+1;
+
+    if (off < code.length-3) {
+      // we don't reach out further than 3 instructions
+      if (code[off] instanceof DUP) {
+        off++;
+
+        if (code[off] instanceof ASTORE) {
+          off++;
+        }
+
+        if (code[off] instanceof MONITORENTER) {
+          return true;
+        }
+      }
+    }
+    
+    return false; // if in doubt, we assume it is not part of a monitorenter code pattern
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2B.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2B.java
new file mode 100644 (file)
index 0000000..09cbf04
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to byte
+ * ..., value => ..., result
+ */
+public class I2B extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int v = frame.pop();
+    
+    frame.push((byte) v, false);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x91;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2C.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2C.java
new file mode 100644 (file)
index 0000000..b33b3a6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to char
+ * ..., value => ..., result
+ */
+public class I2C extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int v = frame.pop();
+    
+    frame.push( (char)v, false);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x92;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2D.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2D.java
new file mode 100644 (file)
index 0000000..4e2968d
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to double
+ * ..., value => ..., result
+ */
+public class I2D extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int ival = frame.pop();
+    
+    frame.pushDouble(ival);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x87;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2F.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2F.java
new file mode 100644 (file)
index 0000000..e5d21c8
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to float
+ * ..., value  =>..., result
+ */
+public class I2F extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v = frame.pop();
+    
+    frame.pushFloat(v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x86;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2L.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2L.java
new file mode 100644 (file)
index 0000000..0e0a3ab
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to long
+ * ..., value => ..., result
+ */
+public class I2L extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v = frame.pop();
+    
+    frame.pushLong(v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x85;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/I2S.java b/src/main/gov/nasa/jpf/jvm/bytecode/I2S.java
new file mode 100644 (file)
index 0000000..6a2cf69
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert int to short
+ * ..., value => ..., result
+ */
+public class I2S extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v = frame.pop();
+    
+    frame.push((short)v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x93;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IADD.java b/src/main/gov/nasa/jpf/jvm/bytecode/IADD.java
new file mode 100644 (file)
index 0000000..a931f94
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Add int
+ * ..., value1, value2  =>..., result
+ */
+public class IADD extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v1 + v2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x60;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/IALOAD.java
new file mode 100644 (file)
index 0000000..9ee777a
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load int from array
+ * ..., arrayref, index => ..., value
+ */
+public class IALOAD extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    int value = ei.getIntElement(index);
+    frame.push( value);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x2E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IAND.java b/src/main/gov/nasa/jpf/jvm/bytecode/IAND.java
new file mode 100644 (file)
index 0000000..e3d08a7
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean AND int
+ * ..., value1, value2 => ..., result
+ */
+public class IAND extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v1 & v2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x7E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IASTORE.java
new file mode 100644 (file)
index 0000000..94fe36d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Store into int array
+ * ..., arrayref, index, value => ...
+ */
+public class IASTORE extends ArrayStoreInstruction {
+
+  int value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = frame.pop();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setIntElement(index, value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x4F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ICONST.java b/src/main/gov/nasa/jpf/jvm/bytecode/ICONST.java
new file mode 100644 (file)
index 0000000..deeb057
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push int constant
+ * ... => ..., <i>
+ */
+public class ICONST extends Instruction implements JVMInstruction {
+  protected int value;
+
+  public ICONST(int value){
+    this.value = value;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.push(value);
+
+    return getNext(ti);
+  }
+
+  public int getValue() {
+    return value;
+  }
+  
+  @Override
+  public int getByteCode () {
+    assert ((value >= -1) && (value < 6)) : ("illegal iconst value: " + value);
+
+    switch (value) {
+    case -1: return 0x2;
+    case 0: return 0x3;
+    case 1: return 0x4;
+    case 2: return 0x5;
+    case 3: return 0x6;
+    case 4: return 0x7;
+    case 5: return 0x8;
+    }
+    return 0;
+  }
+  
+  @Override
+  public String getMnemonic () {
+    String s = "iconst_";
+    
+    if (value == -1) {
+      return s + "m1";
+    } else {
+      return s + value;
+    }
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IDIV.java b/src/main/gov/nasa/jpf/jvm/bytecode/IDIV.java
new file mode 100644 (file)
index 0000000..31b9ca7
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Divide int
+ * ..., value1, value2  =>..., result
+ */
+public class IDIV extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    if (v1 == 0) {
+      return ti.createAndThrowException("java.lang.ArithmeticException",
+                                        "division by zero");
+    }
+
+    frame.push(v2 / v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x6C;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFEQ.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFEQ.java
new file mode 100644 (file)
index 0000000..cfdf693
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFEQ extends IfInstruction {
+
+  public IFEQ(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() == 0);
+  }
+  
+
+  @Override
+  public int getByteCode () {
+    return 0x99;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFGE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFGE.java
new file mode 100644 (file)
index 0000000..9564fd2
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFGE extends IfInstruction {
+
+  public IFGE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() >= 0);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9C;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFGT.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFGT.java
new file mode 100644 (file)
index 0000000..a28feb8
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFGT extends IfInstruction {
+
+  public IFGT(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() > 0);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9D;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFLE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFLE.java
new file mode 100644 (file)
index 0000000..97c3e96
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFLE extends IfInstruction {
+
+  public IFLE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() <= 0);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9E;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFLT.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFLT.java
new file mode 100644 (file)
index 0000000..7e2b10d
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFLT extends IfInstruction {
+
+  public IFLT(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() < 0);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9B;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFNE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFNE.java
new file mode 100644 (file)
index 0000000..bcd9649
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison with zero succeeds
+ * ..., value => ...
+ */
+public class IFNE extends IfInstruction {
+
+  public IFNE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() != 0);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9A;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFNONNULL.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFNONNULL.java
new file mode 100644 (file)
index 0000000..bac53c4
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if reference not null
+ * ..., value => ..., result
+ */
+public class IFNONNULL extends IfInstruction {
+
+  public IFNONNULL(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() != MJIEnv.NULL);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xC7;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IFNULL.java b/src/main/gov/nasa/jpf/jvm/bytecode/IFNULL.java
new file mode 100644 (file)
index 0000000..26673ce
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * branch instruction for null values
+ */
+public class IFNULL extends IfInstruction {
+
+  public IFNULL(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    return (frame.pop() == MJIEnv.NULL);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xC6;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPEQ.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPEQ.java
new file mode 100644 (file)
index 0000000..0ca55eb
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if reference comparison succeeds
+ * ..., value1, value2  => ...
+ */
+public class IF_ACMPEQ extends IfInstruction {
+
+  public IF_ACMPEQ(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 == v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA5;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPNE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ACMPNE.java
new file mode 100644 (file)
index 0000000..a0f02fc
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if reference comparison does not succeed
+ * ..., value1, value2 => ...
+ */
+public class IF_ACMPNE extends IfInstruction {
+
+  public IF_ACMPNE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return  (v1 != v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA6;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPEQ.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPEQ.java
new file mode 100644 (file)
index 0000000..8d3527f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPEQ extends IfInstruction {
+
+  public IF_ICMPEQ(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 == v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x9F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGE.java
new file mode 100644 (file)
index 0000000..0631401
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPGE extends IfInstruction {
+
+  public IF_ICMPGE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 <= v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA2;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGT.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPGT.java
new file mode 100644 (file)
index 0000000..7d75717
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPGT extends IfInstruction {
+
+  public IF_ICMPGT(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 < v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA3;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLE.java
new file mode 100644 (file)
index 0000000..cbf322b
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPLE extends IfInstruction {
+
+  public IF_ICMPLE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 >= v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA4;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLT.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPLT.java
new file mode 100644 (file)
index 0000000..68b1517
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPLT extends IfInstruction {
+
+  public IF_ICMPLT(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 > v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA1;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPNE.java b/src/main/gov/nasa/jpf/jvm/bytecode/IF_ICMPNE.java
new file mode 100644 (file)
index 0000000..85897c9
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Branch if int comparison succeeds
+ * ..., value1, value2 => ...
+ */
+public class IF_ICMPNE extends IfInstruction {
+
+  public IF_ICMPNE(int targetPc) {
+    super(targetPc);
+  }
+
+
+  @Override
+  public boolean popConditionValue (StackFrame frame) {
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    return (v1 != v2);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xA0;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IINC.java b/src/main/gov/nasa/jpf/jvm/bytecode/IINC.java
new file mode 100644 (file)
index 0000000..14b1bba
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Increment local variable by constant
+ * No change
+ */
+public class IINC extends Instruction implements JVMInstruction {
+
+  protected int index;
+  protected int increment;
+
+  public IINC(int localVarIndex, int increment) {
+    this.index = localVarIndex;
+    this.increment = increment;
+  }
+
+  @Override
+  public Instruction execute(ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v = frame.getLocalVariable(index);
+    v += increment;
+
+    frame.setLocalVariable(index, v, false);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index, const
+  }
+
+  @Override
+  public int getByteCode() {
+    return 0x84; // ?? wide
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+    insVisitor.visit(this);
+  }
+
+  public int getIndex() {
+    return index;
+  }
+
+  public int getIncrement() {
+    return increment;
+  }
+
+  @Override
+  public String toPostExecString() {
+    return "iinc " + index + ' ' + increment;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ILOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/ILOAD.java
new file mode 100644 (file)
index 0000000..ff5e11a
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Load int from local variable
+ * ... => ..., value
+ */
+public class ILOAD extends JVMLocalVariableInstruction {
+
+  public ILOAD(int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLocal(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x1a;
+      case 1: return 0x1b;
+      case 2: return 0x1c;
+      case 3: return 0x1d;
+    }
+
+    return 0x15; // ?? wide
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "iload";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IMUL.java b/src/main/gov/nasa/jpf/jvm/bytecode/IMUL.java
new file mode 100644 (file)
index 0000000..d59af99
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Multiply int
+ *  ..., value1, value2 => ..., result
+ */
+public class IMUL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v1 * v2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x68;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INEG.java b/src/main/gov/nasa/jpf/jvm/bytecode/INEG.java
new file mode 100644 (file)
index 0000000..21b970c
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Negate int
+ * ..., value => ..., result
+ */
+public class INEG extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v = frame.pop();
+    
+    frame.push(-v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x74;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INSTANCEOF.java b/src/main/gov/nasa/jpf/jvm/bytecode/INSTANCEOF.java
new file mode 100644 (file)
index 0000000..e0efab4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Determine if object is of given type
+ * ..., objectref => ..., result
+ */
+public class INSTANCEOF extends Instruction implements JVMInstruction {
+  private String type;
+
+
+  /**
+   * typeName is of a/b/C notation
+   */
+  public INSTANCEOF (String typeName){
+    type = Types.getTypeSignature(typeName, false);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    if(Types.isReferenceSignature(type)) {
+      String t;
+      if(Types.isArray(type)) {
+        // retrieve the component terminal
+        t = Types.getComponentTerminal(type);
+      } else {
+        t = type;
+      }
+
+      // resolve the referenced class
+      try {
+        ti.resolveReferencedClass(t);
+      } catch(LoadOnJPFRequired lre) {
+        return ti.getPC();
+      }
+    }
+
+    StackFrame frame = ti.getModifiableTopFrame();
+    int objref = frame.pop();
+
+    if (objref == MJIEnv.NULL) {
+      frame.push(0);
+    } else if (ti.getElementInfo(objref).instanceOf(type)) {
+      frame.push(1);
+    } else {
+      frame.push(0);
+    }
+
+    return getNext(ti);
+  }
+  
+  public String getType() {
+         return type;
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xC1;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKECG.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKECG.java
new file mode 100644 (file)
index 0000000..45bc4b8
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.jvm.JVMInstructionFactory;
+import gov.nasa.jpf.util.Invocation;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ObjRef;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.choice.InvocationCG;
+
+import java.util.List;
+
+/**
+ * a sytnthetic INVOKE instruction that gets it's parameters from an
+ * InvocationCG. Whoever uses this better makes sure the frame this
+ * executes in has enough operand space (e.g. a DirectCallStackFrame).
+ * 
+ */
+public class INVOKECG extends Instruction implements JVMInstruction {
+
+  List<Invocation>  invokes;
+  JVMInvokeInstruction realInvoke;
+
+  public INVOKECG(List<Invocation> invokes){
+    this.invokes = invokes;
+  }
+
+
+  public void setInvokes(List<Invocation> invokes) {
+    this.invokes = invokes;
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    
+    if (!ti.isFirstStepInsn()) {
+      InvocationCG cg = new InvocationCG( "INVOKECG", invokes);
+      if (ti.getVM().setNextChoiceGenerator(cg)){
+        return this;
+      }
+      
+    } else {
+      InvocationCG cg = ti.getVM().getCurrentChoiceGenerator( "INVOKECG", InvocationCG.class);
+      assert (cg != null) : "no current InvocationCG";
+
+      Invocation call = cg.getNextChoice();
+      MethodInfo callee = call.getMethodInfo();
+      JVMInstructionFactory insnFactory = JVMInstructionFactory.getFactory();
+
+      String clsName = callee.getClassInfo().getName();
+      String mthName = callee.getName();
+      String signature = callee.getSignature();
+
+      Instruction realInvoke;
+      if (callee.isStatic()){
+        realInvoke = insnFactory.invokestatic(clsName, mthName, signature);
+      } else {
+        realInvoke = insnFactory.invokevirtual(clsName, mthName, signature);
+      }
+      
+      pushArguments(ti, call.getArguments(), call.getAttrs());
+      
+      return realInvoke;
+    }
+
+    return getNext();
+  }
+
+  void pushArguments (ThreadInfo ti, Object[] args, Object[] attrs){
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    if (args != null){
+      for (int i=0; i<args.length; i++){
+        Object a = args[i];
+        boolean isLong = false;
+        
+        if (a != null){
+          if (a instanceof ObjRef){
+            frame.pushRef(((ObjRef)a).getReference());
+          } else if (a instanceof Boolean){
+            frame.push((Boolean)a ? 1 : 0, false);
+          } else if (a instanceof Integer){
+            frame.push((Integer)a, false);
+          } else if (a instanceof Long){
+            frame.pushLong((Long)a);
+            isLong = true;
+          } else if (a instanceof Double){
+            frame.pushLong(Types.doubleToLong((Double)a));
+            isLong = true;
+          } else if (a instanceof Byte){
+            frame.push((Byte)a, false);
+          } else if (a instanceof Short){
+            frame.push((Short)a, false);
+          } else if (a instanceof Float){
+            frame.push(Types.floatToInt((Float)a), false);
+          }
+        }
+
+        if (attrs != null && attrs[i] != null){
+          if (isLong){
+            frame.setLongOperandAttr(attrs[i]);
+          } else {
+            frame.setOperandAttr(attrs[i]);
+          }
+        }
+      }
+    }
+  }
+  
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 258;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKECLINIT.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKECLINIT.java
new file mode 100644 (file)
index 0000000..50584e5
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * this is an artificial bytecode that we use to deal with the particularities of 
+ * <clinit> calls, which are never in the loaded bytecode but always directly called by
+ * the VM. The most obvious difference is that <clinit> execution does not trigger
+ * class initialization.
+ * A more subtle difference is that we save a wait() - if a class
+ * is concurrently initialized, both enter INVOKECLINIT (i.e. compete and sync for/on
+ * the class object lock), but once the second thread gets resumed and detects that the
+ * class is now initialized (by the first thread), it skips the method execution and
+ * returns right away (after deregistering as a lock contender). That's kind of hackish,
+ * but we have no method to do the wait in, unless we significantly complicate the
+ * direct call stubs, which would obfuscate observability (debugging dynamically
+ * generated code isn't very appealing). 
+ */
+public class INVOKECLINIT extends INVOKESTATIC {
+
+  public INVOKECLINIT (ClassInfo ci){
+    super(ci.getSignature(), "<clinit>", "()V");
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {    
+    MethodInfo callee = getInvokedMethod(ti);
+    ClassInfo ciClsObj = callee.getClassInfo();
+    ElementInfo ei = ciClsObj.getClassObject();
+
+    if (ciClsObj.isInitialized()) { // somebody might have already done it if this is re-executed
+      if (ei.isRegisteredLockContender(ti)){
+        ei = ei.getModifiableInstance();
+        ei.unregisterLockContender(ti);
+      }
+      return getNext();
+    }
+
+    // not much use to update sharedness, clinits are automatically synchronized
+    if (reschedulesLockAcquisition(ti, ei)){     // this blocks or registers as lock contender
+      return this;
+    }
+    
+    // if we get here we still have to execute the clinit method
+    setupCallee( ti, callee); // this creates, initializes & pushes the callee StackFrame, then acquires the lock
+    ciClsObj.setInitializing(ti);
+
+    return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception
+  }
+
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 256;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEDYNAMIC.java
new file mode 100644 (file)
index 0000000..a071ece
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.BootstrapMethodInfo;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FunctionObjectFactory;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Invoke dynamic method. It allows dynamic linkage between a call site and a method implementation.
+ *
+ * ..., [arg1, [arg2 ...]]  => ...
+ */
+public class INVOKEDYNAMIC extends Instruction {
+  
+  // index of a bootstrap method (index to the array bootstrapMethods[] declared in ClassInfo
+  // containing this bytecode instruction)
+  int bootstrapMethodIndex;
+  
+  // Free variables are those that are not defined within the lamabda body and 
+  // are captured from the lexical scope. Note that for instance lambda methods 
+  // the first captured variable always represents "this"
+  String[] freeVariableTypeNames;
+  byte[] freeVariableTypes;
+  
+  String functionalInterfaceName;
+  
+  String samMethodName;
+  
+  int funcObjRef = MJIEnv.NULL;
+  
+  ElementInfo lastFuncObj = null;
+  
+  public INVOKEDYNAMIC () {}
+
+  protected INVOKEDYNAMIC (int bmIndex, String methodName, String descriptor){
+    bootstrapMethodIndex = bmIndex;
+    samMethodName = methodName;
+    freeVariableTypeNames = Types.getArgumentTypeNames(descriptor);
+    freeVariableTypes = Types.getArgumentTypes(descriptor);
+    functionalInterfaceName = Types.getReturnTypeSignature(descriptor);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xBA;
+  }
+  
+  @Override
+  public String toString() {
+    String args = "";
+    for(String type: freeVariableTypeNames) {
+      if(args.length()>0) {
+        type += ','+ type;
+      }
+      args += type;
+    }
+    return "invokedynamic " + bootstrapMethodIndex + " " + 
+    samMethodName + '(' + args +"):" + functionalInterfaceName;
+  }
+
+  /**
+   * For now, INVOKEDYNAMIC works only in the context of lambda expressions.
+   * Executing this returns an object that implements the functional interface 
+   * and contains a method which captures the behavior of the lambda expression.
+   */
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    ElementInfo ei = ti.getHeap().get(funcObjRef);
+    
+    if(ei==null || ei!=lastFuncObj || freeVariableTypes.length>0) {
+      ClassInfo fiClassInfo;
+
+      // First, resolve the functional interface
+      try {
+        fiClassInfo = ti.resolveReferencedClass(functionalInterfaceName);
+      } catch(LoadOnJPFRequired lre) {
+        return ti.getPC();
+      }
+
+      if (fiClassInfo.initializeClass(ti)) {
+        return ti.getPC();
+      }
+      
+      ClassInfo enclosingClass = this.getMethodInfo().getClassInfo();
+      
+      BootstrapMethodInfo bmi = enclosingClass.getBootstrapMethodInfo(bootstrapMethodIndex);
+      
+      VM vm = VM.getVM();
+      FunctionObjectFactory funcObjFactory = vm.getFunctionObjectFacotry();
+      
+      Object[] freeVariableValues = frame.getArgumentsValues(ti, freeVariableTypes);
+      
+      funcObjRef = funcObjFactory.getFunctionObject(bootstrapMethodIndex, ti, fiClassInfo, samMethodName, bmi, freeVariableTypeNames, freeVariableValues);
+      lastFuncObj = ti.getHeap().get(funcObjRef);
+    }
+    
+    frame.pop(freeVariableTypes.length);
+    frame.pushRef(funcObjRef);
+    
+    return getNext(ti);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEINTERFACE.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEINTERFACE.java
new file mode 100644 (file)
index 0000000..3a7c22a
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+/**
+ * Invoke interface method
+ * ..., objectref, [arg1, [arg2 ...]] => ...
+ */
+public class INVOKEINTERFACE extends VirtualInvocation {
+  public INVOKEINTERFACE () {}
+
+  protected INVOKEINTERFACE (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+
+  @Override
+  public int getLength() {
+    return 5; // opcode, index1, index2, nargs, 0
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xB9;
+  }
+
+  @Override
+  public String toString() {
+    // methodInfo not set outside real call context (requires target object)
+    return "invokeinterface " + cname + '.' + mname;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESPECIAL.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESPECIAL.java
new file mode 100644 (file)
index 0000000..94f399c
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Invoke instance method; special handling for superclass, private,
+ * and instance initialization method invocations
+ * ..., objectref, [arg1, [arg2 ...]] => ...
+ *
+ * invokedMethod is supposed to be constant (ClassInfo can't change)
+ */
+public class INVOKESPECIAL extends InstanceInvocation {
+
+  public INVOKESPECIAL (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0xB7;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    int argSize = getArgSize();
+    int objRef = ti.getCalleeThis( argSize);
+    lastObj = objRef;
+
+    // we don't have to check for NULL objects since this is either a ctor, a 
+    // private method, or a super method
+
+    MethodInfo callee;  
+    try {
+      callee = getInvokedMethod(ti);
+    } catch(LoadOnJPFRequired rre) {
+      return ti.getPC();
+    }      
+
+    if (callee == null){
+      return ti.createAndThrowException("java.lang.NoSuchMethodException", "Calling " + cname + '.' + mname);
+    }
+
+    ElementInfo ei = ti.getElementInfo(objRef);
+    if (callee.isSynchronized()){
+      ei = ti.getScheduler().updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
+      if (reschedulesLockAcquisition(ti, ei)){
+        return this;
+      }
+    }
+
+    setupCallee( ti, callee); // this creates, initializes and pushes the callee StackFrame
+
+    return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception
+  }
+
+  /**
+   * If the current thread already owns the lock, then the current thread can go on.
+   * For example, this is a recursive acquisition.
+   */
+  protected boolean isLockOwner(ThreadInfo ti, ElementInfo ei) {
+    return ei.getLockingThread() == ti;
+  }
+
+  /**
+   * If the object will still be owned, then the current thread can go on.
+   * For example, all but the last monitorexit for the object.
+   */
+  protected boolean isLastUnlock(ElementInfo ei) {
+    return ei.getLockCount() == 1;
+  }
+
+  /**
+    * we can do some more caching here - the MethodInfo should be const
+    */
+  @Override
+  public MethodInfo getInvokedMethod (ThreadInfo th) {
+
+    // since INVOKESPECIAL is only used for private methods and ctors,
+    // we don't have to deal with null object calls
+
+    if (invokedMethod == null) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname);
+      boolean recursiveLookup = (mname.charAt(0) != '<'); // no hierarchy lookup for <init>
+      invokedMethod = ci.getMethod(mname, recursiveLookup);
+    }
+
+    return invokedMethod; // we can store internally
+  }
+
+  @Override
+  public String toString() {
+    return ("invokespecial " + cname + '.' + mname);
+  }
+
+  @Override
+  public Object getFieldValue (String id, ThreadInfo ti) {
+    int objRef = getCalleeThis(ti);
+    ElementInfo ei = ti.getElementInfo(objRef);
+
+    Object v = ei.getFieldValueObject(id);
+
+    if (v == null){ // try a static field
+      v = ei.getClassInfo().getStaticFieldValueObject(id);
+    }
+
+    return v;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKESTATIC.java
new file mode 100644 (file)
index 0000000..0ff67ae
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Invoke a class (static) method
+ * ..., [arg1, [arg2 ...]]  => ...
+ */
+public class INVOKESTATIC extends JVMInvokeInstruction {
+  ClassInfo ci;
+  
+  protected INVOKESTATIC (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+  protected ClassInfo getClassInfo () {
+    if (ci == null) {
+      ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname);
+    }
+    return ci;
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xB8;
+  }
+
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(' ');
+    sb.append( invokedMethod.getFullName());
+
+    if (invokedMethod.isMJI()){
+      sb.append(" [native]");
+    }
+    
+    return sb.toString();
+
+  }
+  
+  public StaticElementInfo getStaticElementInfo (){
+    return getClassInfo().getStaticElementInfo();
+  }
+
+  public int getClassObjectRef(){
+    return getClassInfo().getStaticElementInfo().getClassObjectRef();
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    MethodInfo callee;
+    
+    try {
+      callee = getInvokedMethod(ti);
+    } catch (LoadOnJPFRequired lre) {
+      return ti.getPC();
+    }
+        
+    if (callee == null) {
+      return ti.createAndThrowException("java.lang.NoSuchMethodException", cname + '.' + mname);
+    }
+
+    // this can be actually different than (can be a base)
+    ClassInfo ciCallee = callee.getClassInfo();
+    
+    if (ciCallee.initializeClass(ti)) {
+      // do class initialization before continuing
+      // note - this returns the next insn in the topmost clinit that just got pushed
+      return ti.getPC();
+    }
+
+    if (callee.isSynchronized()) {
+      ElementInfo ei = ciCallee.getClassObject();
+      ei = ti.getScheduler().updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
+      
+      if (reschedulesLockAcquisition(ti, ei)){
+        return this;
+      }
+    }
+        
+    setupCallee( ti, callee); // this creates, initializes and pushes the callee StackFrame
+
+    return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception
+  }
+
+  @Override
+  public MethodInfo getInvokedMethod(){
+    if (invokedMethod != null){
+      return invokedMethod;
+    } else {
+      // Hmm, this would be pre-exec, but if the current thread is not the one executing the insn 
+      // this might result in false sharedness of the class object
+      return getInvokedMethod( ThreadInfo.getCurrentThread());
+    }
+  }
+  
+  @Override
+  public MethodInfo getInvokedMethod (ThreadInfo ti){
+    if (invokedMethod == null) {
+      ClassInfo clsInfo = getClassInfo();
+      if (clsInfo != null){
+        MethodInfo callee = clsInfo.getMethod(mname, true);
+        if (callee != null){
+          ClassInfo ciCallee = callee.getClassInfo(); // might be a superclass of ci, i.e. not what is referenced in the insn
+
+          if (!ciCallee.isRegistered()){
+            // if it wasn't registered yet, classLoaded listeners didn't have a chance yet to modify it..
+            ciCallee.registerClass(ti);
+            // .. and might replace/remove MethodInfos
+            callee = clsInfo.getMethod(mname, true);
+          }
+          invokedMethod = callee;
+        }
+      }    
+    }
+    return invokedMethod;
+  }
+  
+  // can be different thatn the ci - method can be in a superclass
+  public ClassInfo getInvokedClassInfo(){
+    return getInvokedMethod().getClassInfo();
+  }
+
+  public String getInvokedClassName(){
+    return getInvokedClassInfo().getName();
+  }
+
+  @Override
+  public int getArgSize () {
+    if (argSize < 0) {
+      argSize = Types.getArgumentsSize(signature);
+    }
+
+    return argSize;
+  }
+
+  
+  @Override
+  public String toString() {
+    // methodInfo not set outside real call context (requires target object)
+    return "invokestatic " + cname + '.' + mname;
+  }
+
+  @Override
+  public Object getFieldValue (String id, ThreadInfo ti) {
+    return getClassInfo().getStaticFieldValueObject(id);
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    INVOKESTATIC clone = null;
+
+    try {
+      clone = (INVOKESTATIC) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+
+      clone.invokedMethod = null;
+      clone.lastObj = Integer.MIN_VALUE;
+      clone.ci = null;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEVIRTUAL.java b/src/main/gov/nasa/jpf/jvm/bytecode/INVOKEVIRTUAL.java
new file mode 100644 (file)
index 0000000..841b328
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+
+/**
+ * Invoke instance method; dispatch based on class
+ * ..., objectref, [arg1, [arg2 ...]] => ...
+ */
+public class INVOKEVIRTUAL extends VirtualInvocation {
+  public INVOKEVIRTUAL () {}
+
+  protected INVOKEVIRTUAL (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0xB6;
+  }
+  
+  @Override
+  public String toString() {
+    // methodInfo not set outside real call context (requires target object)
+    return "invokevirtual " + cname + '.' + mname;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IOR.java b/src/main/gov/nasa/jpf/jvm/bytecode/IOR.java
new file mode 100644 (file)
index 0000000..80f4616
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean OR int
+ * ..., value1, value2 => ..., result
+ */
+public class IOR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v1 | v2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x80;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IREM.java b/src/main/gov/nasa/jpf/jvm/bytecode/IREM.java
new file mode 100644 (file)
index 0000000..013e977
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Remainder int
+ * ..., value1, value2  => ..., result
+ */
+public class IREM extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    if (v1 == 0){
+      return ti.createAndThrowException("java.lang.ArithmeticException", "division by zero");
+    }
+    
+    frame.push(v2 % v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x70;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IRETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/IRETURN.java
new file mode 100644 (file)
index 0000000..339280f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Return int from method
+ * ..., value => [empty]
+ */
+public class IRETURN extends JVMReturnInstruction {
+
+  int ret;
+  
+  @Override
+  public int getReturnTypeSize() {
+    return 1;
+  }
+  
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return frame.getOperandAttr();
+  }
+  
+  @Override
+  protected void getAndSaveReturnValue (StackFrame ti) {
+    ret = ti.pop();
+  }
+  
+  @Override
+  protected void pushReturnValue (StackFrame ti) {
+    ti.push(ret);
+  }
+  
+  public int getReturnValue () {
+    return ret;
+  }
+  
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    if (!isCompleted(ti)) { // we have to pull it from the operand stack
+      StackFrame frame = ti.getTopFrame();
+      ret = frame.peek();
+    }
+
+    return new Integer(ret);
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xAC;
+  }
+  
+  @Override
+  public String toString() {
+    return "ireturn " + mi.getFullName();
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ISHL.java b/src/main/gov/nasa/jpf/jvm/bytecode/ISHL.java
new file mode 100644 (file)
index 0000000..15f6455
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Shift left int
+ * ..., value1, value2 => ..., result
+ */
+public class ISHL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v2 << v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x78;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ISHR.java b/src/main/gov/nasa/jpf/jvm/bytecode/ISHR.java
new file mode 100644 (file)
index 0000000..c3f46f5
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Arithmetic shift right int
+ * ..., value1, value2 => ..., result
+ */
+public class ISHR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v2 >> v1);
+
+    return getNext( ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x7A;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ISTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/ISTORE.java
new file mode 100644 (file)
index 0000000..9d99d25
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Store int into local variable
+ * ..., value => ...
+ */
+public class ISTORE extends JVMLocalVariableInstruction implements StoreInstruction {
+
+  public ISTORE(int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.storeOperand(index);
+    
+    return getNext(ti);
+
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // bytecode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+    case 0: return 0x3b;
+    case 1: return 0x3c;
+    case 2: return 0x3d;
+    case 3: return 0x3e;
+    }
+    return 0x36; // ?? wide, ISTORE_n
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "istore";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/ISUB.java b/src/main/gov/nasa/jpf/jvm/bytecode/ISUB.java
new file mode 100644 (file)
index 0000000..28c195c
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Subtract int
+ *  ..., value1, value2 => ..., result
+ */
+public class ISUB extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v2 - v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x64;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IUSHR.java b/src/main/gov/nasa/jpf/jvm/bytecode/IUSHR.java
new file mode 100644 (file)
index 0000000..58a9176
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Logical shift right int
+ * ..., value1, value2 => ..., result
+ */
+public class IUSHR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v2 >>> v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x7C;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IXOR.java b/src/main/gov/nasa/jpf/jvm/bytecode/IXOR.java
new file mode 100644 (file)
index 0000000..de28b88
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean XOR int
+ * ..., value1, value2 => ..., result
+ */
+public class IXOR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    int v2 = frame.pop();
+
+    frame.push(v1 ^ v2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x82;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/IfInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/IfInstruction.java
new file mode 100644 (file)
index 0000000..2993bc4
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.BooleanChoiceGenerator;
+import gov.nasa.jpf.vm.KernelState;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * abstraction for all comparison instructions
+ */
+public abstract class IfInstruction extends Instruction implements JVMInstruction {
+  protected int targetPosition;  // insn position at jump insnIndex
+  protected Instruction target;  // jump target
+  
+  protected boolean conditionValue;  /** value of last evaluation of branch condition */
+
+  protected IfInstruction(int targetPosition){
+    this.targetPosition = targetPosition;
+  }
+
+  /**
+   * return which branch was taken. Only useful after instruction got executed
+   * WATCH OUT - 'true' means the jump condition is met, which logically is
+   * the 'false' branch
+   */
+  public boolean getConditionValue() {
+    return conditionValue;
+  }
+    
+  /**
+   *  Added so that SimpleIdleFilter can detect do-while loops when 
+   * the while statement evaluates to true.
+   */
+  @Override
+  public boolean isBackJump () { 
+    return (conditionValue) && (targetPosition <= position);
+  }
+    
+  /** 
+   * retrieve value of jump condition from operand stack
+   * (not ideal to have this public, but some listeners might need it for
+   * skipping the insn, plus we require it for subclass factorization)
+   */
+  public abstract boolean popConditionValue(StackFrame frame);
+  
+  public Instruction getTarget() {
+    if (target == null) {
+      target = mi.getInstructionAt(targetPosition);
+    }
+    return target;
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    conditionValue = popConditionValue(frame);
+    if (conditionValue) {
+      return getTarget();
+    } else {
+      return getNext(ti);
+    }
+  }
+
+  /**
+   * use this as a delegatee in overridden executes of derived IfInstructions
+   * (e.g. for symbolic execution)
+   */
+  protected Instruction executeBothBranches (SystemState ss, KernelState ks, ThreadInfo ti){
+    if (!ti.isFirstStepInsn()) {
+      BooleanChoiceGenerator cg = new BooleanChoiceGenerator(ti.getVM().getConfig(), "ifAll");
+      if (ss.setNextChoiceGenerator(cg)){
+        return this;
+
+      } else {
+        StackFrame frame = ti.getModifiableTopFrame();
+        // some listener did override the CG, fallback to normal operation
+        conditionValue = popConditionValue(frame);
+        if (conditionValue) {
+          return getTarget();
+        } else {
+          return getNext(ti);
+        }
+      }
+      
+    } else {
+      BooleanChoiceGenerator cg = ss.getCurrentChoiceGenerator("ifAll", BooleanChoiceGenerator.class);
+      assert (cg != null) : "no BooleanChoiceGenerator";
+      
+      StackFrame frame = ti.getModifiableTopFrame();
+      popConditionValue(frame); // we are not interested in concrete values
+      
+      conditionValue = cg.getNextChoice();
+      
+      if (conditionValue) {
+        return getTarget();
+      } else {
+        return getNext(ti);
+      }
+
+    }
+  }
+  
+  @Override
+  public String toString () {
+    return getMnemonic() + " " + targetPosition;
+  }
+  
+  @Override
+  public int getLength() {
+    return 3; // usually opcode, bb1, bb2
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    IfInstruction clone = null;
+
+    try {
+      clone = (IfInstruction) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+
+      clone.target = null;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/InstanceInvocation.java b/src/main/gov/nasa/jpf/jvm/bytecode/InstanceInvocation.java
new file mode 100644 (file)
index 0000000..6b80869
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction;
+
+/**
+ * base class for INVOKEVIRTUAL, INVOKESPECIAL and INVOLEINTERFACE
+ */
+public abstract class InstanceInvocation extends JVMInvokeInstruction implements InstanceInvokeInstruction {
+
+  protected InstanceInvocation() {}
+
+  protected InstanceInvocation (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+  @Override
+  public int getArgSize () {
+    if (argSize < 0) {
+      argSize = Types.getArgumentsSize(signature) + 1; // 'this'
+    }
+
+    return argSize;
+  }
+  
+  @Override
+  public int getCalleeThis (ThreadInfo ti) {
+    if (!ti.isPostExec()){
+      // we have to dig out the 'this' reference from the callers stack
+      return ti.getCalleeThis( getArgSize());
+    } else {
+      // enter() cached it
+      return lastObj;
+    }
+  }
+
+  @Override
+  public int getObjectSlot (StackFrame frame){
+    int top = frame.getTopPos();
+    int argSize = getArgSize();
+    
+    if (argSize == 1){ // object ref is on top
+      return top;
+      
+    } else {
+      return top - argSize -1;
+    }
+  }
+  
+  public ElementInfo getThisElementInfo (ThreadInfo ti) {
+    int thisRef = getCalleeThis(ti);
+    if (thisRef != MJIEnv.NULL) {
+      return ti.getElementInfo(thisRef);
+    } else {
+      return null;
+    }
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/InstructionFactory.java b/src/main/gov/nasa/jpf/jvm/bytecode/InstructionFactory.java
new file mode 100644 (file)
index 0000000..3ede848
--- /dev/null
@@ -0,0 +1,1120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.jvm.JVMInstructionFactory;
+import gov.nasa.jpf.util.Invocation;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativeMethodInfo;
+
+import java.util.List;
+
+/**
+ * this is the new JVMInstructionFactory
+ */
+public class InstructionFactory extends JVMInstructionFactory {
+
+  public InstructionFactory(){
+    // nothing here
+  }
+
+  @Override
+  public Object clone() {
+    try {
+      return super.clone();
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("InstructionFactory " + this.getClass().getName() + " does not support cloning");
+    }
+  }
+
+  //--- the factory methods
+  @Override
+  public Instruction aconst_null() {
+    return new ACONST_NULL();
+  }
+
+  @Override
+  public Instruction aload(int localVarIndex) {
+    return new ALOAD(localVarIndex);
+  }
+
+  @Override
+  public Instruction aload_0() {
+    return new ALOAD(0);
+  }
+
+  @Override
+  public Instruction aload_1() {
+    return new ALOAD(1);
+  }
+
+  @Override
+  public Instruction aload_2() {
+    return new ALOAD(2);
+  }
+
+  @Override
+  public Instruction aload_3() {
+    return new ALOAD(3);
+  }
+
+  @Override
+  public Instruction aaload() {
+    return new AALOAD();
+  }
+
+  @Override
+  public Instruction astore(int localVarIndex) {
+    return new ASTORE(localVarIndex);
+  }
+
+  @Override
+  public Instruction astore_0() {
+    return new ASTORE(0);
+  }
+
+  @Override
+  public Instruction astore_1() {
+    return new ASTORE(1);
+  }
+
+  @Override
+  public Instruction astore_2() {
+    return new ASTORE(2);
+  }
+
+  @Override
+  public Instruction astore_3() {
+    return new ASTORE(3);
+  }
+
+  @Override
+  public Instruction aastore() {
+    return new AASTORE();
+  }
+
+  @Override
+  public Instruction areturn() {
+    return new ARETURN();
+  }
+
+  @Override
+  public Instruction anewarray(String clsName){
+    return new ANEWARRAY(clsName);
+  }
+
+  @Override
+  public Instruction arraylength() {
+    return new ARRAYLENGTH();
+  }
+
+  @Override
+  public Instruction athrow() {
+    return new ATHROW();
+  }
+
+  @Override
+  public Instruction baload() {
+    return new BALOAD();
+  }
+
+  @Override
+  public Instruction bastore() {
+    return new BASTORE();
+  }
+
+  @Override
+  public Instruction bipush(int b) {
+    return new BIPUSH(b);
+  }
+
+  @Override
+  public Instruction caload() {
+    return new CALOAD();
+  }
+
+  @Override
+  public Instruction castore() {
+    return new CASTORE();
+  }
+
+  @Override
+  public Instruction checkcast(String clsName){
+    return new CHECKCAST(clsName);
+  }
+
+  @Override
+  public Instruction d2f() {
+    return new D2F();
+  }
+
+  @Override
+  public Instruction d2i() {
+    return new D2I();
+  }
+
+  @Override
+  public Instruction d2l() {
+    return new D2L();
+  }
+
+  @Override
+  public Instruction dadd() {
+    return new DADD();
+  }
+
+  @Override
+  public Instruction daload() {
+    return new DALOAD();
+  }
+
+  @Override
+  public Instruction dastore() {
+    return new DASTORE();
+  }
+
+  @Override
+  public Instruction dcmpg() {
+    return new DCMPG();
+  }
+
+  @Override
+  public Instruction dcmpl() {
+    return new DCMPL();
+  }
+
+  @Override
+  public Instruction dconst_0() {
+    return new DCONST(0.0);
+  }
+
+  @Override
+  public Instruction dconst_1() {
+    return new DCONST(1.0);
+  }
+
+  @Override
+  public Instruction ddiv() {
+    return new DDIV();
+  }
+
+  @Override
+  public Instruction dload(int localVarIndex) {
+    return new DLOAD(localVarIndex);
+  }
+
+  @Override
+  public Instruction dload_0() {
+    return new DLOAD(0);
+  }
+
+  @Override
+  public Instruction dload_1() {
+    return new DLOAD(1);
+  }
+
+  @Override
+  public Instruction dload_2() {
+    return new DLOAD(2);
+  }
+
+  @Override
+  public Instruction dload_3() {
+    return new DLOAD(3);
+  }
+
+  @Override
+  public Instruction dmul() {
+    return new DMUL();
+  }
+
+  @Override
+  public Instruction dneg() {
+    return new DNEG();
+  }
+
+  @Override
+  public Instruction drem() {
+    return new DREM();
+  }
+
+  @Override
+  public Instruction dreturn() {
+    return new DRETURN();
+  }
+
+  @Override
+  public Instruction dstore(int localVarIndex) {
+    return new DSTORE(localVarIndex);
+  }
+
+  @Override
+  public Instruction dstore_0() {
+    return new DSTORE(0);
+  }
+
+  @Override
+  public Instruction dstore_1() {
+    return new DSTORE(1);
+  }
+
+  @Override
+  public Instruction dstore_2() {
+    return new DSTORE(2);
+  }
+
+  @Override
+  public Instruction dstore_3() {
+    return new DSTORE(3);
+  }
+
+  @Override
+  public Instruction dsub() {
+    return new DSUB();
+  }
+
+  @Override
+  public Instruction dup() {
+    return new DUP();
+  }
+
+  @Override
+  public Instruction dup_x1() {
+    return new DUP_X1();
+  }
+
+  @Override
+  public Instruction dup_x2() {
+    return new DUP_X2();
+  }
+
+  @Override
+  public Instruction dup2() {
+    return new DUP2();
+  }
+
+  @Override
+  public Instruction dup2_x1() {
+    return new DUP2_X1();
+  }
+
+  @Override
+  public Instruction dup2_x2() {
+    return new DUP2_X2();
+  }
+
+  @Override
+  public Instruction f2d() {
+    return new F2D();
+  }
+
+  @Override
+  public Instruction f2i() {
+    return new F2I();
+  }
+
+  @Override
+  public Instruction f2l() {
+    return new F2L();
+  }
+
+  @Override
+  public Instruction fadd() {
+    return new FADD();
+  }
+
+  @Override
+  public Instruction faload() {
+    return new FALOAD();
+  }
+
+  @Override
+  public Instruction fastore() {
+    return new FASTORE();
+  }
+
+  @Override
+  public Instruction fcmpg() {
+    return new FCMPG();
+  }
+
+  @Override
+  public Instruction fcmpl() {
+    return new FCMPL();
+  }
+
+  @Override
+  public Instruction fconst_0() {
+    return new FCONST(0.0f);
+  }
+
+  @Override
+  public Instruction fconst_1() {
+    return new FCONST(1.0f);
+  }
+
+  @Override
+  public Instruction fconst_2() {
+    return new FCONST(2.0f);
+  }
+
+  @Override
+  public Instruction fdiv() {
+    return new FDIV();
+  }
+
+  @Override
+  public Instruction fload(int localVarIndex) {
+    return new FLOAD(localVarIndex);
+  }
+
+  @Override
+  public Instruction fload_0() {
+    return new FLOAD(0);
+  }
+
+  @Override
+  public Instruction fload_1() {
+    return new FLOAD(1);
+  }
+
+  @Override
+  public Instruction fload_2() {
+    return new FLOAD(2);
+  }
+
+  @Override
+  public Instruction fload_3() {
+    return new FLOAD(3);
+  }
+
+  @Override
+  public Instruction fmul() {
+    return new FMUL();
+  }
+
+  @Override
+  public Instruction fneg() {
+    return new FNEG();
+  }
+
+  @Override
+  public Instruction frem() {
+    return new FREM();
+  }
+
+  @Override
+  public Instruction freturn() {
+    return new FRETURN();
+  }
+
+  @Override
+  public Instruction fstore(int localVarIndex) {
+    return new FSTORE(localVarIndex);
+  }
+
+  @Override
+  public Instruction fstore_0() {
+    return new FSTORE(0);
+  }
+
+  @Override
+  public Instruction fstore_1() {
+    return new FSTORE(1);
+  }
+
+  @Override
+  public Instruction fstore_2() {
+    return new FSTORE(2);
+  }
+
+  @Override
+  public Instruction fstore_3() {
+    return new FSTORE(3);
+  }
+
+  @Override
+  public Instruction fsub() {
+    return new FSUB();
+  }
+
+  @Override
+  public Instruction getfield(String fieldName, String clsName, String fieldDescriptor){
+    return new GETFIELD(fieldName, clsName, fieldDescriptor);
+  }
+
+  @Override
+  public Instruction getstatic(String fieldName, String clsName, String fieldDescriptor){
+    return new GETSTATIC(fieldName, clsName, fieldDescriptor);
+  }
+
+
+  @Override
+  public Instruction goto_(int targetPc) {
+    return new GOTO(targetPc);
+  }
+
+  @Override
+  public Instruction goto_w(int targetPc) {
+    return new GOTO_W(targetPc);
+  }
+
+  @Override
+  public Instruction i2b() {
+    return new I2B();
+  }
+
+  @Override
+  public Instruction i2c() {
+    return new I2C();
+  }
+
+  @Override
+  public Instruction i2d() {
+    return new I2D();
+  }
+
+  @Override
+  public Instruction i2f() {
+    return new I2F();
+  }
+
+  @Override
+  public Instruction i2l() {
+    return new I2L();
+  }
+
+  @Override
+  public Instruction i2s() {
+    return new I2S();
+  }
+
+  @Override
+  public Instruction iadd() {
+    return new IADD();
+  }
+
+  @Override
+  public Instruction iaload() {
+    return new IALOAD();
+  }
+
+  @Override
+  public Instruction iand() {
+    return new IAND();
+  }
+
+  @Override
+  public Instruction iastore() {
+    return new IASTORE();
+  }
+
+  @Override
+  public Instruction iconst_m1() {
+    return new ICONST(-1);
+  }
+
+  @Override
+  public Instruction iconst_0() {
+    return new ICONST(0);
+  }
+
+  @Override
+  public Instruction iconst_1() {
+    return new ICONST(1);
+  }
+
+  @Override
+  public Instruction iconst_2() {
+    return new ICONST(2);
+  }
+
+  @Override
+  public Instruction iconst_3() {
+    return new ICONST(3);
+  }
+
+  @Override
+  public Instruction iconst_4() {
+    return new ICONST(4);
+  }
+
+  @Override
+  public Instruction iconst_5() {
+    return new ICONST(5);
+  }
+
+  @Override
+  public Instruction idiv() {
+    return new IDIV();
+  }
+
+  @Override
+  public Instruction if_acmpeq(int targetPc) {
+    return new IF_ACMPEQ(targetPc);
+  }
+
+  @Override
+  public Instruction if_acmpne(int targetPc) {
+    return new IF_ACMPNE(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmpeq(int targetPc) {
+    return new IF_ICMPEQ(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmpne(int targetPc) {
+    return new IF_ICMPNE(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmplt(int targetPc) {
+    return new IF_ICMPLT(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmpge(int targetPc) {
+    return new IF_ICMPGE(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmpgt(int targetPc) {
+    return new IF_ICMPGT(targetPc);
+  }
+
+  @Override
+  public Instruction if_icmple(int targetPc) {
+    return new IF_ICMPLE(targetPc);
+  }
+
+  @Override
+  public Instruction ifeq(int targetPc) {
+    return new IFEQ(targetPc);
+  }
+
+  @Override
+  public Instruction ifne(int targetPc) {
+    return new IFNE(targetPc);
+  }
+
+  @Override
+  public Instruction iflt(int targetPc) {
+    return new IFLT(targetPc);
+  }
+
+  @Override
+  public Instruction ifge(int targetPc) {
+    return new IFGE(targetPc);
+  }
+
+  @Override
+  public Instruction ifgt(int targetPc) {
+    return new IFGT(targetPc);
+  }
+
+  @Override
+  public Instruction ifle(int targetPc) {
+    return new IFLE(targetPc);
+  }
+
+  @Override
+  public Instruction ifnonnull(int targetPc) {
+    return new IFNONNULL(targetPc);
+  }
+
+  @Override
+  public Instruction ifnull(int targetPc) {
+    return new IFNULL(targetPc);
+  }
+
+  @Override
+  public Instruction iinc(int localVarIndex, int incConstant) {
+    return new IINC(localVarIndex, incConstant);
+  }
+
+  @Override
+  public Instruction iload(int localVarIndex) {
+    return new ILOAD(localVarIndex);
+  }
+
+  @Override
+  public Instruction iload_0() {
+    return new ILOAD(0);
+  }
+
+  @Override
+  public Instruction iload_1() {
+    return new ILOAD(1);
+  }
+
+  @Override
+  public Instruction iload_2() {
+    return new ILOAD(2);
+  }
+
+  @Override
+  public Instruction iload_3() {
+    return new ILOAD(3);
+  }
+
+  @Override
+  public Instruction imul() {
+    return new IMUL();
+  }
+
+  @Override
+  public Instruction ineg() {
+    return new INEG();
+  }
+
+  @Override
+  public Instruction instanceof_(String clsName){
+    return new INSTANCEOF(clsName);
+  }
+
+  @Override
+  public Instruction invokeinterface(String clsName, String methodName, String methodSignature){
+    return new INVOKEINTERFACE(clsName, methodName, methodSignature);
+  }
+
+  @Override
+  public Instruction invokespecial(String clsName, String methodName, String methodSignature){
+    return new INVOKESPECIAL(clsName, methodName, methodSignature);
+  }
+
+  @Override
+  public Instruction invokestatic(String clsName, String methodName, String methodSignature){
+    return new INVOKESTATIC(clsName, methodName, methodSignature);
+  }
+
+  @Override
+  public Instruction invokevirtual(String clsName, String methodName, String methodSignature){
+    return new INVOKEVIRTUAL(clsName, methodName, methodSignature);
+  }
+
+  @Override
+  public Instruction invokedynamic(int bootstrapIndex, String samMethodName, String functionType){
+    return new INVOKEDYNAMIC(bootstrapIndex, samMethodName, functionType);
+  }
+
+  @Override
+  public Instruction ior() {
+    return new IOR();
+  }
+
+  @Override
+  public Instruction irem() {
+    return new IREM();
+  }
+
+  @Override
+  public Instruction ireturn() {
+    return new IRETURN();
+  }
+
+  @Override
+  public Instruction ishl() {
+    return new ISHL();
+  }
+
+  @Override
+  public Instruction ishr() {
+    return new ISHR();
+  }
+
+  @Override
+  public Instruction istore(int localVarIndex) {
+    return new ISTORE(localVarIndex);
+  }
+
+  @Override
+  public Instruction istore_0() {
+    return new ISTORE(0);
+  }
+
+  @Override
+  public Instruction istore_1() {
+    return new ISTORE(1);
+  }
+
+  @Override
+  public Instruction istore_2() {
+    return new ISTORE(2);
+  }
+
+  @Override
+  public Instruction istore_3() {
+    return new ISTORE(3);
+  }
+
+  @Override
+  public Instruction isub() {
+    return new ISUB();
+  }
+
+  @Override
+  public Instruction iushr() {
+    return new IUSHR();
+  }
+
+  @Override
+  public Instruction ixor() {
+    return new IXOR();
+  }
+
+  @Override
+  public Instruction jsr(int targetPc) {
+    return new JSR(targetPc);
+  }
+
+  @Override
+  public Instruction jsr_w(int targetPc) {
+    return new JSR_W(targetPc);
+  }
+
+  @Override
+  public Instruction l2d() {
+    return new L2D();
+  }
+
+  @Override
+  public Instruction l2f() {
+    return new L2F();
+  }
+
+  @Override
+  public Instruction l2i() {
+    return new L2I();
+  }
+
+  @Override
+  public Instruction ladd() {
+    return new LADD();
+  }
+
+  @Override
+  public Instruction laload() {
+    return new LALOAD();
+  }
+
+  @Override
+  public Instruction land() {
+    return new LAND();
+  }
+
+  @Override
+  public Instruction lastore() {
+    return new LASTORE();
+  }
+
+  @Override
+  public Instruction lcmp() {
+    return new LCMP();
+  }
+
+  @Override
+  public Instruction lconst_0() {
+    return new LCONST(0);
+  }
+
+  @Override
+  public Instruction lconst_1() {
+    return new LCONST(1L);
+  }
+
+  @Override
+  public Instruction ldc(int v){
+    return new LDC(v);
+  }
+  @Override
+  public Instruction ldc(float v){
+    return new LDC(v);
+  }
+  @Override
+  public Instruction ldc(String v, boolean isClass){
+    return new LDC(v, isClass);
+  }
+
+
+  @Override
+  public Instruction ldc_w(int v){
+    return new LDC_W(v);
+  }
+  @Override
+  public Instruction ldc_w(float v){
+    return new LDC_W(v);
+  }
+  @Override
+  public Instruction ldc_w(String v, boolean isClass){
+    return new LDC_W(v, isClass);
+  }
+
+  @Override
+  public Instruction ldc2_w(long v){
+    return new LDC2_W(v);
+  }
+  @Override
+  public Instruction ldc2_w(double v){
+    return new LDC2_W(v);
+  }
+
+  @Override
+  public Instruction ldiv() {
+    return new LDIV();
+  }
+
+  @Override
+  public Instruction lload(int localVarIndex) {
+    return new LLOAD(localVarIndex);
+  }
+
+  @Override
+  public Instruction lload_0() {
+    return new LLOAD(0);
+  }
+
+  @Override
+  public Instruction lload_1() {
+    return new LLOAD(1);
+  }
+
+  @Override
+  public Instruction lload_2() {
+    return new LLOAD(2);
+  }
+
+  @Override
+  public Instruction lload_3() {
+    return new LLOAD(3);
+  }
+
+  @Override
+  public Instruction lmul() {
+    return new LMUL();
+  }
+
+  @Override
+  public Instruction lneg() {
+    return new LNEG();
+  }
+
+  @Override
+  public Instruction lookupswitch(int defaultTargetPc, int nEntries) {
+    return new LOOKUPSWITCH(defaultTargetPc, nEntries);
+  }
+
+  @Override
+  public Instruction lor() {
+    return new LOR();
+  }
+
+  @Override
+  public Instruction lrem() {
+    return new LREM();
+  }
+
+  @Override
+  public Instruction lreturn() {
+    return new LRETURN();
+  }
+
+  @Override
+  public Instruction lshl() {
+    return new LSHL();
+  }
+
+  @Override
+  public Instruction lshr() {
+    return new LSHR();
+  }
+
+  @Override
+  public Instruction lstore(int localVarIndex) {
+    return new LSTORE(localVarIndex);
+  }
+
+  @Override
+  public Instruction lstore_0() {
+    return new LSTORE(0);
+  }
+
+  @Override
+  public Instruction lstore_1() {
+    return new LSTORE(1);
+  }
+
+  @Override
+  public Instruction lstore_2() {
+    return new LSTORE(2);
+  }
+
+  @Override
+  public Instruction lstore_3() {
+    return new LSTORE(3);
+  }
+
+  @Override
+  public Instruction lsub() {
+    return new LSUB();
+  }
+
+  @Override
+  public Instruction lushr() {
+    return new LUSHR();
+  }
+
+  @Override
+  public Instruction lxor() {
+    return new LXOR();
+  }
+
+  @Override
+  public Instruction monitorenter() {
+    return new MONITORENTER();
+  }
+
+  @Override
+  public Instruction monitorexit() {
+    return new MONITOREXIT();
+  }
+
+  @Override
+  public Instruction multianewarray(String clsName, int dimensions){
+    return new MULTIANEWARRAY(clsName, dimensions);
+  }
+
+  @Override
+  public Instruction new_(String clsName) {
+    return new NEW(clsName);
+  }
+
+  @Override
+  public Instruction newarray(int typeCode) {
+    return new NEWARRAY(typeCode);
+  }
+
+  @Override
+  public Instruction nop() {
+    return new NOP();
+  }
+
+  @Override
+  public Instruction pop() {
+    return new POP();
+  }
+
+  @Override
+  public Instruction pop2() {
+    return new POP2();
+  }
+
+  @Override
+  public Instruction putfield(String fieldName, String clsName, String fieldDescriptor){
+    return new PUTFIELD(fieldName, clsName, fieldDescriptor);
+  }
+
+  @Override
+  public Instruction putstatic(String fieldName, String clsName, String fieldDescriptor){
+    return new PUTSTATIC(fieldName, clsName, fieldDescriptor);
+  }
+
+  @Override
+  public Instruction ret(int localVarIndex) {
+    return new RET(localVarIndex);
+  }
+
+  @Override
+  public Instruction return_() {
+    return new RETURN();
+  }
+
+  @Override
+  public Instruction saload() {
+    return new SALOAD();
+  }
+
+  @Override
+  public Instruction sastore() {
+    return new SASTORE();
+  }
+
+  @Override
+  public Instruction sipush(int val) {
+    return new SIPUSH(val);
+  }
+
+  @Override
+  public Instruction swap() {
+    return new SWAP();
+  }
+
+  @Override
+  public Instruction tableswitch(int defaultTargetPc, int low, int high) {
+    return new TABLESWITCH(defaultTargetPc, low, high);
+  }
+
+  @Override
+  public Instruction wide() {
+    return new WIDE();
+  }
+
+  
+  //--- the JPF specific ones (only used in synthetic methods)
+  @Override
+  public Instruction invokecg(List<Invocation> invokes) {
+    return new INVOKECG(invokes);
+  }
+
+  @Override
+  public Instruction invokeclinit(ClassInfo ci) {
+    return new INVOKECLINIT(ci);
+  }
+
+  @Override
+  public Instruction directcallreturn(){
+    return new DIRECTCALLRETURN();
+  }
+
+  @Override
+  public Instruction executenative(NativeMethodInfo mi){
+    return new EXECUTENATIVE(mi);
+  }
+
+  @Override
+  public Instruction nativereturn(){
+    return new NATIVERETURN();
+  }
+
+  // this is never part of MethodInfo stored code
+  @Override
+  public Instruction runstart(MethodInfo miRun){
+    return new RUNSTART();
+  }
+
+  @Override
+  public Instruction finishclinit(ClassInfo ci) {
+    return new FINISHCLINIT(ci);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JSR.java b/src/main/gov/nasa/jpf/jvm/bytecode/JSR.java
new file mode 100644 (file)
index 0000000..8649b65
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Jump subroutine
+ * ... => ..., address
+ */
+public class JSR extends Instruction implements JVMInstruction {
+  private int target;
+
+  public JSR(int targetPc){
+    target = targetPc;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    int tgtAdr = getNext(ti).getPosition();
+    
+    frame.push( tgtAdr);
+
+    return mi.getInstructionAt(target);
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, bb1, bb2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xA8;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public int getTarget() {
+       return target;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JSR_W.java b/src/main/gov/nasa/jpf/jvm/bytecode/JSR_W.java
new file mode 100644 (file)
index 0000000..b0d2052
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Jump subroutine (wide insnIndex)
+ * ... => ..., address
+ */
+public class JSR_W extends Instruction implements JVMInstruction {
+  protected int target;
+
+  public JSR_W(int targetPc){
+    target = targetPc;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    int tgtAdr = getNext(ti).getPosition();
+    
+    frame.push(tgtAdr);
+
+    return mi.getInstructionAt(target);
+  }
+
+  @Override
+  public int getLength() {
+    return 5; // opcode, bb1, bb2, bb3, bb4
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xC9;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+  public int getTarget() {
+         return target;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMArrayElementInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMArrayElementInstruction.java
new file mode 100644 (file)
index 0000000..0dee592
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ArrayElementInstruction;
+
+/**
+ * abstract class for operations that access elements of arrays
+ */
+public abstract class JVMArrayElementInstruction extends  ArrayElementInstruction {
+  
+  protected int arrayRef;
+  protected int index; // the accessed element
+
+  // we cache these to avoid the need for executeInstruction() listening
+  // if attrs are processed in instructionExecuted()
+  protected Object arrayOperandAttr;
+  protected Object indexOperandAttr;
+  
+  // we need this to be abstract because of the LongArrayStore insns
+  @Override
+  abstract public int peekIndex (ThreadInfo ti);
+  abstract public int peekArrayRef (ThreadInfo ti);
+
+  abstract public Object peekIndexAttr (ThreadInfo ti);
+  abstract public Object peekArrayAttr (ThreadInfo ti);
+
+  public boolean isReferenceArray() {
+    return false;
+  }
+  
+  @Override
+  public ElementInfo getElementInfo (ThreadInfo ti){
+    if (isCompleted(ti)){
+      return ti.getElementInfo(arrayRef);
+    } else {
+      int ref = peekArrayRef(ti);
+      return ti.getElementInfo(arrayRef);
+    }
+  }
+  
+  /**
+   * only makes sense from an executeInstruction() or instructionExecuted() listener,
+   * it is undefined outside of insn exec notifications
+   */
+  public int getArrayRef (ThreadInfo ti){
+    if (ti.isPreExec()){
+      return peekArrayRef(ti);
+    } else {
+      return arrayRef;
+    }
+  }
+
+  public Object getArrayOperandAttr (ThreadInfo ti){
+    if (ti.isPreExec()) {
+      return peekArrayAttr(ti);
+    } else {
+      return arrayOperandAttr;
+    }
+  }
+
+  public Object getIndexOperandAttr (ThreadInfo ti){
+    if (ti.isPreExec()) {
+      return peekIndexAttr(ti);
+    } else {
+      return indexOperandAttr;
+    }
+  }
+
+
+  @Override
+  public ElementInfo peekArrayElementInfo (ThreadInfo ti){
+    int aref = getArrayRef(ti);
+    return ti.getElementInfo(aref);
+  }
+  
+  public int getIndex (ThreadInfo ti){
+    if (!isCompleted(ti)){
+      return peekIndex(ti);
+    } else {
+      return index;
+    }
+  }
+  
+  /**
+   * return size of array elements in stack words (long,double: 2, all other: 1)
+   * e.g. used to determine where the object reference is on the stack
+   * 
+   * should probably be abstract, but there are lots of subclasses and only LongArrayLoad/Store insns have different size
+   */
+  protected int getElementSize () {
+    return 1;
+  }
+  
+  @Override
+  public abstract boolean isRead();
+  
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMFieldInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMFieldInstruction.java
new file mode 100644 (file)
index 0000000..a9dfcc2
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+
+/**
+ * common type for all GET/PUT insns
+ */
+public interface JVMFieldInstruction extends JVMInstruction {
+
+  FieldInfo getFieldInfo();
+  
+  long getLastValue();
+  
+  String getFieldName();
+  
+  boolean isReferenceField();
+
+  ElementInfo peekElementInfo (ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstanceFieldInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstanceFieldInstruction.java
new file mode 100644 (file)
index 0000000..0830e0e
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
+
+/**
+ * common super type of GETFIELD, PUTFIELD
+ */
+public abstract class JVMInstanceFieldInstruction extends InstanceFieldInstruction implements JVMFieldInstruction {
+
+  protected JVMInstanceFieldInstruction (String fieldName, String classType, String fieldDescriptor){
+    super(fieldName, classType, fieldDescriptor);
+  }
+    
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstruction.java
new file mode 100644 (file)
index 0000000..5412010
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.InstructionInterface;
+
+/**
+ * common type for all JVM instructions
+ * 
+ * this can't be a abstract class since this would not allow us to
+ * have machine independent instruction types
+ */
+public interface JVMInstruction extends InstructionInterface {
+  
+       public void accept(JVMInstructionVisitor insVisitor);
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitor.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitor.java
new file mode 100644 (file)
index 0000000..d5305b0
--- /dev/null
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+
+public interface JVMInstructionVisitor {
+
+       public void visit(Instruction ins);
+       public void visit(AALOAD ins);
+       public void visit(AASTORE ins);
+       public void visit(ACONST_NULL ins);
+       public void visit(ALOAD ins);
+       public void visit(ANEWARRAY ins);
+       public void visit(ARETURN ins);
+       public void visit(JVMArrayElementInstruction ins);
+       public void visit(ARRAYLENGTH ins);
+       public void visit(ArrayLoadInstruction ins);
+       public void visit(ArrayStoreInstruction ins);
+       public void visit(ASTORE ins);
+       public void visit(ATHROW ins);
+       public void visit(BALOAD ins);
+       public void visit(BASTORE ins);
+       public void visit(BIPUSH ins);
+       public void visit(CALOAD ins);
+       public void visit(CASTORE ins);
+       public void visit(CHECKCAST ins);
+       public void visit(D2F ins);
+       public void visit(D2I ins);
+       public void visit(D2L ins);
+       public void visit(DADD ins);
+       public void visit(DALOAD ins);
+       public void visit(DASTORE ins);
+       public void visit(DCMPG ins);
+       public void visit(DCMPL ins);
+       public void visit(DCONST ins);
+       public void visit(DDIV ins);
+  public void visit(DIRECTCALLRETURN ins);
+       public void visit(DLOAD ins);
+       public void visit(DMUL ins);
+       public void visit(DNEG ins);
+       public void visit(DREM ins);
+       public void visit(DRETURN ins);
+       public void visit(DSTORE ins);
+       public void visit(DSUB ins);
+       public void visit(DUP_X1 ins);
+       public void visit(DUP_X2 ins);
+       public void visit(DUP ins);
+       public void visit(DUP2_X1 ins);
+       public void visit(DUP2_X2 ins);
+       public void visit(DUP2 ins);
+  public void visit(EXECUTENATIVE ins);
+       public void visit(F2D ins);
+       public void visit(F2I ins);
+       public void visit(FADD ins);
+       public void visit(FALOAD ins);
+       public void visit(FASTORE ins);
+       public void visit(FCMPL ins);
+       public void visit(FCONST ins);
+       public void visit(FDIV ins);
+       public void visit(JVMFieldInstruction ins);
+       public void visit(FLOAD ins);
+       public void visit(FMUL ins);
+       public void visit(FNEG ins);
+       public void visit(FREM ins);
+       public void visit(FRETURN ins);
+       public void visit(FSTORE ins);
+       public void visit(FSUB ins);
+       public void visit(GETFIELD ins);
+       public void visit(GETSTATIC ins);
+       public void visit(GOTO_W ins);
+       public void visit(GOTO ins);
+       public void visit(I2B ins);
+       public void visit(I2C ins);
+       public void visit(I2D ins);
+       public void visit(I2F ins);
+       public void visit(I2L ins);
+       public void visit(I2S ins);
+       public void visit(IADD ins);
+       public void visit(IALOAD ins);
+       public void visit(IAND ins);
+       public void visit(IASTORE ins);
+       public void visit(ICONST ins);
+       public void visit(IDIV ins);
+       public void visit(IF_ACMPEQ ins);
+       public void visit(IF_ACMPNE ins);
+       public void visit(IF_ICMPEQ ins);
+       public void visit(IF_ICMPGE ins);
+       public void visit(IF_ICMPGT ins);
+       public void visit(IF_ICMPLE ins);
+       public void visit(IF_ICMPLT ins);
+       public void visit(IF_ICMPNE ins);
+       public void visit(IFEQ ins);
+       public void visit(IFGE ins);
+       public void visit(IFGT ins);
+       public void visit(IfInstruction ins);
+       public void visit(IFLE ins);
+       public void visit(IFLT ins);
+       public void visit(IFNE ins);
+       public void visit(IFNONNULL ins);
+       public void visit(IFNULL ins);
+       public void visit(IINC ins);
+       public void visit(ILOAD ins);
+       public void visit(IMUL ins);
+       public void visit(INEG ins);
+       public void visit(JVMInstanceFieldInstruction ins);
+       public void visit(InstanceInvocation ins);
+       public void visit(INSTANCEOF ins);
+       public void visit(INVOKECG ins);
+       public void visit(INVOKECLINIT ins);
+       public void visit(JVMInvokeInstruction ins);
+       public void visit(INVOKEINTERFACE ins);
+       public void visit(INVOKESPECIAL ins);
+       public void visit(INVOKESTATIC ins);
+       public void visit(INVOKEVIRTUAL ins);
+       public void visit(IOR ins);
+       public void visit(IREM ins);
+       public void visit(IRETURN ins);
+       public void visit(ISHL ins);
+       public void visit(ISHR ins);
+       public void visit(ISTORE ins);
+       public void visit(ISUB ins);
+       public void visit(IUSHR ins);
+       public void visit(IXOR ins);
+       public void visit(JSR_W ins);
+       public void visit(JSR ins);
+       public void visit(L2D ins);
+       public void visit(L2F ins);
+       public void visit(L2I ins);
+       public void visit(LADD ins);
+       public void visit(LALOAD ins);
+       public void visit(LAND ins);
+       public void visit(LASTORE ins);
+       public void visit(LCMP ins);
+       public void visit(LCONST ins);
+       public void visit(LDC_W ins);
+       public void visit(LDC ins);
+       public void visit(LDC2_W ins);
+       public void visit(LDIV ins);
+       public void visit(LLOAD ins);
+       public void visit(LMUL ins);
+       public void visit(LNEG ins);
+       public void visit(JVMLocalVariableInstruction ins);
+       public void visit(LockInstruction ins);
+       public void visit(LongArrayLoadInstruction ins);
+       public void visit(LongArrayStoreInstruction ins);
+       public void visit(LOOKUPSWITCH ins);
+       public void visit(LOR ins);
+       public void visit(LREM ins);
+       public void visit(LRETURN ins);
+       public void visit(LSHL ins);
+       public void visit(LSHR ins);
+       public void visit(LSTORE ins);
+       public void visit(LSUB ins);
+       public void visit(LUSHR ins);
+       public void visit(LXOR ins);
+       public void visit(MONITORENTER ins);
+       public void visit(MONITOREXIT ins);
+       public void visit(MULTIANEWARRAY ins);
+  public void visit(NATIVERETURN ins);
+       public void visit(NEW ins);
+       public void visit(NEWARRAY ins);
+       public void visit(NOP ins);
+       public void visit(POP ins);
+       public void visit(POP2 ins);
+       public void visit(PUTFIELD ins);
+       public void visit(PUTSTATIC ins);
+       public void visit(RET ins);
+       public void visit(RETURN ins);
+       public void visit(JVMReturnInstruction ins);
+       public void visit(SALOAD ins);
+       public void visit(SASTORE ins);
+       public void visit(SIPUSH ins);
+       public void visit(JVMStaticFieldInstruction ins);
+       /**public void visit(StoreInstruction ins);**/ // neha: this is just an interface, not implemented
+       public void visit(SWAP ins);
+       public void visit(SwitchInstruction ins);
+       public void visit(TABLESWITCH ins);
+       /**public void visit(VariableAccessor ins);**/ // neha: this is just an interface, not implemented
+       public void visit(VirtualInvocation ins);
+       public void visit(WIDE ins);
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitorAdapter.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMInstructionVisitorAdapter.java
new file mode 100644 (file)
index 0000000..24794a8
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+
+public abstract class JVMInstructionVisitorAdapter 
+                                                       implements JVMInstructionVisitor {
+
+  @Override public void visit(Instruction ins) {}
+
+  @Override public void visit(AALOAD ins) {}
+
+  @Override public void visit(AASTORE ins) {}
+
+  @Override public void visit(ACONST_NULL ins) {}
+
+  @Override public void visit(ALOAD ins) {}
+
+  @Override public void visit(ANEWARRAY ins) {}
+
+  @Override public void visit(ARETURN ins) {}
+
+  @Override public void visit(JVMArrayElementInstruction ins) {}
+  
+  @Override public void visit(ARRAYLENGTH ins) {}
+
+  @Override public void visit(ArrayLoadInstruction ins) {}
+
+  @Override public void visit(ArrayStoreInstruction ins) {}
+
+  @Override public void visit(ASTORE ins) {}
+
+  @Override public void visit(ATHROW ins) {}
+
+  @Override public void visit(BALOAD ins) {}
+
+  @Override public void visit(BASTORE ins) {}
+
+  @Override public void visit(BIPUSH ins) {}
+
+  @Override public void visit(CALOAD ins) {}
+
+  @Override public void visit(CASTORE ins) {}
+
+  @Override public void visit(CHECKCAST ins) {}
+
+  @Override public void visit(D2F ins) {}
+
+  @Override public void visit(D2I ins) {}
+
+  @Override public void visit(D2L ins) {}
+
+  @Override public void visit(DADD ins) {}
+
+  @Override public void visit(DALOAD ins) {}
+
+  @Override public void visit(DASTORE ins) {}
+
+  @Override public void visit(DCMPG ins) {}
+
+  @Override public void visit(DCMPL ins) {}
+
+  @Override public void visit(DCONST ins) {}
+
+  @Override public void visit(DDIV ins) {}
+
+  @Override public void visit(DIRECTCALLRETURN ins) {}
+
+  @Override public void visit(DLOAD ins) {}
+
+  @Override public void visit(DMUL ins) {}
+
+  @Override public void visit(DNEG ins) {}
+
+  @Override public void visit(DREM ins) {}
+
+  @Override public void visit(DRETURN ins) {}
+
+  @Override public void visit(DSTORE ins) {}
+
+  @Override public void visit(DSUB ins) {}
+
+  @Override public void visit(DUP_X1 ins) {}
+
+  @Override public void visit(DUP_X2 ins) {}
+
+  @Override public void visit(DUP ins) {}
+
+  @Override public void visit(DUP2_X1 ins) {}
+
+  @Override public void visit(DUP2_X2 ins) {}
+
+  @Override public void visit(DUP2 ins) {}
+
+  @Override public void visit(F2D ins) {}
+
+  @Override public void visit(F2I ins) {}
+
+  @Override public void visit(FADD ins) {}
+
+  @Override public void visit(FALOAD ins) {}
+
+  @Override public void visit(FASTORE ins) {}
+
+  @Override public void visit(FCMPL ins) {}
+
+  @Override public void visit(FCONST ins) {}
+
+  @Override public void visit(FDIV ins) {}
+
+  @Override public void visit(JVMFieldInstruction ins) {}
+
+  @Override public void visit(FLOAD ins) {}
+
+  @Override public void visit(FMUL ins) {}
+
+  @Override public void visit(FNEG ins) {}
+
+  @Override public void visit(FREM ins) {}
+
+  @Override public void visit(FRETURN ins) {}
+
+  @Override public void visit(FSTORE ins) {}
+
+  @Override public void visit(FSUB ins) {}
+
+  @Override public void visit(GETFIELD ins) {}
+
+  @Override public void visit(GETSTATIC ins) {}
+
+  @Override public void visit(GOTO_W ins) {}
+
+  @Override public void visit(GOTO ins) {}
+
+  @Override public void visit(I2B ins) {}
+
+  @Override public void visit(I2C ins) {}
+
+  @Override public void visit(I2D ins) {}
+
+  @Override public void visit(I2F ins) {}
+
+  @Override public void visit(I2L ins) {}
+
+  @Override public void visit(I2S ins) {}
+
+  @Override public void visit(IADD ins) {}
+
+  @Override public void visit(IALOAD ins) {}
+
+  @Override public void visit(IAND ins) {}
+
+  @Override public void visit(IASTORE ins) {}
+
+  @Override public void visit(ICONST ins) {}
+
+  @Override public void visit(IDIV ins) {}
+
+  @Override public void visit(IF_ACMPEQ ins) {}
+
+  @Override public void visit(IF_ACMPNE ins) {}
+
+  @Override public void visit(IF_ICMPEQ ins) {}
+
+  @Override public void visit(IF_ICMPGE ins) {}
+
+  @Override public void visit(IF_ICMPGT ins) {}
+
+  @Override public void visit(IF_ICMPLE ins) {}
+
+  @Override public void visit(IF_ICMPLT ins) {}
+
+  @Override public void visit(IF_ICMPNE ins) {}
+
+  @Override public void visit(IFEQ ins) {}
+
+  @Override public void visit(IFGE ins) {}
+
+  @Override public void visit(IFGT ins) {}
+
+  @Override public void visit(IfInstruction ins) {}
+
+  @Override public void visit(IFLE ins) {}
+
+  @Override public void visit(IFLT ins) {}
+
+  @Override public void visit(IFNE ins) {}
+
+  @Override public void visit(IFNONNULL ins) {}
+
+  @Override public void visit(IFNULL ins) {}
+
+  @Override public void visit(IINC ins) {}
+
+  @Override public void visit(ILOAD ins) {}
+
+  @Override public void visit(IMUL ins) {}
+
+  @Override public void visit(INEG ins) {}
+
+  @Override public void visit(JVMInstanceFieldInstruction ins) {}
+
+  @Override public void visit(InstanceInvocation ins){}
+       
+  @Override public void visit(INSTANCEOF ins){}
+       
+  @Override public void visit(INVOKECG ins){}
+       
+  @Override public void visit(INVOKECLINIT ins){}
+       
+  @Override public void visit(JVMInvokeInstruction ins){}
+       
+  @Override public void visit(INVOKEINTERFACE ins){}
+       
+  @Override public void visit(INVOKESPECIAL ins){}
+       
+  @Override public void visit(INVOKESTATIC ins){}
+       
+  @Override public void visit(INVOKEVIRTUAL ins){}
+
+  @Override public void visit(EXECUTENATIVE ins){}
+       
+  @Override public void visit(IOR ins){}
+       
+  @Override public void visit(IREM ins){}
+       
+  @Override public void visit(IRETURN ins){}
+       
+  @Override public void visit(ISHL ins){}
+       
+  @Override public void visit(ISHR ins){}
+       
+  @Override public void visit(ISTORE ins){}
+       
+  @Override public void visit(ISUB ins){}
+       
+  @Override public void visit(IUSHR ins){}
+       
+  @Override public void visit(IXOR ins){}
+       
+  @Override public void visit(JSR_W ins){}
+       
+  @Override public void visit(JSR ins){}
+       
+  @Override public void visit(L2D ins){}
+       
+  @Override public void visit(L2F ins){}
+       
+  @Override public void visit(L2I ins){}
+       
+  @Override public void visit(LADD ins){}
+       
+  @Override public void visit(LALOAD ins){}
+       
+  @Override public void visit(LAND ins){}
+       
+  @Override public void visit(LASTORE ins){}
+       
+  @Override public void visit(LCMP ins){}
+       
+  @Override public void visit(LCONST ins){}
+       
+  @Override public void visit(LDC_W ins){}
+       
+  @Override public void visit(LDC ins){}
+       
+  @Override public void visit(LDC2_W ins){}
+       
+  @Override public void visit(LDIV ins){}
+       
+  @Override public void visit(LLOAD ins){}
+       
+  @Override public void visit(LMUL ins){}
+       
+  @Override public void visit(LNEG ins){}
+       
+  @Override public void visit(JVMLocalVariableInstruction ins){}
+       
+  @Override public void visit(LockInstruction ins){}
+       
+  @Override public void visit(LongArrayLoadInstruction ins){}
+       
+  @Override public void visit(LongArrayStoreInstruction ins){}
+       
+  @Override public void visit(LOOKUPSWITCH ins){}
+       
+  @Override public void visit(LOR ins){}
+       
+  @Override public void visit(LREM ins){}
+       
+  @Override public void visit(LRETURN ins){}
+       
+  @Override public void visit(LSHL ins){}
+       
+  @Override public void visit(LSHR ins){}
+       
+  @Override public void visit(LSTORE ins){}
+       
+  @Override public void visit(LSUB ins){}
+       
+  @Override public void visit(LUSHR ins){}
+       
+  @Override public void visit(LXOR ins){}
+       
+  @Override public void visit(MONITORENTER ins){}
+       
+  @Override public void visit(MONITOREXIT ins){}
+       
+  @Override public void visit(MULTIANEWARRAY ins){}
+
+  @Override public void visit(NATIVERETURN ins) {}
+       
+  @Override public void visit(NEW ins){}
+       
+  @Override public void visit(NEWARRAY ins){}
+       
+  @Override public void visit(NOP ins){}
+       
+  @Override public void visit(POP ins){}
+       
+  @Override public void visit(POP2 ins){}
+       
+  @Override public void visit(PUTFIELD ins){}
+       
+  @Override public void visit(PUTSTATIC ins){}
+       
+  @Override public void visit(RET ins){}
+       
+  @Override public void visit(RETURN ins){}
+       
+  @Override public void visit(JVMReturnInstruction ins){}
+       
+  @Override public void visit(SALOAD ins){}
+       
+  @Override public void visit(SASTORE ins){}
+       
+  @Override public void visit(SIPUSH ins){}
+       
+  @Override public void visit(JVMStaticFieldInstruction ins){}
+
+  @Override public void visit(SWAP ins){}
+       
+  @Override public void visit(SwitchInstruction ins){}
+       
+  @Override public void visit(TABLESWITCH ins){}
+       
+  @Override public void visit(VirtualInvocation ins){}
+       
+  @Override public void visit(WIDE ins){}
+       
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMInvokeInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMInvokeInstruction.java
new file mode 100644 (file)
index 0000000..3dda5a5
--- /dev/null
@@ -0,0 +1,281 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
+
+
+/**
+ * abstraction for all invoke instructions
+ */
+public abstract class JVMInvokeInstruction extends InvokeInstruction implements JVMInstruction {
+  /* Those are all straight from the class file.
+   * Note that we can't directly resolve to MethodInfo objects because
+   * the corresponding class might not be loaded yet (has to be done
+   * on execution)
+   */
+  protected String cname;
+  protected String mname;
+  protected String signature;
+
+  protected int argSize = -1;
+
+  /** to cache the last callee object */
+  protected int lastObj = Integer.MIN_VALUE;
+
+  /**
+   * watch out - this is only const for static and special invocation
+   * all virtuals will use it only as a cache
+   */
+  protected MethodInfo invokedMethod;
+
+  protected Object[] arguments; // temporary cache for arg values (for listeners)
+
+  protected JVMInvokeInstruction (String clsName, String methodName, String signature){
+    this.cname = Types.getClassNameFromTypeName(clsName);
+    this.signature = signature;
+    this.mname = MethodInfo.getUniqueName(methodName, signature);
+  }
+
+  protected JVMInvokeInstruction () {}
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+  
+  // only useful from post-exec notifications
+  public int getLastObjRef() {
+    return lastObj;
+  }
+
+  /**
+   * this is for explicit initialization (not BCEL)
+   */
+  public void setInvokedMethod (String clsName, String mthName, String sig) {
+    cname = clsName;
+    mname = mthName + sig;
+    signature = sig;
+  }
+
+  /**
+   * be aware of that this might differ from getInvokedMethod(), since it only
+   * denotes the target type info we have at the static point of the call, i.e.
+   * before dynamic dispatching
+   */
+  @Override
+  public String getInvokedMethodClassName() {
+    return cname;
+  }
+
+  @Override
+  public String getInvokedMethodSignature() {
+    return signature;
+  }
+
+  @Override
+  public String getInvokedMethodName () {
+    return mname;
+  }
+
+  public abstract MethodInfo getInvokedMethod (ThreadInfo ti);
+
+  @Override
+  public MethodInfo getInvokedMethod () {
+    if (invokedMethod == null){
+      invokedMethod = getInvokedMethod(ThreadInfo.getCurrentThread());
+    }
+
+    return invokedMethod;
+  }
+
+  @Override
+  public boolean isCompleted(ThreadInfo ti) {
+    Instruction nextPc = ti.getNextPC();
+
+    if (nextPc == null || nextPc == this){
+      return false;
+    }
+
+    if (invokedMethod != null){
+      MethodInfo topMethod = ti.getTopFrame().getMethodInfo();
+      if (invokedMethod.isMJI() && (topMethod == mi)) {
+        // same frame -> this was a native method that already returned
+        return true;
+      }
+
+      if (topMethod == invokedMethod){
+        return true;
+      }
+    }
+
+    // <2do> how do we account for exceptions?
+
+    return false;
+  }
+
+  StackFrame getCallerFrame (ThreadInfo ti, MethodInfo callee) {
+    return ti.getStackFrameExecuting(this, 0);
+  }
+
+  //--- invocation processing
+
+  protected void setupCallee (ThreadInfo ti, MethodInfo callee){
+    ClassInfo ciCaller = callee.getClassInfo();
+    StackFrame frame = ciCaller.createStackFrame( ti, callee);
+    
+    ti.pushFrame(frame);
+    ti.enter();
+  }
+  
+  /**
+   * this is a little helper to find out about call argument values from listeners that
+   * don't want to dig through MethodInfos and Types. Reference arguments are returned as
+   * either ElementInfos or 'null', all others are boxed (i.e. a 'double' is returned as
+   * a 'Double' object).
+   * It goes without saying that this method can only be called during an executeInstruction()
+   * or instructionExecuted() notification for the corresponding JVMInvokeInstruction
+   * We use the caller frame to retrieve the arguments (instead of the locals of
+   * the callee) since that works for both pre- and post-exec notification
+   */
+  public Object[] getArgumentValues (ThreadInfo ti) {    
+    MethodInfo callee = getInvokedMethod(ti);
+    StackFrame frame = getCallerFrame(ti, callee);
+    
+    assert frame != null : "can't find caller stackframe for: " + this;
+    return frame.getCallArguments(ti);
+  }
+
+  public Object[] getArgumentAttrs (ThreadInfo ti) {
+    MethodInfo callee = getInvokedMethod(ti);
+    StackFrame frame = getCallerFrame(ti, callee);
+
+    assert frame != null : "can't find caller stackframe for: " + this;
+    return frame.getArgumentAttrs(callee);
+  }
+  
+  
+  /**
+   * check if there is any argument attr of the specified type
+   * (use this before using any of the more expensive retrievers)
+   */
+  public boolean hasArgumentAttr (ThreadInfo ti, Class<?> type){
+    MethodInfo callee = getInvokedMethod(ti);
+    StackFrame frame = getCallerFrame(ti, callee);
+
+    assert frame != null : "no caller stackframe for: " + this;
+    return frame.hasArgumentAttr(callee,type);
+  }
+
+  /**
+   * do we have a reference argument that has an object attribute?
+   * less efficient, but still without object creation
+   */
+  public boolean hasArgumentObjectAttr (ThreadInfo ti, Class<?> type){
+    MethodInfo callee = getInvokedMethod(ti);
+    StackFrame frame = getCallerFrame(ti, callee);
+
+    assert frame != null : "no caller stackframe for: " + this;
+    return frame.hasArgumentObjectAttr(ti,callee,type);
+  }
+
+  /**
+   * this is slot size, i.e. includes 'this' for InstanceInvocations 
+   */
+  abstract public int getArgSize();
+  
+  public int getReturnType() {
+    return Types.getReturnBuiltinType(signature);
+  }
+
+  public boolean isReferenceReturnType() {
+    int r = Types.getReturnBuiltinType(signature);
+    return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY));
+  }
+
+  public String getReturnTypeName() {
+    return Types.getReturnTypeName(signature);
+  }
+
+  public Object getFieldOrArgumentValue (String id, ThreadInfo ti){
+    Object v = null;
+
+    if ((v = getArgumentValue(id,ti)) == null){
+      v = getFieldValue(id, ti);
+    }
+
+    return v;
+  }
+
+  public abstract Object getFieldValue (String id, ThreadInfo ti);
+
+
+  /**
+   * <2do> - this relies on same order of arguments and LocalVariableTable entries, which
+   * seems to hold for javac, but is not required by the VM spec, which only
+   * says that arguments are stored in consecutive slots starting at 0
+   */
+  public Object getArgumentValue (String id, ThreadInfo ti){
+    MethodInfo mi = getInvokedMethod();
+    LocalVarInfo localVars[] = mi.getLocalVars();
+    Object[] args = getArgumentValues(ti);
+
+    if (localVars != null){
+      int j = mi.isStatic() ? 0 : 1;
+
+      for (int i=0; i<args.length; i++, j++){
+        Object a = args[i];
+        if (id.equals(localVars[j].getName())){
+          return a;
+        }
+      }
+    }
+
+    return null;
+  }
+    
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    JVMInvokeInstruction clone = null;
+
+    try {
+      clone = (JVMInvokeInstruction) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+
+      clone.invokedMethod = null;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMLocalVariableInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMLocalVariableInstruction.java
new file mode 100644 (file)
index 0000000..4048dff
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LocalVarInfo;
+
+/**
+ * class abstracting instructions that access local variables, to keep
+ * track of slot/varname mapping
+ */
+public abstract class JVMLocalVariableInstruction extends Instruction implements JVMInstruction, LocalVariableInstruction {
+
+  protected int index;
+  protected LocalVarInfo lv;
+
+
+  protected JVMLocalVariableInstruction(int index){
+    this.index = index;
+  }
+  
+  @Override
+  public int getLocalVariableSlot(){
+    return index;
+  }
+
+  public int getLocalVariableIndex() {
+    return index;
+  }
+  
+  @Override
+  public LocalVarInfo getLocalVarInfo(){
+    if (lv == null){
+     lv = mi.getLocalVar(index, position+getLength());
+    }
+    return lv;
+  }
+  
+  public String getLocalVariableName () {
+    LocalVarInfo lv = getLocalVarInfo();
+    return (lv == null) ? Integer.toString(index) : lv.getName();
+  }
+  
+  public String getLocalVariableType () {
+    LocalVarInfo lv = getLocalVarInfo();
+    return (lv == null) ? "?" : lv.getType();
+  }
+  
+  /**
+   * return the fully qualified class/method/var name
+   * (don't use this for top-level filtering since it dynamically constructs the name)
+   */
+  @Override
+  public String getVariableId () {
+    return mi.getClassInfo().getName() + '.' + mi.getUniqueName() + '.' + getLocalVariableName();
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+  
+  public abstract String getBaseMnemonic();
+  
+  @Override
+  public String getMnemonic(){
+    String baseMnemonic = getBaseMnemonic();
+    
+    if (index <= 3){
+      return baseMnemonic + '_' + index;
+    } else {
+      return baseMnemonic;
+    }
+  }
+  
+  @Override
+  public String toString(){
+    String baseMnemonic = getBaseMnemonic();
+    
+    if (index <= 3){
+      return baseMnemonic + '_' + index;
+    } else {
+      return baseMnemonic + " " + index;
+    }
+  }
+}
+
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMReturnInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMReturnInstruction.java
new file mode 100644 (file)
index 0000000..b6d997e
--- /dev/null
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
+
+import java.util.Iterator;
+
+
+/**
+ * abstraction for the various return instructions
+ */
+public abstract class JVMReturnInstruction extends ReturnInstruction implements JVMInstruction {
+
+  // to store where we came from
+  protected StackFrame returnFrame;
+
+  abstract public int getReturnTypeSize();
+  abstract protected Object getReturnedOperandAttr(StackFrame frame);
+  
+  // note these are only callable from within the same enter - thread interleavings
+  // would cause races
+  abstract protected void getAndSaveReturnValue (StackFrame frame);
+  abstract protected void pushReturnValue (StackFrame frame);
+
+  public abstract Object getReturnValue(ThreadInfo ti);
+
+  public StackFrame getReturnFrame() {
+    return returnFrame;
+  }
+
+  public void setReturnFrame(StackFrame frame){
+    returnFrame = frame;
+  }
+
+  /**
+   * this is important since keeping the StackFrame alive would be a major
+   * memory leak
+   */
+  @Override
+  public void cleanupTransients(){
+    returnFrame = null;
+  }
+  
+  //--- attribute accessors
+  
+  // the accessors are here to save the client some effort regarding the
+  // return type (slot size).
+  // Since these are all public methods that can be called by listeners,
+  // we stick to the ThreadInfo argument
+  
+  public boolean hasReturnAttr (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.hasOperandAttr();
+  }
+  public boolean hasReturnAttr (ThreadInfo ti, Class<?> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.hasOperandAttr(type);
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   * 
+   * obviously, this only makes sense from an instructionExecuted(), since
+   * the value is pushed during the enter(). Use ObjectList to access values
+   */
+  public Object getReturnAttr (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getOperandAttr();
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   * 
+   * we don't clone since pushing a return value already changed the caller frame
+   */
+  public void setReturnAttr (ThreadInfo ti, Object a){
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.setOperandAttr(a);
+  }
+  
+  public void addReturnAttr (ThreadInfo ti, Object attr){
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.addOperandAttr(attr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getReturnAttr (ThreadInfo ti, Class<T> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getOperandAttr(type);
+  }
+  public <T> T getNextReturnAttr (ThreadInfo ti, Class<T> type, Object prev){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getNextOperandAttr(type, prev);
+  }
+  public Iterator<?> returnAttrIterator (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.operandAttrIterator();
+  }
+  public <T> Iterator<T> returnAttrIterator (ThreadInfo ti, Class<T> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.operandAttrIterator(type);
+  }
+  
+  // -- end attribute accessors --
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    boolean didUnblock = false;
+    
+    if (!ti.isFirstStepInsn()) {
+      didUnblock = ti.leave();  // takes care of unlocking before potentially creating a CG
+    }
+    
+    if (mi.isSynchronized()) {
+      int objref = mi.isStatic() ? mi.getClassInfo().getClassObjectRef() : ti.getThis();
+      ElementInfo ei = ti.getElementInfo(objref);
+
+      if (ei.getLockCount() == 0) {
+        if (ti.getScheduler().setsLockReleaseCG(ti, ei, didUnblock)){
+          return this;
+        }
+      }
+    }
+
+    StackFrame frame = ti.getModifiableTopFrame();
+    returnFrame = frame;
+    Object attr = getReturnedOperandAttr(frame); // the return attr - get this before we pop
+    getAndSaveReturnValue(frame);
+    
+    // note that this is never the first frame, since we start all threads (incl. main)
+    // through a direct call
+    frame = ti.popAndGetModifiableTopFrame();
+
+    // remove args, push return value and continue with next insn
+    // (DirectCallStackFrames don't use this)
+    frame.removeArguments(mi);
+    pushReturnValue(frame);
+
+    if (attr != null) {
+      setReturnAttr(ti, attr);
+    }
+
+    return frame.getPC().getNext();
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+  
+  @Override
+  public String toPostExecString() {
+    return getMnemonic() + " [" + mi.getFullName() + ']';
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/JVMStaticFieldInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/JVMStaticFieldInstruction.java
new file mode 100644 (file)
index 0000000..60741ed
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.bytecode.StaticFieldInstruction;
+
+/**
+ * common super type of GETSTATIC and PUTSTATIC
+ */
+public abstract class JVMStaticFieldInstruction extends StaticFieldInstruction implements JVMFieldInstruction {
+
+  protected JVMStaticFieldInstruction(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    JVMStaticFieldInstruction clone = null;
+
+    try {
+      clone = (JVMStaticFieldInstruction) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+      clone.fi = null; // ClassInfo is going to be different
+      
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/L2D.java b/src/main/gov/nasa/jpf/jvm/bytecode/L2D.java
new file mode 100644 (file)
index 0000000..720ad2b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert long to double
+ * ..., value => ..., result
+ */
+public class L2D extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v = frame.popLong();
+    frame.pushDouble( v);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x8A;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/L2F.java b/src/main/gov/nasa/jpf/jvm/bytecode/L2F.java
new file mode 100644 (file)
index 0000000..10b961a
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert long to float
+ * ..., value => ..., result
+ */
+public class L2F extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v = frame.popLong();
+    frame.pushFloat( v);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x89;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/L2I.java b/src/main/gov/nasa/jpf/jvm/bytecode/L2I.java
new file mode 100644 (file)
index 0000000..9c32e9b
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Convert long to int
+ * ..., value => ..., result
+ */
+public class L2I extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v = frame.popLong();
+    frame.push( (int)v);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x88;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LADD.java b/src/main/gov/nasa/jpf/jvm/bytecode/LADD.java
new file mode 100644 (file)
index 0000000..a60adbc
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Add long
+ * ..., value1, value2 => ..., result
+ */
+public class LADD extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v1 + v2;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x61;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/LALOAD.java
new file mode 100644 (file)
index 0000000..889d03f
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load long from array
+ * ..., arrayref, index => ..., value
+ */
+public class LALOAD extends LongArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    long value = ei.getLongElement(index);
+    frame.pushLong( value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x2F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LAND.java b/src/main/gov/nasa/jpf/jvm/bytecode/LAND.java
new file mode 100644 (file)
index 0000000..3fa57f9
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean AND long
+ * ..., value1, value2 => ..., result
+ */
+public class LAND extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v1 & v2;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x7F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/LASTORE.java
new file mode 100644 (file)
index 0000000..fc6fba6
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * Store into long array
+ * ..., arrayref, index, value => ...
+ */
+public class LASTORE extends LongArrayStoreInstruction {
+
+  long value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = frame.popLong();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setLongElement(index, value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x50;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LCMP.java b/src/main/gov/nasa/jpf/jvm/bytecode/LCMP.java
new file mode 100644 (file)
index 0000000..1fcaea3
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Compare long
+ * ..., value1, value2 => ..., result
+ */
+public class LCMP extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    int condVal = conditionValue( v1, v2);
+    
+    frame.push(condVal);
+
+    return getNext(ti);
+  }
+  
+  protected int conditionValue(long v1, long v2) {
+      if (v1 == v2) {
+        return 0;
+      } else if (v2 > v1) {
+        return 1;
+      } else {
+        return -1;
+      }
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x94;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LCONST.java b/src/main/gov/nasa/jpf/jvm/bytecode/LCONST.java
new file mode 100644 (file)
index 0000000..1bc982f
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push long constant
+ * ... => ..., <l>
+ */
+public class LCONST extends Instruction implements JVMInstruction {
+  protected long value;
+
+
+  public LCONST(long value){
+    this.value = value;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLong(value);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    if (value == 0) {
+      return 0x09;
+    } else {
+      return 0x0a;
+    }
+  }
+  
+  @Override
+  public String getMnemonic () {
+    if (value == 0) {
+      return "lconst_0";
+    } else {
+      return "lconst_1";
+    }    
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public long getValue() {
+         return value;
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LDC.java b/src/main/gov/nasa/jpf/jvm/bytecode/LDC.java
new file mode 100644 (file)
index 0000000..73d3971
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Push item from runtime constant pool
+ * ... => ..., value
+ */
+public class LDC extends Instruction implements JVMInstruction {
+
+  public enum Type {STRING, CLASS, INT, FLOAT};
+
+  Type type;
+
+  protected String  string;  // the string value if Type.STRING, classname if Type.CLASS
+  protected int     value;
+
+  public LDC() {}
+
+  public LDC (String s, boolean isClass){
+    if (isClass){
+      string = Types.getClassNameFromTypeName(s);
+      type = Type.CLASS;
+    } else {
+      string = s;
+      type = Type.STRING;
+    }
+  }
+
+  public LDC (int v){
+    value = v;
+    type = Type.INT;
+  }
+
+  public LDC (float f){
+    value = Float.floatToIntBits(f);
+    type = Type.FLOAT;
+  }
+
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    switch (type){
+      case STRING:
+        // too bad we can't cache it, since location might change between different paths
+        ElementInfo eiValue = ti.getHeap().newInternString(string, ti); 
+        value = eiValue.getObjectRef();
+        frame.pushRef(value);
+        break;
+
+      case INT:
+      case FLOAT:
+        frame.push(value);
+        break;
+
+      case CLASS:
+        ClassInfo ci;
+        // resolve the referenced class
+        try {
+          ci = ti.resolveReferencedClass(string);
+        } catch(LoadOnJPFRequired lre) {
+          return frame.getPC();
+        }
+
+        // LDC doesn't cause a <clinit> - we only register all required classes
+        // to make sure we have class objects. <clinit>s are called prior to
+        // GET/PUT or INVOKE
+        if (!ci.isRegistered()) {
+          ci.registerClass(ti);
+        }
+
+        frame.pushRef( ci.getClassObjectRef());
+
+        break;
+    }
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 2; // opcode, index
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x12;
+  }
+  
+  public int getValue() {
+    return value;
+  }
+  
+  public Type getType() {
+    return type;
+  }
+  
+  public boolean isString() {
+    return (type == Type.STRING);
+  }
+  
+  public float getFloatValue(){
+         if(type!=Type.FLOAT){
+      throw new IllegalStateException();
+         }
+    
+         return Float.intBitsToFloat(value);
+       }
+
+  public String getStringValue() { // if it is a String (not acquired from the class const pool)
+    if (type == Type.STRING) {
+      return string;
+    } else {
+      return null;
+    }
+  }
+  
+  public String getClassValue() { // if it is the name of a Class (acquired from the class const pool)
+           if (type == Type.CLASS) {
+             return string;
+           } else {
+             return null;
+           }
+         }
+
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LDC2_W.java b/src/main/gov/nasa/jpf/jvm/bytecode/LDC2_W.java
new file mode 100644 (file)
index 0000000..1a9a37a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push long or double from runtime constant pool (wide index)
+ * ... => ..., value
+ */
+public class LDC2_W extends Instruction implements JVMInstruction {
+
+  public enum Type {LONG, DOUBLE};
+
+  protected Type type;
+  protected long value;
+
+  public LDC2_W(long l){
+    value = l;
+    type = Type.LONG;
+  }
+
+  public LDC2_W(double d){
+    value = Double.doubleToLongBits(d);
+    type = Type.DOUBLE;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLong(value);
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x14;
+  }
+  
+  public Type getType() {
+    return type;
+  }
+  
+  public double getDoubleValue(){
+         if(type!=Type.DOUBLE){
+                 throw new IllegalStateException();
+         }
+    
+         return Double.longBitsToDouble(value);
+  }
+  
+  public long getValue() {
+    return value;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LDC_W.java b/src/main/gov/nasa/jpf/jvm/bytecode/LDC_W.java
new file mode 100644 (file)
index 0000000..1ad0a78
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+/**
+ * Push item from runtime constant pool (wide index)
+ * ... => ..., value
+ */
+public class LDC_W extends LDC {
+
+  public LDC_W() {}
+
+  public LDC_W (String s, boolean isClass){
+    super(s,isClass);
+  }
+
+  public LDC_W (int v){
+    super(v);
+  }
+
+  public LDC_W (float f){
+    super(f);
+  }
+
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x13;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LDIV.java b/src/main/gov/nasa/jpf/jvm/bytecode/LDIV.java
new file mode 100644 (file)
index 0000000..e21a035
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Divide long
+ * ..., value1, value2 => ..., result
+ */
+public class LDIV extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+
+    if (v1 == 0) {
+      return ti.createAndThrowException("java.lang.ArithmeticException", "long division by zero");
+    }
+    
+    long r = v2 / v1;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x6D;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LLOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/LLOAD.java
new file mode 100644 (file)
index 0000000..8d9ec48
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Load long from local variable
+ * ... => ..., value
+ */
+public class LLOAD extends JVMLocalVariableInstruction {
+
+  public LLOAD(int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pushLongLocal(index);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+      case 0: return 0x1e;
+      case 1: return 0x1f;
+      case 2: return 0x20;
+      case 3: return 0x21;
+    }
+    
+    return 0x16; // ?? wide
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "lload";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LMUL.java b/src/main/gov/nasa/jpf/jvm/bytecode/LMUL.java
new file mode 100644 (file)
index 0000000..b80643e
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Multiply long
+ * ..., value1, value2 => ..., result
+ */
+public class LMUL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v1 * v2;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x69;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LNEG.java b/src/main/gov/nasa/jpf/jvm/bytecode/LNEG.java
new file mode 100644 (file)
index 0000000..d12a2d5
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Negate long
+ * ..., value => ..., result
+ */
+public class LNEG extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v = frame.popLong();    
+    frame.pushLong(-v);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x75;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LOOKUPSWITCH.java b/src/main/gov/nasa/jpf/jvm/bytecode/LOOKUPSWITCH.java
new file mode 100644 (file)
index 0000000..18d1894
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+
+/**
+ * Access jump table by key match and jump
+ * ..., key => ...
+ */
+public class LOOKUPSWITCH extends SwitchInstruction implements gov.nasa.jpf.vm.bytecode.LookupSwitchInstruction {
+
+  public LOOKUPSWITCH (int defaultTarget, int numberOfTargets) {
+    super(defaultTarget, numberOfTargets);
+  }
+
+  @Override
+  public void setTarget (int index, int match, int target){
+    targets[index] = target;
+    matches[index] = match;
+  }
+
+
+  @Override
+  public int getLength() {
+    return 10 + 2*(matches.length); // <2do> NOT RIGHT: padding!!
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xAB;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LOR.java b/src/main/gov/nasa/jpf/jvm/bytecode/LOR.java
new file mode 100644 (file)
index 0000000..1f99d96
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean OR long
+ * ..., value1, value2 => ..., result
+ */
+public class LOR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v1 | v2;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x81;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LREM.java b/src/main/gov/nasa/jpf/jvm/bytecode/LREM.java
new file mode 100644 (file)
index 0000000..7c64f8b
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Remainder long
+ * ..., value1, value2 => ..., result
+ */
+public class LREM extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v2 % v1;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x71;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LRETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/LRETURN.java
new file mode 100644 (file)
index 0000000..c41b915
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Return long from method
+ * ..., value => [empty]
+ */
+public class LRETURN extends LongReturn {
+
+  public long getReturnValue () {
+    return ret;
+  }
+
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    if (!isCompleted(ti)) { // we have to pull it from the operand stack
+      StackFrame frame = ti.getTopFrame();      
+      ret = frame.peekLong();
+    }
+
+    return new Long(ret);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xAD;
+  }
+
+  @Override
+  public String toString() {
+    return "lreturn " + mi.getFullName();
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LSHL.java b/src/main/gov/nasa/jpf/jvm/bytecode/LSHL.java
new file mode 100644 (file)
index 0000000..3322ce4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Shift left
+ * ..., value1, value2  =>..., result
+ */
+public class LSHL extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    long v2 = frame.popLong();
+
+    frame.pushLong(v2 << v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x79;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LSHR.java b/src/main/gov/nasa/jpf/jvm/bytecode/LSHR.java
new file mode 100644 (file)
index 0000000..03c55c9
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Arithmetic shift right long
+ * ..., value1, value2  =>..., result
+ */
+public class LSHR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int v1 = frame.pop();
+    long v2 = frame.popLong();
+
+    frame.pushLong(v2 >> v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x7B;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LSTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/LSTORE.java
new file mode 100644 (file)
index 0000000..7fa122f
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Store long into local variable
+ * ..., value => ...
+ */
+public class LSTORE extends JVMLocalVariableInstruction implements StoreInstruction {
+
+  public LSTORE(int localVarIndex){
+    super(localVarIndex);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.storeLongOperand(index);
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    if (index > 3){
+      return 2; // opcode, index
+    } else {
+      return 1;
+    }
+  }
+  
+  @Override
+  public int getByteCode () {
+    switch (index) {
+    case 0: return 0x3f;
+    case 1: return 0x40;
+    case 2: return 0x41;
+    case 3: return 0x42;
+    }
+    return 0x37; // ?? wide, LSTORE_n
+  }
+  
+  @Override
+  public String getBaseMnemonic() {
+    return "lstore";
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LSUB.java b/src/main/gov/nasa/jpf/jvm/bytecode/LSUB.java
new file mode 100644 (file)
index 0000000..9990348
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Subtract long
+ * ..., value1, value2 => ..., result
+ */
+public class LSUB extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v2 - v1;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x65;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LUSHR.java b/src/main/gov/nasa/jpf/jvm/bytecode/LUSHR.java
new file mode 100644 (file)
index 0000000..2ad1797
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Logical shift right long
+ * ..., value1, value2  =>..., result
+ */
+public class LUSHR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int  v1 = frame.pop();
+    long v2 = frame.popLong();
+
+    frame.pushLong(v2 >>> v1);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x7D;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LXOR.java b/src/main/gov/nasa/jpf/jvm/bytecode/LXOR.java
new file mode 100644 (file)
index 0000000..79514af
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Boolean XOR long
+ * ..., value1, value2 => ..., result
+ */
+public class LXOR extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    long v1 = frame.popLong();
+    long v2 = frame.popLong();
+    
+    long r = v1 ^ v2;
+    
+    frame.pushLong(r);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x83;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LockInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/LockInstruction.java
new file mode 100644 (file)
index 0000000..9187c8f
--- /dev/null
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+
+
+/**
+ * common root for MONITORENTER/EXIT
+ */
+public abstract class LockInstruction extends Instruction implements JVMInstruction {
+  int lastLockRef = MJIEnv.NULL;
+
+  /**
+    * only useful post-execution (in an instructionExecuted() notification)
+    */
+  public int getLastLockRef () {
+    return lastLockRef;
+  }
+
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(" @");
+    sb.append( Integer.toHexString(lastLockRef));
+    
+    return sb.toString();
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LongArrayLoadInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/LongArrayLoadInstruction.java
new file mode 100644 (file)
index 0000000..350214b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * abstraction for long array loads
+ */
+public abstract class LongArrayLoadInstruction extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo e, int index)
+                throws ArrayIndexOutOfBoundsExecutiveException {
+    e.checkArrayBounds(index);
+    frame.pushLong(e.getLongElement(index));
+  }
+  
+  @Override
+  protected int getElementSize () {
+    return 2;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LongArrayStoreInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/LongArrayStoreInstruction.java
new file mode 100644 (file)
index 0000000..a962cfa
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * absraction for long array stores
+ *
+ * ... array, index, long-value => ...
+ */
+public abstract class LongArrayStoreInstruction extends ArrayStoreInstruction {
+  protected void setField (ElementInfo e, int index, long value)
+                    throws ArrayIndexOutOfBoundsExecutiveException {
+    e.checkArrayBounds(index);
+    e.setLongElement(index, value);
+  }
+
+  @Override
+  protected int getElementSize () {
+    return 2;
+  }
+
+  protected long getValue (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();    
+    return frame.popLong();
+  }
+  
+  @Override
+  public int peekArrayRef(ThreadInfo ti) {
+    return ti.getTopFrame().peek(3);  // ..,ref,idx,long(value)
+  }
+
+  @Override
+  public int peekIndex(ThreadInfo ti){
+    return ti.getTopFrame().peek(2);
+  }
+
+  @Override
+  public Object  peekArrayAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr(3);
+  }
+
+  @Override
+  public Object peekIndexAttr (ThreadInfo ti){
+    return ti.getTopFrame().getOperandAttr(2);
+  }
+
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/LongReturn.java b/src/main/gov/nasa/jpf/jvm/bytecode/LongReturn.java
new file mode 100644 (file)
index 0000000..5fe236e
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.Iterator;
+
+/**
+ * common base for DRETURN and LRETURN
+ */
+public abstract class LongReturn extends JVMReturnInstruction {
+
+  protected long ret;
+  
+  @Override
+  public int getReturnTypeSize() {
+    return 2;
+  }
+  
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return frame.getLongOperandAttr();
+  }
+  
+  @Override
+  protected void getAndSaveReturnValue (StackFrame frame) {
+    ret = frame.popLong();
+  }
+
+  @Override
+  protected void pushReturnValue (StackFrame frame) {
+    frame.pushLong(ret);
+  }
+
+  //--- attribute accessors 
+  
+  @Override
+  public boolean hasReturnAttr (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.hasLongOperandAttr();
+  }
+  @Override
+  public boolean hasReturnAttr (ThreadInfo ti, Class<?> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.hasLongOperandAttr(type);
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   * 
+   * obviously, this only makes sense from an instructionExecuted(), since
+   * the value is pushed during the enter(). Use ObjectList to access values
+   */
+  @Override
+  public Object getReturnAttr (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getLongOperandAttr();
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   * 
+   * we don't clone since pushing a return value already changed the caller frame
+   */
+  @Override
+  public void setReturnAttr (ThreadInfo ti, Object a){
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.setLongOperandAttr(a);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  @Override
+  public <T> T getReturnAttr (ThreadInfo ti, Class<T> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getLongOperandAttr(type);
+  }
+  @Override
+  public <T> T getNextReturnAttr (ThreadInfo ti, Class<T> type, Object prev){
+    StackFrame frame = ti.getTopFrame();
+    return frame.getNextLongOperandAttr(type, prev);
+  }
+  @Override
+  public Iterator returnAttrIterator (ThreadInfo ti){
+    StackFrame frame = ti.getTopFrame();
+    return frame.longOperandAttrIterator();
+  }
+  @Override
+  public <T> Iterator<T> returnAttrIterator (ThreadInfo ti, Class<T> type){
+    StackFrame frame = ti.getTopFrame();
+    return frame.longOperandAttrIterator(type);
+  }
+  
+  @Override
+  public void addReturnAttr (ThreadInfo ti, Object attr){
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.addLongOperandAttr(attr);
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/MONITORENTER.java b/src/main/gov/nasa/jpf/jvm/bytecode/MONITORENTER.java
new file mode 100644 (file)
index 0000000..b365c5e
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Enter monitor for object
+ * ..., objectref => ...
+ */
+public class MONITORENTER extends LockInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    Scheduler scheduler = ti.getScheduler();
+    StackFrame frame = ti.getTopFrame();
+
+    int objref = frame.peek();      // Don't pop yet before we know we really enter
+    if (objref == MJIEnv.NULL){
+      return ti.createAndThrowException("java.lang.NullPointerException", "Attempt to acquire lock for null object");
+    }
+
+    lastLockRef = objref;
+    ElementInfo ei = ti.getModifiableElementInfo(objref);    
+    ei = scheduler.updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
+    
+    if (!ti.isLockOwner(ei)){ // we only need to register, block and/or reschedule if this is not a recursive lock
+      if (ei.canLock(ti)) {
+        // record that this thread would lock the object upon next execution if we break the transition
+        // (note this doesn't re-add if already registered)
+        ei.registerLockContender(ti);
+        if (scheduler.setsLockAcquisitionCG(ti, ei)) { // optional scheduling point
+          return this;
+        }
+        
+      } else { // we need to block
+        ei.block(ti); // this means we only re-execute once we can acquire the lock
+        if (scheduler.setsBlockedThreadCG(ti, ei)){ // mandatory scheduling point
+          return this;
+        }
+        throw new JPFException("blocking MONITORENTER without transition break");            
+      }
+    }
+    
+    //--- bottom half or lock acquisition succeeded without transition break
+    frame = ti.getModifiableTopFrame(); // now we need to modify it
+    frame.pop();
+    
+    ei.lock(ti);  // mark object as locked, increment the lockCount, and set the thread as owner
+    return getNext(ti);
+  }  
+
+  @Override
+  public int getByteCode () {
+    return 0xC2;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/MONITOREXIT.java b/src/main/gov/nasa/jpf/jvm/bytecode/MONITOREXIT.java
new file mode 100644 (file)
index 0000000..c6ea604
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * Exit monitor for object 
+ * ..., objectref => ... 
+ */
+public class MONITOREXIT extends LockInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    boolean didUnblock = false;
+    StackFrame frame = ti.getTopFrame();
+    Scheduler scheduler = ti.getScheduler();
+    
+    int objref = frame.peek();
+    if (objref == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException", "attempt to release lock for null object");
+    }
+
+    lastLockRef = objref;
+    ElementInfo ei = ti.getElementInfo(objref);
+    ei = scheduler.updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
+    
+    if (!ti.isFirstStepInsn()){
+      ei = ei.getModifiableInstance();
+      // we only do this in the top half of the first execution, but before potentially creating
+      // a CG so that blocked threads are runnable again
+      didUnblock = ei.unlock(ti);
+    }
+    
+    if (ei.getLockCount() == 0) { // might still be recursively locked, which wouldn't be a release
+      if (scheduler.setsLockReleaseCG(ti, ei, didUnblock)){ // scheduling point
+        return this;
+      }
+    }
+
+    // bottom half or monitorexit proceeded
+    frame = ti.getModifiableTopFrame();
+    frame.pop();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xC3;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/MULTIANEWARRAY.java b/src/main/gov/nasa/jpf/jvm/bytecode/MULTIANEWARRAY.java
new file mode 100644 (file)
index 0000000..fb2c81f
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Create new multidimensional array
+ * ..., count1, [count2, ...] => ..., arrayref
+ */
+public class MULTIANEWARRAY extends Instruction implements JVMInstruction {
+  protected String type;
+  
+  protected int dimensions;
+  protected int[] arrayLengths;
+
+  public MULTIANEWARRAY (String typeName, int dimensions){
+    this.type = Types.getClassNameFromTypeName(typeName);
+    this.dimensions = dimensions;
+  }
+
+  public static int allocateArray (Heap heap, String type, int[] dim, ThreadInfo ti, int d) {
+    int l = dim[d];
+    ElementInfo eiArray = heap.newArray(type.substring(d + 1), l, ti);
+
+    if (dim.length > (d + 1)) {
+      for (int i = 0; i < l; i++) {
+        eiArray.setReferenceElement(i, allocateArray(heap, type, dim, ti, d + 1));
+      }
+    }
+
+    return eiArray.getObjectRef();
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    String compType = Types.getComponentTerminal(type);
+
+    // resolve the component class first
+    if(Types.isReferenceSignature(type)) {
+      try {
+        ti.resolveReferencedClass(compType);
+      } catch(LoadOnJPFRequired lre) {
+        return ti.getPC();
+      }
+    }
+
+    arrayLengths = new int[dimensions];
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    for (int i = dimensions - 1; i >= 0; i--) {
+      arrayLengths[i] = frame.pop();
+    }
+
+    // there is no clinit for array classes, but we still have  to create a class object
+    // since its a builtin class, we also don't have to bother with NoClassDefFoundErrors
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(type);
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+      ci.setInitialized();
+    }
+    
+    int arrayRef = allocateArray(ti.getHeap(), type, arrayLengths, ti, 0);
+
+    // put the result (the array reference) on the stack
+    frame.pushRef(arrayRef);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 4; // opcode, index1, index2, dimensions
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xC5;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public String getType(){
+    return type;
+  }
+  
+  public int getDimensions() {
+    return dimensions;
+  }
+  
+  public int getArrayLength (int dimension){
+    if (dimension < dimensions && arrayLengths != null){
+      return arrayLengths[dimension];
+    } else {
+      return -1;
+    }
+  }
+  
+  @Override
+  public void cleanupTransients(){
+    arrayLengths= null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/NATIVERETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/NATIVERETURN.java
new file mode 100644 (file)
index 0000000..95682ef
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.NativeStackFrame;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+/**
+ * synthetic return instruction for native method invocations, so that
+ * we don't have to do special provisions to copy the caller args in case
+ * a post exec listener wants them.
+ */
+public class NATIVERETURN extends JVMReturnInstruction {
+
+  Object ret;
+  Object retAttr;
+  Byte retType;
+
+  // this is more simple than a normal JVMReturnInstruction because NativeMethodInfos
+  // are not synchronized, and NativeStackFrames are never the first frame in a thread
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    if (!ti.isFirstStepInsn()) {
+      ti.leave();  // takes care of unlocking before potentially creating a CG
+      // NativeMethodInfo is never synchronized, so no thread CG here
+    }
+
+    StackFrame frame = ti.getModifiableTopFrame();    
+    getAndSaveReturnValue(frame);
+
+    // NativeStackFrame can never can be the first stack frame, so no thread CG
+
+    frame = ti.popAndGetModifiableTopFrame();
+
+    // remove args, push return value and continue with next insn
+    frame.removeArguments(mi);
+    pushReturnValue(frame);
+
+    if (retAttr != null) {
+      setReturnAttr(ti, retAttr);
+    }
+
+    if (mi.isClinit()) {
+      // this is in the clinit RETURN insn for non-MJIs so we have to duplicate here
+      // Duplication could be avoided in DIRECTCALLRETURN, but there is no reliable
+      // way to check if the direct call did return from a clinit since the corresponding
+      // synthetic method could do anything
+      mi.getClassInfo().setInitialized();
+    }
+
+    return frame.getPC().getNext();
+  }
+
+  @Override
+  public void cleanupTransients(){
+    ret = null;
+    retAttr = null;
+    returnFrame = null;
+  }
+  
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  public static final int OPCODE = 260;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  protected void getAndSaveReturnValue (StackFrame frame) {
+    // it's got to be a NativeStackFrame since this insn is created by JPF
+    NativeStackFrame nativeFrame = (NativeStackFrame)frame;
+
+    returnFrame = nativeFrame;
+
+    ret = nativeFrame.getReturnValue();
+    retAttr = nativeFrame.getReturnAttr();
+    retType = nativeFrame.getMethodInfo().getReturnTypeCode();
+  }
+
+  @Override
+  public int getReturnTypeSize() {
+    switch (retType) {
+    case Types.T_BOOLEAN:
+    case Types.T_BYTE:
+    case Types.T_CHAR:
+    case Types.T_SHORT:
+    case Types.T_INT:
+    case Types.T_FLOAT:
+      return 1;
+      
+    case Types.T_LONG:
+    case Types.T_DOUBLE:
+      return 2;
+
+    default:
+      return 1;
+    }
+  }
+
+  // this is only called internally right before we return
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return retAttr;
+  }
+
+  // <2do> this should use the getResult..() methods of NativeStackFrame
+  
+  @Override
+  protected void pushReturnValue (StackFrame fr) {
+    int  ival;
+    long lval;
+    int  retSize = 1;
+
+    // in case of a return type mismatch, we get a ClassCastException, which
+    // is handled in executeMethod() and reported as a InvocationTargetException
+    // (not completely accurate, but we rather go with safety)
+    if (ret != null) {
+      switch (retType) {
+      case Types.T_BOOLEAN:
+        ival = Types.booleanToInt(((Boolean) ret).booleanValue());
+        fr.push(ival);
+        break;
+
+      case Types.T_BYTE:
+        fr.push(((Byte) ret).byteValue());
+        break;
+
+      case Types.T_CHAR:
+        fr.push(((Character) ret).charValue());
+        break;
+
+      case Types.T_SHORT:
+        fr.push(((Short) ret).shortValue());
+        break;
+
+      case Types.T_INT:
+        fr.push(((Integer) ret).intValue());
+        break;
+
+      case Types.T_LONG:
+        fr.pushLong(((Long)ret).longValue());
+        retSize=2;
+        break;
+
+      case Types.T_FLOAT:
+        ival = Types.floatToInt(((Float) ret).floatValue());
+        fr.push(ival);
+        break;
+
+      case Types.T_DOUBLE:
+        lval = Types.doubleToLong(((Double) ret).doubleValue());
+        fr.pushLong(lval);
+        retSize=2;
+        break;
+
+      default:
+        // everything else is supposed to be a reference
+        fr.push(((Integer) ret).intValue(), true);
+      }
+
+      if (retAttr != null) {
+        if (retSize == 1) {
+          fr.setOperandAttr(retAttr);
+        } else {
+          fr.setLongOperandAttr(retAttr);
+        }
+      }
+    }
+  }
+
+  @Override
+  public Object getReturnAttr (ThreadInfo ti) {
+    if (isCompleted(ti)){
+      return retAttr;
+    } else {
+      NativeStackFrame nativeFrame = (NativeStackFrame) ti.getTopFrame();
+      return nativeFrame.getReturnAttr();
+    }
+  }
+
+
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    if (isCompleted(ti)){
+      return ret;
+    } else {
+      NativeStackFrame nativeFrame = (NativeStackFrame) ti.getTopFrame();
+      return nativeFrame.getReturnValue();
+    }
+  }
+
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append("nativereturn ");
+    sb.append(mi.getFullName());
+
+    return sb.toString();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/NEW.java b/src/main/gov/nasa/jpf/jvm/bytecode/NEW.java
new file mode 100644 (file)
index 0000000..482c21a
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.NewInstruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Create new object
+ * ... => ..., objectref
+ */
+public class NEW extends NewInstruction implements JVMInstruction  {
+  protected String cname;
+  protected int newObjRef = MJIEnv.NULL;
+
+  public NEW (String clsDescriptor){
+    cname = Types.getClassNameFromTypeName(clsDescriptor);
+  }
+  
+  public String getClassName(){    // Needed for Java Race Finder
+    return(cname);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    Heap heap = ti.getHeap();
+    ClassInfo ci;
+
+    // resolve the referenced class
+    try {
+      ci = ti.resolveReferencedClass(cname);
+    } catch(LoadOnJPFRequired lre) {
+      return ti.getPC();
+    }
+
+    if (ci.initializeClass(ti)){
+      // continue with the topframe and re-exec this insn once the clinits are done
+      return ti.getPC();
+    }
+
+    if (heap.isOutOfMemory()) { // simulate OutOfMemoryError
+      return ti.createAndThrowException("java.lang.OutOfMemoryError",
+                                        "trying to allocate new " + cname);
+    }
+
+    ElementInfo ei = heap.newObject(ci, ti);
+    int objRef = ei.getObjectRef();
+    newObjRef = objRef;
+
+    // pushes the return value onto the stack
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.pushRef( objRef);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xBB;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public int getNewObjectRef() {
+    return newObjRef;
+  }
+
+  @Override
+  public String toString() {
+    if (newObjRef != MJIEnv.NULL){
+      return "new " + cname + '@' + Integer.toHexString(newObjRef);
+
+    } else {
+      return "new " + cname;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/NEWARRAY.java b/src/main/gov/nasa/jpf/jvm/bytecode/NEWARRAY.java
new file mode 100644 (file)
index 0000000..0152d2b
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * Create new array
+ * ..., count => ..., arrayref
+ */
+public class NEWARRAY extends NewArrayInstruction {
+
+  public NEWARRAY(int typeCode) {
+    type = Types.getElementDescriptorOfType(typeCode);
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    arrayLength = frame.pop();
+    Heap heap = ti.getHeap();
+
+    if (arrayLength < 0){
+      return ti.createAndThrowException("java.lang.NegativeArraySizeException");
+    }
+
+    // there is no clinit for array classes, but we still have  to create a class object
+    // since its a builtin class, we also don't have to bother with NoClassDefFoundErrors
+    String clsName = "[" + type;
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+      ci.setInitialized();
+    }
+   
+    if (heap.isOutOfMemory()) { // simulate OutOfMemoryError
+      return ti.createAndThrowException("java.lang.OutOfMemoryError",
+                                        "trying to allocate new " +
+                                          getTypeName() +
+                                        "[" + arrayLength + "]");
+    }
+    
+    ElementInfo eiArray = heap.newArray(type, arrayLength, ti);
+    int arrayRef = eiArray.getObjectRef();
+    
+    frame.pushRef(arrayRef);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 2; // opcode, atype
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xBC;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("newarray ");
+    sb.append(getTypeName());
+    sb.append('[');
+    if (arrayLength >=0){
+      sb.append(arrayLength);
+    }
+    sb.append(']');
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/NOP.java b/src/main/gov/nasa/jpf/jvm/bytecode/NOP.java
new file mode 100644 (file)
index 0000000..d8f50b0
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Do nothing
+ *    No change
+ */
+public class NOP extends Instruction implements JVMInstruction {
+  
+  public NOP () {
+    // nothing to do
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo th) {
+    return getNext(th);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x00;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/NewArrayInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/NewArrayInstruction.java
new file mode 100644 (file)
index 0000000..ae8181f
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.bytecode.NewInstruction;
+import gov.nasa.jpf.vm.Types;
+
+public abstract class NewArrayInstruction extends NewInstruction implements JVMInstruction {
+
+  protected String type;
+  protected String typeName; // deferred initialization
+  
+  protected int arrayLength = -1;
+
+  /**
+   * this only makes sense post-execution since the array dimension
+   * is obtained from the operand stack
+   * 
+   * @return length of allocated array
+   */
+  public int getArrayLength(){
+    return arrayLength;
+  }
+  
+  public String getType(){
+    return type;
+  }
+  
+  public String getTypeName() {
+    if (typeName == null){
+      typeName = Types.getTypeName(type);
+    }
+    return typeName;
+  }
+  
+  @Override
+  public void cleanupTransients(){
+    arrayLength = -1;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/POP.java b/src/main/gov/nasa/jpf/jvm/bytecode/POP.java
new file mode 100644 (file)
index 0000000..ef7749e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Pop the top operand stack value
+ * ..., value => ...
+ */
+public class POP extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.pop();
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x57;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/POP2.java b/src/main/gov/nasa/jpf/jvm/bytecode/POP2.java
new file mode 100644 (file)
index 0000000..9dd7a1b
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Pop the top two operand slots
+ *  ..., value2, value1 => ...
+ */
+public class POP2 extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.pop(2);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x58;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/PUTFIELD.java b/src/main/gov/nasa/jpf/jvm/bytecode/PUTFIELD.java
new file mode 100644 (file)
index 0000000..69adcf3
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.util.InstructionState;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.WriteInstruction;
+
+/**
+ * Set field in object
+ * ..., objectref, value => ...
+ */
+public class PUTFIELD extends JVMInstanceFieldInstruction implements WriteInstruction {
+
+  public PUTFIELD(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }  
+
+  @Override
+  public int getObjectSlot (StackFrame frame){
+    return frame.getTopPos() - size;
+  }
+
+  /**
+   * where do we get the value from?
+   * NOTE: only makes sense in a executeInstruction() context 
+   */
+  @Override
+  public int getValueSlot (StackFrame frame){
+    return frame.getTopPos();
+  }
+
+  
+  /**
+   * where do we write to?
+   * NOTE: this should only be used from a executeInstruction()/instructionExecuted() context
+   */
+  @Override
+  public ElementInfo getElementInfo(ThreadInfo ti){
+    if (isCompleted(ti)){
+      return ti.getElementInfo(lastThis);
+    } else {
+      return peekElementInfo(ti); // get it from the stack
+    }
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    int objRef = frame.peek( size);
+    lastThis = objRef;
+    
+    if (objRef == MJIEnv.NULL) {
+      return ti.createAndThrowException("java.lang.NullPointerException", "referencing field '" + fname + "' on null object");
+    }
+
+    ElementInfo eiFieldOwner = ti.getModifiableElementInfo(objRef);
+    FieldInfo fieldInfo = getFieldInfo();
+    if (fieldInfo == null) {
+      return ti.createAndThrowException("java.lang.NoSuchFieldError", "no field " + fname + " in " + eiFieldOwner);
+    }
+    
+    //--- check scheduling point due to shared object access
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedObjectCG(ti,this,eiFieldOwner,fieldInfo)){
+      eiFieldOwner = scheduler.updateObjectSharedness( ti, eiFieldOwner, fi);
+      if (scheduler.setsSharedObjectCG( ti, this, eiFieldOwner, fieldInfo)){
+        return this; // re-execute
+      }
+    }
+    
+    // this might be re-executed
+    if (frame.getAndResetFrameAttr(InstructionState.class) == null){
+      lastValue = PutHelper.setField(ti, frame, eiFieldOwner, fieldInfo);
+    }
+    
+    //--- check scheduling point due to exposure through shared object
+    if (isReferenceField()){
+      int refValue = frame.peek();
+      if (refValue != MJIEnv.NULL){
+        ElementInfo eiExposed = ti.getElementInfo(refValue);
+        if (scheduler.setsSharedObjectExposureCG(ti, this, eiFieldOwner, fi, eiExposed)){
+          frame.addFrameAttr( InstructionState.processed);
+          return this; // re-execute AFTER assignment
+        }
+      }
+    }
+    
+    popOperands(frame);      
+    return getNext();
+  }
+
+  protected void popOperands (StackFrame frame){
+    if (size == 1){
+      frame.pop(2); // .. objref, val => ..
+    } else {
+      frame.pop(3); // .. objref, highVal,lowVal => ..    
+    }
+  }
+    
+  @Override
+  public ElementInfo peekElementInfo (ThreadInfo ti) {
+    FieldInfo fi = getFieldInfo();
+    int storageSize = fi.getStorageSize();
+    int objRef = ti.getTopFrame().peek( (storageSize == 1) ? 1 : 2);
+    ElementInfo ei = ti.getElementInfo( objRef);
+
+    return ei;
+  }
+
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xB5;
+  }
+
+  @Override
+  public boolean isRead() {
+    return false;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
+
+
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java b/src/main/gov/nasa/jpf/jvm/bytecode/PUTSTATIC.java
new file mode 100644 (file)
index 0000000..ac16018
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.util.InstructionState;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LoadOnJPFRequired;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.WriteInstruction;
+
+
+/**
+ * Set static field in class
+ * ..., value => ...
+ */
+public class PUTSTATIC extends JVMStaticFieldInstruction implements WriteInstruction {
+
+  public PUTSTATIC(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }
+
+  /**
+   * where do we get the value from?
+   * NOTE: only makes sense in a executeInstruction() context 
+   */
+  @Override
+  public int getValueSlot (StackFrame frame){
+    return frame.getTopPos();
+  }
+    
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    FieldInfo fieldInfo;
+    
+    //--- check if this causes a class load by a user defined classloader
+    try {
+      fieldInfo = getFieldInfo();
+    } catch (LoadOnJPFRequired lre) {
+      return ti.getPC();
+    }
+    
+    if (fieldInfo == null) {
+      return ti.createAndThrowException("java.lang.NoSuchFieldError", (className + '.' + fname));
+    }
+
+    //--- check if this has to trigger class initialization
+    ClassInfo ciField = fieldInfo.getClassInfo();
+    if (!mi.isClinit(ciField) && ciField.initializeClass(ti)) {
+      return ti.getPC(); // this returns the next insn in the topmost clinit that just got pushed
+    }
+    ElementInfo eiFieldOwner = ciField.getModifiableStaticElementInfo();
+
+    //--- check scheduling point due to shared class access
+    Scheduler scheduler = ti.getScheduler();
+    if (scheduler.canHaveSharedClassCG( ti, this, eiFieldOwner, fieldInfo)){
+      eiFieldOwner = scheduler.updateClassSharedness(ti, eiFieldOwner, fi);
+      if (scheduler.setsSharedClassCG( ti, this, eiFieldOwner, fieldInfo)){
+        return this; // re-execute
+      }
+    }
+    
+    // check if this gets re-executed from a exposure CG (which already did the assignment
+    if (frame.getAndResetFrameAttr(InstructionState.class) == null){
+      lastValue = PutHelper.setField(ti, frame, eiFieldOwner, fieldInfo);
+    }
+      
+    //--- check scheduling point due to exposure through shared class
+    if (isReferenceField()){
+      int refValue = frame.peek();
+      if (refValue != MJIEnv.NULL){
+        ElementInfo eiExposed = ti.getElementInfo(refValue);
+        if (scheduler.setsSharedClassExposureCG(ti,this,eiFieldOwner,fieldInfo,eiExposed)){
+          frame.addFrameAttr( InstructionState.processed);
+          return this; // re-execute AFTER assignment
+        }
+      }        
+    }
+    
+    popOperands(frame);      
+    return getNext();
+  }
+  
+  protected void popOperands (StackFrame frame){
+    if (size == 1){
+      frame.pop(); // .. val => ..
+    } else {
+      frame.pop(2);  // .. highVal, lowVal => ..
+    }
+  }
+  
+  @Override
+  public int getLength() {
+    return 3; // opcode, index1, index2
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xB3;
+  }
+
+  @Override
+  public boolean isRead() {
+    return false;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/PutHelper.java b/src/main/gov/nasa/jpf/jvm/bytecode/PutHelper.java
new file mode 100644 (file)
index 0000000..2868e31
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * helper class to factor out common PUT code
+ * 
+ * <2do> This is going to be moved into a Java 8 interface with default methods
+ */
+public class PutHelper {
+
+  protected static boolean hasNewValue (ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner, FieldInfo fi){
+    Object valAttr = null;
+    int fieldSize = fi.getStorageSize();
+    
+    if (fieldSize == 1){
+      valAttr = frame.getOperandAttr();
+      int val = frame.peek();
+      if (eiFieldOwner.get1SlotField(fi) != val){
+        return true;
+      }
+      
+    } else {
+      valAttr = frame.getLongOperandAttr();
+      long val = frame.peekLong();
+      if (eiFieldOwner.get2SlotField(fi) != val){
+        return true;
+      }
+    }
+    
+    if (eiFieldOwner.getFieldAttr(fi) != valAttr){
+      return true;
+    }
+    
+    return false;
+  }
+  
+  protected static int setReferenceField (ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner, FieldInfo fi){
+    Object valAttr = frame.getOperandAttr();
+    int val = frame.peek();
+    eiFieldOwner.set1SlotField(fi, val);
+    eiFieldOwner.setFieldAttr(fi, valAttr);
+    return val;
+  }
+  
+  protected static long setField (ThreadInfo ti, StackFrame frame, ElementInfo eiFieldOwner, FieldInfo fi){
+    int fieldSize = fi.getStorageSize();
+    
+    if (fieldSize == 1){
+      Object valAttr = frame.getOperandAttr();
+      int val = frame.peek();
+      eiFieldOwner.set1SlotField(fi, val);
+      eiFieldOwner.setFieldAttr(fi, valAttr);
+      return val;
+      
+    } else {
+      Object valAttr = frame.getLongOperandAttr();
+      long val = frame.peekLong();
+      eiFieldOwner.set2SlotField(fi, val);
+      eiFieldOwner.setFieldAttr(fi, valAttr);
+      return val;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/RET.java b/src/main/gov/nasa/jpf/jvm/bytecode/RET.java
new file mode 100644 (file)
index 0000000..a9f62a6
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Return from subroutine
+ *   No change
+ */
+public class RET extends Instruction implements JVMInstruction {
+  private int index;
+
+  public RET( int index){
+    this.index = index;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getTopFrame();
+    int jumpTgt = frame.getLocalVariable(index);
+    return mi.getInstructionAt(jumpTgt);
+  }
+
+  @Override
+  public int getLength() {
+    return 2; // opcode, insnIndex
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xA9; // ?? wide
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public int getIndex() {
+       return index;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/RETURN.java b/src/main/gov/nasa/jpf/jvm/bytecode/RETURN.java
new file mode 100644 (file)
index 0000000..605e8ac
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Return void from method
+ *   ...  [empty]
+ */
+public class RETURN extends JVMReturnInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+
+    if (mi.isInit()) {  // Check to see if this method is a constructor.
+      int objref = ti.getThis();
+      ElementInfo ei = ti.getElementInfo(objref); // Get the object.
+
+      if (!ei.isConstructed()) {  // Don't bother doing the following work if the object is already constructed.
+
+        ClassInfo ei_ci = ei.getClassInfo();  // Get the object's class.
+        ClassInfo mi_ci = mi.getClassInfo();  // Get the method's class.
+
+        if (ei_ci == mi_ci) { // If the object's class and the method's class are equal, then the thread is returning from the object's constructor.
+          ei = ei.getModifiableInstance();
+          ei.setConstructed();
+        }
+      }
+
+    } else if (mi.isClinit()) {
+      // this also needs to happen in NATIVERETURN for native clinits. See comment
+      // there why we can't refactor this into DIRECTCALLRETURN
+      mi.getClassInfo().setInitialized();
+    }
+
+    return super.execute(ti);
+  }
+
+  @Override
+  public int getReturnTypeSize() {
+    return 0;
+  }
+  
+  @Override
+  protected Object getReturnedOperandAttr (StackFrame frame) {
+    return null;
+  }
+
+  
+  @Override
+  public Object getReturnAttr (ThreadInfo ti){
+    return null; // no return value
+  }
+
+  @Override
+  protected void getAndSaveReturnValue (StackFrame frame) {
+    // we don't have any
+  }
+
+  @Override
+  protected void pushReturnValue (StackFrame frame) {
+    // nothing to do
+  }
+
+  @Override
+  public Object getReturnValue(ThreadInfo ti) {
+    //return Void.class; // Hmm, not sure if this is right, but we have to distinguish from ARETURN <null>
+    return null;
+  }
+
+  @Override
+  public String toString() {
+    return "return  " + mi.getFullName();
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0xB1;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/RUNSTART.java b/src/main/gov/nasa/jpf/jvm/bytecode/RUNSTART.java
new file mode 100644 (file)
index 0000000..9fa1512
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * this is an artificial instruction that is automatically prepended to
+ * a run()/main() method call.
+ * 
+ * The main purpose is to have a special instruction marking the beginning
+ * of a new thread execution which does not cause CGs or is otherwise subject
+ * to execution semantics that change the program state.
+ * 
+ * For instance, without it we would have to add a new ThreadInfo state to
+ * determine if the first instruction within this thread was re-executed or
+ * just happens to be the first transition we execute within this thread
+ * 
+ */
+public class RUNSTART extends Instruction implements JVMInstruction {
+
+  public RUNSTART () {
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    // nothing here, we could have used a NOP
+    return getNext(ti);
+  }
+
+  public static final int OPCODE = 257;
+
+  @Override
+  public int getByteCode () {
+    return OPCODE;
+  }
+
+  @Override
+  public boolean isExtendedInstruction() {
+    return true;
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/SALOAD.java b/src/main/gov/nasa/jpf/jvm/bytecode/SALOAD.java
new file mode 100644 (file)
index 0000000..aea3261
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Load short from array
+ * ..., arrayref, index => ..., value
+ */
+public class SALOAD extends ArrayLoadInstruction {
+
+  @Override
+  protected void push (StackFrame frame, ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    short value = ei.getShortElement(index);
+    frame.push( value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x35;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/SASTORE.java b/src/main/gov/nasa/jpf/jvm/bytecode/SASTORE.java
new file mode 100644 (file)
index 0000000..96a090c
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ArrayIndexOutOfBoundsExecutiveException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * Store into short array 
+ * ..., array, index, value => ...
+ */
+public class SASTORE extends ArrayStoreInstruction {
+
+  short value;
+
+  @Override
+  protected void popValue(StackFrame frame){
+    value = (short)frame.pop();
+  }
+
+  @Override
+  protected void setField (ElementInfo ei, int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    ei.checkArrayBounds(index);
+    ei.setShortElement(index, value);
+  }
+
+
+  @Override
+  public int getByteCode () {
+    return 0x56;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/SIPUSH.java b/src/main/gov/nasa/jpf/jvm/bytecode/SIPUSH.java
new file mode 100644 (file)
index 0000000..8b1f18d
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Push short
+ * ... => ..., value
+ */
+public class SIPUSH extends Instruction implements JVMInstruction {
+  protected int value;
+
+  public SIPUSH(int value){
+    this.value = value;
+  }
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.push(value);
+
+    return getNext(ti);
+  }
+
+  @Override
+  public int getLength() {
+    return 3; // opcode, b1, b2
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0x11;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public int getValue() {
+       return value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/SWAP.java b/src/main/gov/nasa/jpf/jvm/bytecode/SWAP.java
new file mode 100644 (file)
index 0000000..c53d655
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Swap the top two operand stack values
+ * ..., value2, value1 => ..., value1, value2
+ */
+public class SWAP extends Instruction implements JVMInstruction {
+
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    
+    frame.swap();
+    
+    return getNext(ti);
+  }
+
+  @Override
+  public int getByteCode () {
+    return 0x5F;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/StaticFieldInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/StaticFieldInstruction.java
new file mode 100644 (file)
index 0000000..f2a896e
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.FieldInstruction;
+
+/**
+ * class to abstract instructions accessing static fields
+ */
+public abstract class StaticFieldInstruction extends FieldInstruction {
+
+  protected StaticFieldInstruction(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }
+
+  /**
+   * on-demand initialize the ClassInfo and FieldInfo fields. Note that
+   * classinfo might not correspond with the static className, but can be one of
+   * the super classes. Rather than checking for this on each subsequent access,
+   * we get the right one that declares the field here
+   */
+  protected void initialize() {
+    ClassInfo ciRef = mi.getClassInfo().resolveReferencedClass(className);
+    
+    FieldInfo f = ciRef.getStaticField(fname);
+    ClassInfo ciField = f.getClassInfo();
+    if (!ciField.isRegistered()){
+      // classLoaded listeners might change/remove this field
+      ciField.registerClass(ThreadInfo.getCurrentThread());
+      f = ciField.getStaticField(fname);
+    }
+    
+    fi = f;
+  }
+
+  /**
+   * who owns the field?
+   * NOTE: this should only be used from a executeInstruction()/instructionExecuted() context
+   */
+  @Override
+  public ElementInfo getElementInfo(ThreadInfo ti){
+    return getFieldInfo().getClassInfo().getStaticElementInfo();
+  }
+  
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(' ');
+    sb.append( fi.getFullName());
+    
+    return sb.toString();
+  }
+
+  public ClassInfo getClassInfo() {
+    if (fi == null) {
+      initialize();
+    }
+    return fi.getClassInfo();
+  }
+
+  @Override
+  public FieldInfo getFieldInfo() {
+    if (fi == null) {
+      initialize();
+    }
+    return fi;
+  }
+
+  /**
+   *  that's invariant, as opposed to InstanceFieldInstruction, so it's
+   *  not really a peek
+   */
+  @Override
+  public ElementInfo peekElementInfo (ThreadInfo ti) {
+    return getLastElementInfo();
+  }
+
+  @Override
+  public StaticElementInfo getLastElementInfo() {
+    return getFieldInfo().getClassInfo().getStaticElementInfo();
+  }
+
+  // this can be different than ciField - the field might be in one of its
+  // superclasses
+  public ClassInfo getLastClassInfo(){
+    return getFieldInfo().getClassInfo();
+  }
+
+  public String getLastClassName() {
+    return getLastClassInfo().getName();
+  }
+
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo mi) {
+    StaticFieldInstruction clone = null;
+
+    try {
+      clone = (StaticFieldInstruction) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+      clone.fi = null; // ClassInfo is going to be different
+      
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/SwitchInstruction.java b/src/main/gov/nasa/jpf/jvm/bytecode/SwitchInstruction.java
new file mode 100644 (file)
index 0000000..0752b5f
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.KernelState;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
+
+/**
+ * common root class for LOOKUPSWITCH and TABLESWITCH insns
+ *
+ * <2do> this is inefficient. First, we should store targets as instruction indexes
+ * to avoid execution() looping. Second, there are no matches for a TABLESWITCH
+ */
+public abstract class SwitchInstruction extends Instruction implements JVMInstruction {
+
+  public static final int DEFAULT = -1; 
+  
+  protected int   target;   // default branch
+  protected int[] targets;  // explicit value branches
+  protected int[] matches;  // branch consts
+
+  protected int lastIdx;
+
+  protected SwitchInstruction (int defaultTarget, int numberOfTargets){
+    target = defaultTarget;
+    targets = new int[numberOfTargets];
+    matches = new int[numberOfTargets];
+  }
+
+  public int getNumberOfEntries() {
+    return targets.length;
+  }
+
+  protected Instruction executeConditional (ThreadInfo ti){
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int value = frame.pop();
+
+    lastIdx = DEFAULT;
+
+    for (int i = 0, l = matches.length; i < l; i++) {
+      if (value == matches[i]) {
+        lastIdx = i;
+        return mi.getInstructionAt(targets[i]);
+      }
+    }
+
+    return mi.getInstructionAt(target);
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    // this can be overridden by subclasses, so we have to delegate the conditional execution
+    // to avoid getting recursive in executeAllBranches()
+    return executeConditional(ti);
+  }
+
+  /** useful for symbolic execution modes */
+  public Instruction executeAllBranches (SystemState ss, KernelState ks, ThreadInfo ti) {
+    if (!ti.isFirstStepInsn()) {
+      IntIntervalGenerator cg = new IntIntervalGenerator("switchAll", 0,matches.length);
+      if (ss.setNextChoiceGenerator(cg)){
+        return this;
+
+      } else {
+        // listener did override CG, fall back to conditional execution
+        return executeConditional(ti);
+      }
+      
+    } else {
+      IntIntervalGenerator cg = ss.getCurrentChoiceGenerator("switchAll", IntIntervalGenerator.class);
+      assert (cg != null) : "no IntIntervalGenerator";
+      
+      StackFrame frame = ti.getModifiableTopFrame();
+      int idx = frame.pop(); // but we are not using it
+      idx = cg.getNextChoice();
+      
+      if (idx == matches.length){ // default branch
+        lastIdx = DEFAULT;
+        return mi.getInstructionAt(target);
+      } else {
+        lastIdx = idx;
+        return mi.getInstructionAt(targets[idx]);
+      }
+    }
+  }
+
+  //--- a little inspection, but only post exec yet
+  
+  public int getLastTargetIndex () {
+    return lastIdx;
+  }
+  
+  public int getNumberOfTargets () {
+    return matches.length;
+  }
+  
+  public int getMatchConst (int idx){
+    return matches[idx];
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  public int getTarget() {
+       return target;
+  }
+
+  public int[] getTargets() {
+       return targets;
+  }
+
+  public int[] getMatches() {
+       return matches;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/TABLESWITCH.java b/src/main/gov/nasa/jpf/jvm/bytecode/TABLESWITCH.java
new file mode 100644 (file)
index 0000000..ee77258
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * Access jump table by index and jump
+ *   ..., index  => ...
+ */
+public class TABLESWITCH extends SwitchInstruction implements gov.nasa.jpf.vm.bytecode.TableSwitchInstruction {
+
+  int min, max;
+
+  public TABLESWITCH(int defaultTarget, int min, int max){
+    super(defaultTarget, (max - min +1));
+    this.min = min;
+    this.max = max;
+  }
+  
+  public int getMin(){
+         return min;
+  }
+  
+  public int getMax(){
+         return max;
+  }
+
+  @Override
+  public void setTarget (int value, int target){
+    int i = value-min;
+
+    if (i>=0 && i<targets.length){
+      targets[i] = target;
+    } else {
+      throw new JPFException("illegal tableswitch target: " + value);
+    }
+  }
+
+  @Override
+  protected Instruction executeConditional (ThreadInfo ti){
+    StackFrame frame = ti.getModifiableTopFrame();
+
+    int value = frame.pop();
+    int i = value-min;
+    int pc;
+
+    if (i>=0 && i<targets.length){
+      lastIdx = i;
+      pc = targets[i];
+    } else {
+      lastIdx = -1;
+      pc = target;
+    }
+
+    // <2do> this is BAD - we should compute the target insns just once
+    return mi.getInstructionAt(pc);
+  }
+
+
+  @Override
+  public int getLength() {
+    return 13 + 2*(matches.length); // <2do> NOT RIGHT: padding!!
+  }
+  
+  @Override
+  public int getByteCode () {
+    return 0xAA;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/VirtualInvocation.java b/src/main/gov/nasa/jpf/jvm/bytecode/VirtualInvocation.java
new file mode 100644 (file)
index 0000000..f394eea
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.ClassChangeException;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * a base class for virtual call instructions
+ */
+public abstract class VirtualInvocation extends InstanceInvocation {
+
+  // note that we can't null laseCalleeCi and invokedMethod in cleanupTransients()
+  // since we use it as an internal optimization (loops with repeated calls on the
+  // same object)
+  
+  ClassInfo lastCalleeCi; // cached for performance
+
+  protected VirtualInvocation () {}
+
+  protected VirtualInvocation (String clsDescriptor, String methodName, String signature){
+    super(clsDescriptor, methodName, signature);
+  }
+
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(' ');
+    
+    if (invokedMethod != null){
+      sb.append( lastCalleeCi.getName());
+      sb.append('@');
+      sb.append(Integer.toHexString(lastObj));
+      sb.append('.');
+      sb.append(invokedMethod.getUniqueName());
+
+      if (invokedMethod.isMJI()){
+        sb.append(" [native]");
+      }
+      
+    } else { // something went wrong, the method wasn't found
+      if (lastCalleeCi != null){
+        sb.append( lastCalleeCi.getName());
+      } else {
+        sb.append(cname);
+      }
+      sb.append('@');
+      if (lastObj == MJIEnv.NULL){
+        sb.append("<null>");
+      } else {
+        sb.append(Integer.toHexString(lastObj));
+      }
+      sb.append('.');
+      sb.append(mname);
+      sb.append(signature);
+      sb.append(" (?)");
+    }
+    
+    return sb.toString();
+  }
+  
+  @Override
+  public Instruction execute (ThreadInfo ti) {
+    int objRef = ti.getCalleeThis(getArgSize());
+    MethodInfo callee;
+
+    if (objRef == MJIEnv.NULL) {
+      lastObj = MJIEnv.NULL;
+      return ti.createAndThrowException("java.lang.NullPointerException", "Calling '" + mname + "' on null object");
+    }
+
+    try {
+      callee = getInvokedMethod(ti, objRef);
+    } catch (ClassChangeException ccx){
+      return ti.createAndThrowException("java.lang.IncompatibleClassChangeError", ccx.getMessage());
+    }
+    
+    ElementInfo ei = ti.getElementInfo(objRef);
+    
+    if (callee == null) {
+      String clsName = ti.getClassInfo(objRef).getName();
+      return ti.createAndThrowException("java.lang.NoSuchMethodError", clsName + '.' + mname);
+    } else {
+      if (callee.isAbstract()){
+        return ti.createAndThrowException("java.lang.AbstractMethodError", callee.getFullName() + ", object: " + ei);
+      }
+    }
+
+    if (callee.isSynchronized()) {
+      ei = ti.getScheduler().updateObjectSharedness(ti, ei, null); // locks most likely belong to shared objects
+      if (reschedulesLockAcquisition(ti, ei)){
+        return this;
+      }
+    }
+
+    setupCallee( ti, callee); // this creates, initializes and pushes the callee StackFrame
+
+    return ti.getPC(); // we can't just return the first callee insn if a listener throws an exception
+  }
+  
+  /**
+   * If the current thread already owns the lock, then the current thread can go on.
+   * For example, this is a recursive acquisition.
+   */
+  protected boolean isLockOwner(ThreadInfo ti, ElementInfo ei) {
+    return ei.getLockingThread() == ti;
+  }
+
+  /**
+   * If the object will still be owned, then the current thread can go on.
+   * For example, all but the last monitorexit for the object.
+   */
+  protected boolean isLastUnlock(ElementInfo ei) {
+    return ei.getLockCount() == 1;
+  }
+
+
+  @Override
+  public MethodInfo getInvokedMethod(ThreadInfo ti){
+    int objRef;
+
+    if (ti.getNextPC() == null){ // this is pre-exec
+      objRef = ti.getCalleeThis(getArgSize());
+    } else {                     // this is post-exec
+      objRef = lastObj;
+    }
+
+    return getInvokedMethod(ti, objRef);
+  }
+
+  public MethodInfo getInvokedMethod (ThreadInfo ti, int objRef) {
+
+    if (objRef != MJIEnv.NULL) {
+      lastObj = objRef;
+
+      ClassInfo cci = ti.getClassInfo(objRef);
+
+      if (lastCalleeCi != cci) { // callee ClassInfo has changed
+        lastCalleeCi = cci;
+        invokedMethod = cci.getMethod(mname, true);
+
+        if (invokedMethod == null) {
+          invokedMethod = cci.getDefaultMethod(mname);
+                    
+          if (invokedMethod == null){
+            lastObj = MJIEnv.NULL;
+            lastCalleeCi = null;
+          }
+        }
+      }
+
+    } else {
+      lastObj = MJIEnv.NULL;
+      lastCalleeCi = null;
+      invokedMethod = null;
+    }
+
+    return invokedMethod;
+  }
+
+  @Override
+  public Object getFieldValue (String id, ThreadInfo ti){
+    int objRef = getCalleeThis(ti);
+    ElementInfo ei = ti.getElementInfo(objRef);
+
+    Object v = ei.getFieldValueObject(id);
+
+    if (v == null){ // try a static field
+      v = ei.getClassInfo().getStaticFieldValueObject(id);
+    }
+
+    return v;
+  }
+  
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+
+  @Override
+  public Instruction typeSafeClone(MethodInfo clonedMethod) {
+    VirtualInvocation clone = null;
+
+    try {
+      clone = (VirtualInvocation) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = clonedMethod;
+
+      clone.lastCalleeCi = null;
+      clone.invokedMethod = null;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/jvm/bytecode/WIDE.java b/src/main/gov/nasa/jpf/jvm/bytecode/WIDE.java
new file mode 100644 (file)
index 0000000..8d43ddb
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * modifies following insn, no stack manipulation
+ * NOTE: transparently handled by BCEL, we should never receive this
+ *
+ * (1): <iload,fload,aload,lload,dload,istore,fstore,astore,lstore,dstore,ret> indexbyte1 indexbyte2
+ * (2): <iinc> indexbyte1 indexbyte2 constbyte1 constbyte2
+ *
+ */
+public class WIDE extends Instruction implements JVMInstruction {
+
+  // would have to be checked and reset by following insn
+  public static boolean isWide = false;
+
+  @Override
+  public int getByteCode() {
+    return 0xc4;
+  }
+
+  @Override
+  public Instruction execute(ThreadInfo ti) {
+    // nothing, BCEL doesn't even pass this on;
+    return getNext(ti);
+  }
+
+  @Override
+  public void accept(JVMInstructionVisitor insVisitor) {
+         insVisitor.visit(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/AssertionProperty.java b/src/main/gov/nasa/jpf/listener/AssertionProperty.java
new file mode 100644 (file)
index 0000000..178b1d6
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.ATHROW;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * this is a property listener that turns thrown AssertionErrors into
+ * property violations before they are caught (i.e. potentially
+ * change the stack).
+ * Besides serving the purpose of eliminating the "catch(Throwable)" case,
+ * it can be used in conjunction with "search.multiple_errors=true" to
+ * report assertions but otherwise ignore them and go on searching the
+ * same path (otherwise, multiple_errors would cause a backtrack)
+ */
+public class AssertionProperty extends PropertyListenerAdapter {
+
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.listener.AssertionProperty");
+  
+  boolean goOn;
+  String msg;
+  
+  public AssertionProperty (Config config) {
+    goOn = config.getBoolean("ap.go_on",false);
+  }
+  
+  @Override
+  public boolean check(Search search, VM vm) {
+    return (msg == null);
+  }
+
+  @Override
+  public String getErrorMessage() {
+    return msg;
+  }
+
+  protected String getMessage (String details, Instruction insn){
+    String s = "failed assertion";
+    
+    if (details != null){
+      s += ": \"";
+      s += details;
+      s += '"';
+    }
+
+    s += " at ";
+    s += insn.getSourceLocation();
+    
+    return s;
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn){
+    
+    if (insn instanceof ATHROW) {
+      
+      Heap heap = vm.getHeap();
+      StackFrame frame = ti.getTopFrame();
+      int xobjref = frame.peek();
+      ElementInfo ei = heap.get(xobjref);
+      ClassInfo ci = ei.getClassInfo();
+      
+      if (ci.getName().equals("java.lang.AssertionError")) {
+        int msgref = ei.getReferenceField("detailMessage");
+        ElementInfo eiMsg = heap.get(msgref);
+        String details = eiMsg != null ? eiMsg.asString() : null;
+
+        // Ok, arm ourselves
+        msg = getMessage( details, insn.getNext());
+        
+        if (goOn) {
+          log.warning(msg);
+
+          frame = ti.getModifiableTopFrame();
+          frame.pop(); // ensure operand stack integrity (ATHROW pops)
+          
+          ti.skipInstruction(insn.getNext());
+
+        } else {
+          ti.skipInstruction(insn);
+          ti.breakTransition("assertion");
+        }
+      }
+    }
+  }
+  
+  @Override
+  public void reset() {
+    msg = null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/BudgetChecker.java b/src/main/gov/nasa/jpf/listener/BudgetChecker.java
new file mode 100644 (file)
index 0000000..2a84b6c
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.annotation.JPFOption;
+import gov.nasa.jpf.annotation.JPFOptions;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+import java.lang.management.ManagementFactory;
+import java.lang.management.MemoryMXBean;
+import java.lang.management.MemoryUsage;
+
+/**
+ * Listener that implements various budget constraints
+ */
+@JPFOptions({
+  @JPFOption(type = "Long", key = "budget.max_time", defaultValue= "-1", comment = "stop search after specified duration [msec]"),
+  @JPFOption(type = "Long", key = "budget.max_heap", defaultValue = "-1", comment="stop search when VM heapsize reaches specified limit"),
+  @JPFOption(type = "Int", key = "budget.max_depth", defaultValue = "-1", comment = "stop search at specified search depth"),
+  @JPFOption(type = "long", key = "budget.max_insn", defaultValue = "-1", comment = "stop search after specified number of intstructions"),
+  @JPFOption(type = "Int", key = "budget.max_state", defaultValue = "-1", comment = "stop search when reaching specified number of new states"),
+  @JPFOption(type = "Int", key = "budget.max_new_states", defaultValue = "-1", comment="stop search ater specified number of non-replayed new states")
+})
+public class BudgetChecker extends ListenerAdapter {
+
+  static final int CHECK_INTERVAL = 10000;
+  static final int CHECK_INTERVAL1 = CHECK_INTERVAL-1;
+    
+  long tStart;
+  MemoryUsage muStart;
+  long mStart;
+  MemoryMXBean mxb;
+  
+  VM vm;
+  Search search;
+  long insnCount;
+
+  //--- the budget thresholds
+  long maxTime;
+  long maxHeap;
+  
+  int maxDepth;
+  long maxInsn;
+  int maxState;
+  int maxNewStates;
+  
+  int newStates;
+  
+  // the message explaining the exceeded budget
+  String message;
+  
+  public BudgetChecker (Config conf, JPF jpf) {
+    
+    //--- get the configured budget limits (0 means not set)
+    maxTime = conf.getDuration("budget.max_time", 0);
+    maxHeap = conf.getMemorySize("budget.max_heap", 0);
+    maxDepth = conf.getInt("budget.max_depth", 0);
+    maxInsn = conf.getLong("budget.max_insn", 0);
+    maxState = conf.getInt("budget.max_state", 0);
+    maxNewStates = conf.getInt("budget.max_new_states", 0);
+    
+    tStart = System.currentTimeMillis();
+    
+    if (maxHeap > 0) {
+      mxb = ManagementFactory.getMemoryMXBean();
+      muStart = mxb.getHeapMemoryUsage();
+      mStart = muStart.getUsed();
+    }
+
+    search = jpf.getSearch();
+    vm = jpf.getVM();
+  }
+      
+  public boolean timeExceeded() {
+    if (maxTime > 0) {
+      long dur = System.currentTimeMillis() - tStart;
+      if (dur > maxTime) {
+        message = "max time exceeded: " + Publisher.formatHMS(dur)
+               + " >= " + Publisher.formatHMS(maxTime);
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean heapExceeded() {
+    if (maxHeap > 0) {
+      MemoryUsage mu = mxb.getHeapMemoryUsage();
+      long used = mu.getUsed() - mStart;
+      if (used > maxHeap) {
+        message = "max heap exceeded: " + (used / (1024*1024)) + "MB" 
+                      + " >= " + (maxHeap / (1024*1024)) + "MB" ;
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean depthExceeded () {
+    if (maxDepth > 0) {
+      int d = search.getDepth();
+      if (d > maxDepth) {
+        message = "max search depth exceeded: " + maxDepth;
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean statesExceeded () {
+    if (maxState > 0) {
+      int stateId = vm.getStateId();
+      if (stateId > maxState) {
+        message = "max states exceeded: " + maxState;;
+        return true;
+      }
+    }
+    
+    return false;
+  }
+    
+  public boolean insnExceeded () {
+    if (maxInsn > 0) {
+      if (insnCount > maxInsn) {
+        message = "max instruction count exceeded: " + maxInsn;
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  public boolean newStatesExceeded(){
+    if (maxNewStates > 0){
+      if (newStates > maxNewStates) {
+        message = "max new state count exceeded: " + maxNewStates;
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  @Override
+  public void stateAdvanced (Search search) {    
+    if (timeExceeded() || heapExceeded()) {
+      search.notifySearchConstraintHit(message);
+      search.terminate();
+    }
+    
+    if (search.isNewState()){
+      if (!vm.isTraceReplay()){
+        newStates++;
+      }
+      if (statesExceeded() || depthExceeded() || newStatesExceeded()){
+        search.notifySearchConstraintHit(message);
+        search.terminate();        
+      }
+    }
+  }
+      
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    if ((insnCount++ % CHECK_INTERVAL) == CHECK_INTERVAL1) {
+
+      if (timeExceeded() || heapExceeded() || insnExceeded()) {
+        search.notifySearchConstraintHit(message);
+
+        vm.getCurrentThread().breakTransition("budgetConstraint");
+        search.terminate();
+      }    
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/CGMonitor.java b/src/main/gov/nasa/jpf/listener/CGMonitor.java
new file mode 100644 (file)
index 0000000..9f3c21c
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.VM;
+import java.io.PrintStream;
+
+/**
+ * listener to report out what CGs and choices are processed during the search.
+ * This is a simple tool to find out about the SUT state space
+ */
+public class CGMonitor extends ListenerAdapter {
+  
+  protected PrintStream out;
+  
+  protected int depth;
+  
+  // display options
+  protected boolean showInsn = false;   // show the insn that caused the CG
+  protected boolean showChoice = false; // show the choice value (-> show each CG.advance())
+  protected boolean showDepth = true;   // show search depth at point of CG set/advance
+  
+  public CGMonitor (Config conf) {
+    showInsn = conf.getBoolean("cgm.show_insn", showInsn);
+    showChoice = conf.getBoolean("cgm.show_choice", showChoice);
+    showDepth = conf.getBoolean("cgm.show_depth", showDepth);
+    
+    out = System.out;
+  }
+  
+  @Override
+  public void stateAdvanced (Search search) {
+    depth++;
+  }
+  
+  @Override
+  public void stateBacktracked (Search search) {
+    depth--;
+  }
+  
+  @Override
+  public void stateRestored (Search search) {
+    depth = search.getDepth();    
+  }
+  
+  void printPrefix(char c) {
+    for (int i=0; i<depth; i++) {
+      System.out.print(c);
+    }
+  }
+  
+  void printCG (ChoiceGenerator<?> cg, boolean printChoice){
+    if (showDepth){
+      printPrefix('.');
+    }
+    
+    out.print(cg);
+
+    if (printChoice){
+      out.print(", ");
+      out.print(cg.getNextChoice());
+    }
+
+    if (showInsn){
+      out.print(", \"");
+      out.print(cg.getInsn());
+      out.print('\"');
+    }
+
+    out.println();    
+  }
+  
+  @Override
+  public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> currentCG) {
+    if (!showChoice){
+      printCG( vm.getChoiceGenerator(), false);
+    }
+  }
+  
+  @Override
+  public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+    if (showChoice){
+      printCG( vm.getChoiceGenerator(), true);      
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/CGRemover.java b/src/main/gov/nasa/jpf/listener/CGRemover.java
new file mode 100644 (file)
index 0000000..14aa962
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.LocationSpec;
+import gov.nasa.jpf.util.MethodSpec;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * listener that removes CGs for specified locations, method calls or method bodies
+ *
+ * This is an application specific state space optimizer that should be used
+ * carefully since it has to be aware of which CGs can be removed, and which
+ * ones can't (e.g. blocking sync or wait). You also have to be aware of the
+ * order of listener registration, since subsequently registered listeners can
+ * still add new CGs after they got removed here. THIS IS ONLY AN OPTIMIZATION
+ * TOOL THAT SHOULD BE USED IN A WELL KNOWN APPLICATION CONTEXT.
+ *
+ *  cgrm.thread.cg_class = gov.nasa.jpf.vm.ThreadChoiceGenerator
+ *  cgrm.thread.locations = Foobar.java:42                // either a LocationSpec
+ *  cgrm.thread.method_bodies = a.SomeClass.someMethod()  // ..or a MethodSpec for a body
+ *  cgrm.thread.method_calls = b.A.foo(int)               // ..or a MethodSpec for a call
+ *
+ * NOTE: in its current implementation, this listener has to be registered
+ * before targeted classes are loaded
+ */
+public class CGRemover extends ListenerAdapter {
+
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.CGRemover");
+
+  static class Category {
+    String id;
+
+    Class<?> cgClass;
+
+    ArrayList<LocationSpec> locations = new ArrayList<LocationSpec>();
+    ArrayList<MethodSpec> methodBodies = new ArrayList<MethodSpec>();
+    ArrayList<MethodSpec> methodCalls = new ArrayList<MethodSpec>();
+
+    Category (String id){
+      this.id = id;
+    }
+
+    boolean checkSpecification() {
+      return cgClass != null &&
+              (!locations.isEmpty() || !methodBodies.isEmpty() || !methodCalls.isEmpty());
+    }
+  }
+
+  List<Category> categories;
+
+  HashMap<MethodInfo,Category> methodBodies;
+  HashMap<MethodInfo,Category> methodCalls;
+  HashMap<Instruction,Category> locations;
+
+
+  public CGRemover (Config conf){
+    categories = parseCategories(conf);
+  }
+
+  protected List<Category> parseCategories (Config conf){
+    ArrayList<Category> list = new ArrayList<Category>();
+    Category category = null;
+
+    for (String key : conf.getKeysStartingWith("cgrm.")){
+      String[] kc = conf.getKeyComponents(key);
+      if (kc.length == 3){
+        String k = kc[1];
+        if (category != null){
+          if (!category.id.equals(k)){
+            addCategory(list, category);
+
+            category = new Category(k);
+          }
+        } else {
+          category = new Category(k);
+        }
+
+        k = kc[2];
+
+        if ("cg_class".equals(k)){
+          category.cgClass = conf.getClass(key);
+
+        } else if ("locations".equals(k)){
+          parseLocationSpecs(category.locations, conf.getStringArray(key));
+
+        } else if ("method_bodies".equals(k)){
+          parseMethodSpecs(category.methodBodies, conf.getStringArray(key));
+
+        } else if ("method_calls".equals(k)){
+          parseMethodSpecs(category.methodCalls, conf.getStringArray(key));
+
+        } else {
+          // we might have more options in the future
+          log.warning("illegal CGRemover option: ", key);
+        }
+
+      } else {
+        log.warning("illegal CGRemover key: ", key);
+      }
+    }
+
+    addCategory(list, category);
+    return list;
+  }
+
+  protected void addCategory (List<Category> list, Category cat){
+    if (cat != null) {
+      if (cat.checkSpecification()) {
+        list.add(cat);
+        log.info("added category: ", cat.id);
+      } else {
+        log.warning("incomplete CGRemover category: ", cat.id);
+      }
+    }
+  }
+
+  protected void parseLocationSpecs (List<LocationSpec> list, String[] specs){
+    for (String spec : specs) {
+      LocationSpec locSpec = LocationSpec.createLocationSpec(spec);
+      if (locSpec != null) {
+        if (locSpec.isAnyLine()){
+          log.warning("whole file location specs not supported by CGRemover (use cgrm...method_bodies)");
+        } else {
+          list.add(locSpec);
+        }
+      } else {
+        log.warning("location spec did not parse: ", spec);
+      }
+    }
+  }
+
+  protected void parseMethodSpecs (List<MethodSpec> list, String[] specs){
+    for (String spec : specs) {
+      MethodSpec mthSpec = MethodSpec.createMethodSpec(spec);
+      if (mthSpec != null) {
+        list.add(mthSpec);
+      } else {
+        log.warning("methos spec did not parse: ", spec);
+      }
+    }
+  }
+
+
+  protected void processClass (ClassInfo ci, Category cat){
+    String fname = ci.getSourceFileName();
+
+    for (LocationSpec loc : cat.locations){
+      if (loc.matchesFile(fname)){ // Ok, we have to dig out the corresponding insns (if any)
+        Instruction[] insns = ci.getMatchingInstructions(loc);
+        if (insns != null){
+          if (locations == null){
+            locations = new HashMap<Instruction,Category>();
+          }
+          for (Instruction insn : insns){
+            locations.put(insn, cat);
+          }
+        } else {
+          log.warning("no insns for location: ", loc, " in class: ", ci.getName());
+        }
+      }
+    }
+
+    for (MethodSpec ms : cat.methodBodies){
+      List<MethodInfo> list = ci.getMatchingMethodInfos(ms);
+      if (list != null){
+        for (MethodInfo mi : list){
+          if (methodBodies == null){
+            methodBodies = new HashMap<MethodInfo,Category>();
+          }
+          methodBodies.put(mi, cat);
+        }
+      }
+    }
+
+    for (MethodSpec ms : cat.methodCalls){
+      List<MethodInfo> list = ci.getMatchingMethodInfos(ms);
+      if (list != null){
+        for (MethodInfo mi : list){
+          if (methodCalls == null){
+            methodCalls = new HashMap<MethodInfo,Category>();
+          }
+          methodCalls.put(mi, cat);
+        }
+      }
+    }
+  }
+
+  protected boolean removeCG (VM vm, Category cat, ChoiceGenerator<?> cg){
+    if (cat != null){
+      if (cat.cgClass.isAssignableFrom(cg.getClass())){
+        vm.getSystemState().removeNextChoiceGenerator();
+        log.info("removed CG: ", cg);
+        return true;        
+      }
+    }
+    
+    return false;
+  }
+
+  //--- VMListener interface
+
+  // this is where we turn Categories into MethodInfos and Instructions to watch out for
+  @Override
+  public void classLoaded (VM vm, ClassInfo loadedClass){
+    for (Category cat : categories){
+      processClass(loadedClass, cat);
+    }
+  }
+
+  // this is our main purpose in life
+  @Override
+  public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo ti, Instruction executedInsn){
+    ChoiceGenerator<?> cg = vm.getNextChoiceGenerator();
+    Instruction insn = cg.getInsn();
+
+    if (locations != null){
+      if ( removeCG(vm, locations.get(insn), cg)){
+        return;
+      }
+    }
+
+    if (insn instanceof JVMInvokeInstruction){
+      MethodInfo invokedMi = ((JVMInvokeInstruction)insn).getInvokedMethod();
+      if (methodCalls != null) {
+        if (removeCG(vm, methodCalls.get(invokedMi), cg)) {
+          return;
+        }
+      }
+    }
+
+    if (methodBodies != null){
+      if (removeCG(vm, methodBodies.get(insn.getMethodInfo()), cg)) {
+        return;
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/CallMonitor.java b/src/main/gov/nasa/jpf/listener/CallMonitor.java
new file mode 100644 (file)
index 0000000..010e52c
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * this isn't yet a useful tool, but it shows how to track method calls with
+ * their corresponding argument values
+ */
+public class CallMonitor extends ListenerAdapter {
+
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    
+    if (executedInsn instanceof JVMInvokeInstruction) {
+      if (executedInsn.isCompleted(ti) && !ti.isInstructionSkipped()) {
+        JVMInvokeInstruction call = (JVMInvokeInstruction)executedInsn;
+        MethodInfo mi = call.getInvokedMethod();
+        Object[] args = call.getArgumentValues(ti);
+        ClassInfo ci = mi.getClassInfo();
+
+        StringBuilder sb = new StringBuilder();
+
+        sb.append(ti.getId());
+        sb.append(": ");
+
+        int d = ti.getStackDepth();
+        for (int i=0; i<d; i++){
+          sb.append(" ");
+        }
+
+        if (ci != null){
+          sb.append(ci.getName());
+          sb.append('.');
+        }
+        sb.append(mi.getName());
+        sb.append('(');
+
+        int n = args.length-1;
+        for (int i=0; i<=n; i++) {
+          if (args[i] != null) {
+            sb.append(args[i].toString());
+          } else {
+            sb.append("null");
+          }
+          if (i<n) {
+            sb.append(',');
+          }
+        }
+        sb.append(')');
+
+        System.out.println(sb);
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/ChoiceSelector.java b/src/main/gov/nasa/jpf/listener/ChoiceSelector.java
new file mode 100644 (file)
index 0000000..53e3d23
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.annotation.JPFOption;
+import gov.nasa.jpf.annotation.JPFOptions;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoicePoint;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.Random;
+
+/**
+ * this is a listener that only executes single choices until it detects
+ * that it should start to search. If nothing is specified, this is pretty
+ * much a simulator that randomly picks choices. Otherwise the user can
+ * give it any combination of
+ *  - a set of thread names
+ *  - a set of method names
+ *  - a start search depth
+ * to turn on the search. If more than one condition is given, all have to be
+ * satisfied
+ */
+
+@JPFOptions({
+  @JPFOption(type = "Int", key = "choice.seed", defaultValue= "42", comment = ""),
+  @JPFOption(type = "StringArray", key = "choice.threads", defaultValue = "", comment="start search, when all threads in the set are active"),
+  @JPFOption(type = "StringArray", key = "choice.calls", defaultValue = "", comment = "start search, when any of the methods is called"),
+  @JPFOption(type = "Int", key = "choice.depth", defaultValue = "-1", comment = "start search, when reaching this depth"),
+  @JPFOption(type = "String", key = "choice.use_trace", defaultValue ="", comment = ""),
+  @JPFOption(type = "Boolean", key = "choice.search_after_trace", defaultValue = "true", comment="start search, when reaching the end of the stored trace")
+})
+public class ChoiceSelector extends ListenerAdapter {
+
+  Random random;
+  boolean singleChoice = true;
+
+  // those are our singleChoice end conditions (i.e. where we start the search)
+  StringSetMatcher threadSet; // we start when all threads in the set are active
+  boolean threadsAlive = true;;
+
+  StringSetMatcher calls; // .. when any of the methods is called
+  boolean callSeen = true;
+
+  int startDepth; // .. when reaching this depth
+  boolean depthReached = true;
+
+  // set if we replay a trace
+  ChoicePoint trace;
+
+  // start the search when reaching the end of the stored trace. If not set,
+  // the listener will just randomly select single choices once the trace
+  // got processed
+  boolean searchAfterTrace;
+  
+
+  public ChoiceSelector (Config config, JPF jpf) {
+    random = new Random( config.getInt("choice.seed", 42));
+
+    threadSet = StringSetMatcher.getNonEmpty(config.getStringArray("choice.threads"));
+    if (threadSet != null) {
+      threadsAlive = false;
+    }
+
+    calls = StringSetMatcher.getNonEmpty(config.getStringArray("choice.calls"));
+    callSeen = false;
+
+    startDepth = config.getInt("choice.depth", -1);
+    if (startDepth != -1) {
+      depthReached = false;
+    }
+
+    // if nothing was specified, we just do single choice (simulation)
+    if ((threadSet == null) && (calls == null) && (startDepth == -1)) {
+      threadsAlive = false;
+      callSeen = false;
+      depthReached = false;
+    }
+
+    VM vm = jpf.getVM();
+    trace = ChoicePoint.readTrace(config.getString("choice.use_trace"), vm.getSUTName());
+    searchAfterTrace = config.getBoolean("choice.search_after_trace", true);
+    vm.setTraceReplay(trace != null);
+  }
+
+  void checkSingleChoiceCond() {
+    singleChoice = !(depthReached && callSeen && threadsAlive);
+  }
+
+  @Override
+  public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+    int n = currentCG.getTotalNumberOfChoices();
+
+    if (trace != null) { // this is a replay
+
+      // <2do> maybe that should just be a warning, and then a single choice
+      assert currentCG.getClass().getName().equals(trace.getCgClassName()) :
+        "wrong choice generator class, expecting: " + trace.getCgClassName()
+        + ", read: " + currentCG.getClass().getName();
+
+      int choiceIndex = trace.getChoiceIndex();
+      currentCG.select(choiceIndex);
+
+    } else {
+      if (singleChoice) {
+        if (n > 1) {
+          int r = random.nextInt(n);
+          currentCG.select(r); // sets it done, so we never backtrack into it
+        }
+      }
+    }
+  }
+
+  @Override
+  public void threadStarted(VM vm, ThreadInfo ti) {
+    if (singleChoice && (threadSet != null)) {
+      String tname = ti.getName();
+      if (threadSet.matchesAny( tname)){
+        threadsAlive = true;
+        checkSingleChoiceCond();
+      }
+    }
+  }
+
+  @Override
+  public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute) {
+    if (singleChoice && !callSeen && (calls != null)) {
+      if (insnToExecute instanceof JVMInvokeInstruction) {
+        String mthName = ((JVMInvokeInstruction)insnToExecute).getInvokedMethod(ti).getBaseName();
+
+        if (calls.matchesAny(mthName)){
+          callSeen = true;
+          checkSingleChoiceCond();
+        }
+      }
+    }
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+
+    if (trace != null) {
+      // there is no backtracking or restoring as long as we replay
+      trace = trace.getNext();
+
+      if (trace == null){
+        search.getVM().setTraceReplay(false);
+        if (searchAfterTrace){
+          singleChoice = false;
+        }
+      }
+
+    } else {
+      if (singleChoice && !depthReached && (startDepth >= 0)) {
+        if (search.getDepth() == startDepth) {
+          depthReached = true;
+          checkSingleChoiceCond();
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/ChoiceTracker.java b/src/main/gov/nasa/jpf/listener/ChoiceTracker.java
new file mode 100644 (file)
index 0000000..0f418d8
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+
+/**
+ * generic choice tracker tool, to produce a list of choice values that
+ * can be used to create readable replay scripts etc.
+ */
+public class ChoiceTracker extends ListenerAdapter implements PublisherExtension {
+
+  enum Format { CG, CHOICE };
+
+  Config config;
+  VM vm;
+  Search search;
+  
+  protected PrintWriter pw;
+  Class<?>[] cgClasses;
+  boolean isReportExtension;
+
+  boolean showLocation;
+  Format format = Format.CHOICE;
+  String[] excludes;
+
+  // <2do> hardwired type specific tracker for use with some shells - check if
+  // we can get rid of it
+  public ChoiceTracker (JPF jpf, String traceFileName, Class<?> cgClass){
+    config = jpf.getConfig();
+    vm = jpf.getVM();
+    search = jpf.getSearch();
+    
+    cgClasses = new Class<?>[1];
+    cgClasses[0] = cgClass;
+    
+    try {
+      pw = new PrintWriter(traceFileName);
+    } catch (FileNotFoundException fnfx) {
+      System.err.println("cannot write choice trace to file: " + traceFileName);
+      pw = new PrintWriter(System.out);
+    }
+  }
+
+  public ChoiceTracker (Config config, JPF jpf) {
+    this.config = config;
+    vm = jpf.getVM();
+    search = jpf.getSearch();
+    
+    String fname = config.getString("choice.trace");
+    if (fname == null) {
+      isReportExtension = true;
+      jpf.addPublisherExtension(ConsolePublisher.class, this);
+      // pw is going to be set later
+    } else {
+      try {
+        pw = new PrintWriter(fname);
+      } catch (FileNotFoundException fnfx) {
+        System.err.println("cannot write choice trace to file: " + fname);
+        pw = new PrintWriter(System.out);
+      }
+    }
+    
+    excludes = config.getStringArray("choice.exclude");
+    cgClasses = config.getClasses("choice.class");
+
+    format = config.getEnum("choice.format", Format.values(), Format.CG);
+    showLocation = config.getBoolean("choice.show_location", true);
+  }
+
+  public void setExcludes (String... ex) {
+    excludes=ex;
+  }
+  
+  boolean isRelevantCG (ChoiceGenerator cg){
+    if (cgClasses == null){
+      return true;
+    } else {
+      for (Class<?> cls : cgClasses){
+        if (cls.isAssignableFrom(cg.getClass())){
+          return true;
+        }
+      }
+      
+      return false;
+    }
+  }
+
+  @Override
+  public void propertyViolated (Search search) {
+        
+    if (!isReportExtension) {
+
+      pw.print("// application: ");
+      pw.println( search.getVM().getSUTDescription());
+
+      if (cgClasses == null) {
+        pw.println("// trace over all CG classes");
+      } else {
+        pw.print("// trace over CG types: ");
+        for (Class<?> cls : cgClasses){
+          pw.print(cls.getName());
+          pw.print(' ');
+        }
+        pw.println();
+      }
+
+      pw.println("//------------------------- choice trace");
+      printChoices();
+      
+      pw.println("//------------------------- end choice trace");
+      pw.flush();
+    }
+  }
+
+  void printChoices () {
+    int i = 0;
+    SystemState ss = vm.getSystemState();
+    ChoiceGenerator<?>[] cgStack = ss.getChoiceGenerators();
+
+    nextChoice:
+    for (ChoiceGenerator<?> cg : cgStack) {
+      if (isRelevantCG(cg) && !cg.isDone()){
+
+        Object choice = cg.getNextChoice();
+        if (choice == null) {
+          continue;
+        } else {
+          if (excludes != null) {
+            for (String e : excludes) {
+              if (choice.toString().startsWith(e)) {
+                continue nextChoice;
+              }
+            }
+          }
+        }
+
+        String line = null;
+
+        switch (format){
+          case CHOICE:
+            line = choice.toString();
+            if (line.startsWith("gov.nasa.jpf.vm.")){
+              line = line.substring(17);
+            }
+            break;
+          case CG:
+            line = cg.toString();
+            if (line.startsWith("gov.nasa.jpf.vm.choice.")){
+              line = line.substring(24);
+            }
+            break;
+        }
+
+        if (line != null){
+          pw.print(String.format("%4d: ", i++));
+
+          pw.print(line);
+
+          if (showLocation) {
+            String loc = cg.getSourceLocation();
+            if (loc != null) {
+              pw.println();
+              pw.print(" \tat ");
+              pw.print(loc);
+            }
+          }
+          pw.println();
+        }
+      }
+    }
+  }
+
+  //--- the PublisherExtension interface
+
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {
+    pw = publisher.getOut();
+    publisher.publishTopicStart("choice trace " + publisher.getLastErrorId());
+    printChoices();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/CoverageAnalyzer.java b/src/main/gov/nasa/jpf/listener/CoverageAnalyzer.java
new file mode 100644 (file)
index 0000000..23010ca
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.GOTO;
+import gov.nasa.jpf.jvm.bytecode.IfInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassInfoException;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ExceptionHandler;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Logger;
+
+/**
+ * a listener to report coverage statistics
+ *
+ * The idea is to collect per-class/-method/-thread information about
+ * executed instructions, and then analyze this deeper when it comes to
+ * report time (e.g. branch coverage, basic blocks, ..)
+ *
+ * Keep in mind that this is potentially a concurrent, model checked program,
+ * i.e. there is more to coverage than what hits the eye of a static analyzer
+ * (exceptions, data and thread CGs)
+ */
+public class CoverageAnalyzer extends ListenerAdapter implements PublisherExtension {
+
+  static Logger log = JPF.getLogger("gov.nasa.jpf.listener.CoverageAnalyzer");
+
+  static class Coverage {
+
+    int total;
+    int covered;
+
+    Coverage(int total, int covered) {
+      this.total = total;
+      this.covered = covered;
+    }
+
+    public void add(Coverage cov) {
+      total += cov.total;
+      covered += cov.covered;
+    }
+
+    public int percent() {
+      if (total > 0) {
+        return covered * 100 / total;
+      }
+
+      return (Integer.MIN_VALUE);
+    }
+
+    public int covered() {
+      return covered;
+    }
+
+    public int total() {
+      return total;
+    }
+
+    public boolean isPartiallyCovered() {
+      return ((covered > 0) && (covered < total));
+    }
+
+    public boolean isNotCovered() {
+      return (covered == 0);
+    }
+
+    public boolean isFullyCovered() {
+      return (covered == total);
+    }
+  }
+
+  static class MethodCoverage {
+
+    MethodInfo mi;
+
+    // we base everything else on bytecode instruction coverage
+    BitSet[] covered;
+    BitSet basicBlocks; // set on demand
+    BitSet handlers; // set on demand
+    BitSet branches; // set on demand
+    BitSet branchTrue;
+    BitSet branchFalse;
+
+    MethodCoverage(MethodInfo mi) {
+      this.mi = mi;
+      log.info("add method: " + mi.getUniqueName());
+    }
+
+    MethodInfo getMethodInfo() {
+      return mi;
+    }
+
+    void setExecuted(ThreadInfo ti, Instruction insn) {
+      int idx = ti.getId();
+
+      if (covered == null) {
+        covered = new BitSet[idx + 1];
+      } else if (idx >= covered.length) {
+        BitSet[] a = new BitSet[idx + 1];
+        System.arraycopy(covered, 0, a, 0, covered.length);
+        covered = a;
+      }
+
+      if (covered[idx] == null) {
+        covered[idx] = new BitSet(mi.getInstructions().length);
+      }
+
+      int off = insn.getInstructionIndex();
+      covered[idx].set(off);
+
+      if (showBranchCoverage && (insn instanceof IfInstruction)) {
+        if (branchTrue == null) {
+          branchTrue = new BitSet(mi.getInstructions().length);
+          branchFalse = new BitSet(branchTrue.size());
+        }
+        if (!((IfInstruction) insn).getConditionValue()) {
+          branchTrue.set(off);
+        } else {
+          branchFalse.set(off);
+        }
+      }
+    }
+
+    void setCGed(ThreadInfo ti, Instruction insn) {
+      ti = null;  // Remove IDE warning about unused variable.
+      // Hmm, we have to store the bb set at this point
+      BitSet bb = getBasicBlocks();
+      Instruction next = insn.getNext();
+      if (next != null) { // insn might be a sync return
+        bb.set(next.getInstructionIndex());
+      }
+    }
+
+    BitSet getExecutedInsn() {
+      int nTotal = mi.getInstructions().length;
+      BitSet bUnion = new BitSet(nTotal);
+
+      if (covered != null) {
+        for (BitSet b : covered) {
+          if (b != null) {
+            bUnion.or(b);
+          }
+        }
+      }
+
+      return bUnion;
+    }
+
+    Coverage getCoveredInsn() {
+      int nTotal = mi.getInstructions().length;
+
+      if (excludeHandlers) {
+        nTotal -= getHandlers().cardinality();
+      }
+
+      if (covered != null) {
+        BitSet bExec = getExecutedInsn();
+        if (excludeHandlers) {
+          bExec.andNot(getHandlers());
+        }
+        return new Coverage(nTotal, bExec.cardinality());
+      } else {
+        return new Coverage(nTotal, 0);
+      }
+    }
+
+    Coverage getCoveredLines() {
+      BitSet executable = new BitSet();
+      BitSet covered = new BitSet();
+
+      getCoveredLines(executable, covered);
+
+      return new Coverage(executable.cardinality(), covered.cardinality());
+    }
+
+    boolean getCoveredLines(BitSet executable, BitSet covered) {
+      Instruction inst[] = mi.getInstructions();
+      BitSet insn;
+      int i, line;
+
+      if (covered == null) {
+        return false;
+      }
+
+      insn = getExecutedInsn();
+      if (excludeHandlers) {
+        insn.andNot(getHandlers());
+      }
+
+      if (branchTrue != null) {
+        for (i = branchTrue.length() - 1; i >= 0; i--) {
+          boolean cTrue = branchTrue.get(i);
+          boolean cFalse = branchFalse.get(i);
+
+          if ((!cTrue || !cFalse) && (inst[i] instanceof IfInstruction)) {
+            insn.clear(i);
+          }
+        }
+      }
+
+      for (i = inst.length - 1; i >= 0; i--) {
+        line = inst[i].getLineNumber();
+         
+        if (line > 0) {
+          executable.set(line);
+          covered.set(line);
+        }
+      }
+
+      for (i = inst.length - 1; i >= 0; i--) {
+        line = inst[i].getLineNumber();
+        if ((!insn.get(i)) && (line > 0)) {         // TODO What do we do with instructions that don't have a line number.  Is this even possible?
+          covered.clear(line);
+        }
+      }
+
+      return true;
+    }
+
+    Coverage getCoveredBranches() {
+      BitSet b = getBranches();
+      int nTotal = b.cardinality();
+      int nCovered = 0;
+
+      if (branchTrue != null) {
+        int n = branchTrue.size();
+
+        for (int i = 0; i < n; i++) {
+          boolean cTrue = branchTrue.get(i);
+          boolean cFalse = branchFalse.get(i);
+
+          if (cTrue && cFalse) {
+            nCovered++;
+          }
+        }
+      }
+
+      return new Coverage(nTotal, nCovered);
+    }
+
+    BitSet getHandlerStarts() {
+      BitSet b = new BitSet(mi.getInstructions().length);
+      ExceptionHandler[] handler = mi.getExceptions();
+
+      if (handler != null) {
+        for (int i = 0; i < handler.length; i++) {
+          Instruction hs = mi.getInstructionAt(handler[i].getHandler());
+          b.set(hs.getInstructionIndex());
+        }
+      }
+
+      return b;
+    }
+
+    BitSet getHandlers() {
+      // this algorithm is a bit subtle - we walk through the code until
+      // we hit a forward GOTO (or RETURN). If the insn following the goto is the
+      // beginning of a handler, we mark everything up to the jump address
+      // as a handler block
+
+      if (handlers == null) {
+        BitSet hs = getHandlerStarts();
+        Instruction[] code = mi.getInstructions();
+        BitSet b = new BitSet(code.length);
+
+        if (!hs.isEmpty()) {
+          for (int i = 0; i < code.length; i++) {
+            Instruction insn = code[i];
+            if (insn instanceof GOTO) {
+              GOTO gotoInsn = (GOTO) insn;
+              if (!gotoInsn.isBackJump() && hs.get(i + 1)) { // jump around handler
+                int handlerEnd = gotoInsn.getTarget().getInstructionIndex();
+                for (i++; i < handlerEnd; i++) {
+                  b.set(i);
+                }
+              }
+            } else if (insn instanceof JVMReturnInstruction) { // everything else is handler
+              for (i++; i < code.length; i++) {
+                b.set(i);
+              }
+            }
+          }
+        }
+
+        handlers = b;
+      }
+
+      return handlers;
+    }
+
+    // that's kind of redundant with basic blocks, but not really - here
+    // we are interested in the logic behind branches, i.e. we want to know
+    // what the condition values were. We are not interested in GOTOs and exceptions
+    BitSet getBranches() {
+      if (branches == null) {
+        Instruction[] code = mi.getInstructions();
+        BitSet br = new BitSet(code.length);
+
+        for (int i = 0; i < code.length; i++) {
+          Instruction insn = code[i];
+          if (insn instanceof IfInstruction) {
+            br.set(i);
+          }
+        }
+
+        branches = br;
+      }
+
+      return branches;
+    }
+
+    BitSet getBasicBlocks() {
+      if (basicBlocks == null) {
+        Instruction[] code = mi.getInstructions();
+        BitSet bb = new BitSet(code.length);
+
+        bb.set(0); // first insn is always a bb start
+
+        // first, look at the insn type
+        for (int i = 0; i < code.length; i++) {
+          Instruction insn = code[i];
+          if (insn instanceof IfInstruction) {
+            IfInstruction ifInsn = (IfInstruction) insn;
+
+            Instruction tgt = ifInsn.getTarget();
+            bb.set(tgt.getInstructionIndex());
+
+            tgt = ifInsn.getNext();
+            bb.set(tgt.getInstructionIndex());
+          } else if (insn instanceof GOTO) {
+            Instruction tgt = ((GOTO) insn).getTarget();
+            bb.set(tgt.getInstructionIndex());
+          } else if (insn instanceof JVMInvokeInstruction) {
+            // hmm, this might be a bit too conservative, but who says we
+            // don't jump out of a caller into a handler, or even that we
+            // ever return from the call?
+            Instruction tgt = insn.getNext();
+            bb.set(tgt.getInstructionIndex());
+          }
+        }
+
+        // and now look at the handlers (every first insn is a bb start)
+        ExceptionHandler[] handlers = mi.getExceptions();
+        if (handlers != null) {
+          for (int i = 0; i < handlers.length; i++) {
+            Instruction tgt = mi.getInstructionAt(handlers[i].getHandler());
+            bb.set(tgt.getInstructionIndex());
+          }
+        }
+
+        basicBlocks = bb;
+
+      /** dump
+      System.out.println();
+      System.out.println(mi.getFullName());
+      for (int i=0; i<code.length; i++) {
+      System.out.print(String.format("%1$2d %2$c ",i, bb.get(i) ? '>' : ' '));
+      System.out.println(code[i]);
+      }
+       **/
+      }
+
+      return basicBlocks;
+    }
+
+    Coverage getCoveredBasicBlocks() {
+      BitSet bExec = getExecutedInsn();
+      BitSet bb = getBasicBlocks();
+      int nCov = 0;
+
+      if (excludeHandlers) {
+        BitSet handlers = getHandlers();
+        bb.and(handlers);
+      }
+
+      if (bExec != null) {
+        BitSet bCov = new BitSet(bb.size());
+        bCov.or(bb);
+        bCov.and(bExec);
+        nCov = bCov.cardinality();
+      }
+
+      return new Coverage(bb.cardinality(), nCov);
+    }
+  }
+
+  static class ClassCoverage {
+
+    String className; // need to store since we never might get a ClassInfo
+    ClassInfo ci;     // not initialized before the class is loaded
+    boolean covered;
+    HashMap<MethodInfo, MethodCoverage> methods;
+
+    ClassCoverage(String className) {
+      this.className = className;
+    }
+
+    void setLoaded(ClassInfo ci) {
+      if (methods == null) {
+        this.ci = ci;
+        covered = true;
+        log.info("used class: " + className);
+
+        methods = new HashMap<MethodInfo, MethodCoverage>();
+        for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
+          // <2do> what about MJI methods? we should report why we don't cover them
+          if (!mi.isNative() && !mi.isAbstract()) {
+            MethodCoverage mc = new MethodCoverage(mi);
+            methods.put(mi, mc);
+          }
+        }
+      }
+    }
+
+    boolean isInterface() {
+      if (ci == null)           // never loaded
+        if (!isCodeLoaded())    // can it be loaded?
+          return false;         // will never load
+
+      return ci.isInterface();
+    }
+    
+    boolean isCodeLoaded() {
+      if (ci != null)
+        return true;
+
+      try {
+        ci = ClassLoaderInfo.getCurrentResolvedClassInfo(className);
+      } catch (ClassInfoException cie) {
+        log.warning("CoverageAnalyzer problem: " + cie);   // Just log the problem but don't fail.  We still want the report to be written.
+      }
+      
+      return ci != null;
+    }
+
+    MethodCoverage getMethodCoverage(MethodInfo mi) {
+      if (methods == null) {
+        setLoaded(ci);
+      }
+      return methods.get(mi);
+    }
+
+    Coverage getCoveredMethods() {
+      Coverage cov = new Coverage(0, 0);
+
+      if (methods != null) {
+        cov.total = methods.size();
+
+        for (MethodCoverage mc : methods.values()) {
+          if (mc.covered != null) {
+            cov.covered++;
+          }
+        }
+      }
+
+      return cov;
+    }
+
+    Coverage getCoveredInsn() {
+      Coverage cov = new Coverage(0, 0);
+
+      if (methods != null) {
+        for (MethodCoverage mc : methods.values()) {
+          Coverage c = mc.getCoveredInsn();
+          cov.total += c.total;
+          cov.covered += c.covered;
+        }
+      }
+
+      return cov;
+    }
+
+    boolean getCoveredLines(BitSet executable, BitSet covered) {
+      boolean result = false;
+
+      if (methods == null) {
+        return false;
+      }
+
+      for (MethodCoverage mc : methods.values()) {
+        result = mc.getCoveredLines(executable, covered) || result;
+      }
+
+      return result;
+    }
+
+    Coverage getCoveredLines() {
+      BitSet executable = new BitSet();
+      BitSet covered = new BitSet();
+
+      getCoveredLines(executable, covered);
+
+      return new Coverage(executable.cardinality(), covered.cardinality());
+    }
+
+    Coverage getCoveredBasicBlocks() {
+      Coverage cov = new Coverage(0, 0);
+
+      if (methods != null) {
+        for (MethodCoverage mc : methods.values()) {
+          Coverage c = mc.getCoveredBasicBlocks();
+          cov.total += c.total;
+          cov.covered += c.covered;
+        }
+      }
+
+      return cov;
+    }
+
+    Coverage getCoveredBranches() {
+      Coverage cov = new Coverage(0, 0);
+
+      if (methods != null) {
+        for (MethodCoverage mc : methods.values()) {
+          Coverage c = mc.getCoveredBranches();
+          cov.total += c.total;
+          cov.covered += c.covered;
+        }
+      }
+
+      return cov;
+    }
+  }
+
+  StringSetMatcher includes = null; //  means all
+  StringSetMatcher excludes = null; //  means none
+  StringSetMatcher loaded;
+  static boolean loadedOnly; // means we only check loaded classes that are not filtered
+  static boolean showMethods;      // do we want to show per-method coverage?
+  static boolean showMethodBodies;
+  static boolean excludeHandlers;  // do we count the handlers in? (off-nominal CF)
+  static boolean showBranchCoverage; // makes only sense with showMethods
+  static boolean showRequirements; // report requirements coverage
+  HashMap<String, ClassCoverage> classes = new HashMap<String, ClassCoverage>();
+
+  public CoverageAnalyzer(Config conf, JPF jpf) {
+    includes = StringSetMatcher.getNonEmpty(conf.getStringArray("coverage.include"));
+    excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("coverage.exclude"));
+
+    showMethods = conf.getBoolean("coverage.show_methods", false);
+    showMethodBodies = conf.getBoolean("coverage.show_bodies", false);
+    excludeHandlers = conf.getBoolean("coverage.exclude_handlers", false);
+    showBranchCoverage = conf.getBoolean("coverage.show_branches", true);
+    loadedOnly = conf.getBoolean("coverage.loaded_only", true);
+    showRequirements = conf.getBoolean("coverage.show_requirements", false);
+
+    if (!loadedOnly) {
+      getCoverageCandidates(); // this might take a little while
+    }
+
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+
+  void getCoverageCandidates() {
+
+    // doesn't matter in which order we process this, since we
+    // just store one entry per qualified class name (i.e. there won't be
+    // multiple entries)
+    // NOTE : this doesn't yet deal with ClassLoaders, but that's also true for BCEL
+    ClassLoaderInfo cl = ClassLoaderInfo.getCurrentClassLoader();
+    for (String s : cl.getClassPathElements()) {
+      log.fine("analyzing classpath element: " + s);
+      File f = new File(s);
+      if (f.exists()) {
+        if (f.isDirectory()) {
+          traverseDir(f, null);
+        } else if (s.endsWith(".jar")) {
+          traverseJar(f);
+        }
+      }
+    }
+  }
+
+  void addClassEntry(String clsName) {
+    ClassCoverage cc = new ClassCoverage(clsName);
+    classes.put(clsName, cc);
+    log.info("added class candidate: " + clsName);
+  }
+
+  boolean isAnalyzedClass(String clsName) {
+    return StringSetMatcher.isMatch(clsName, includes, excludes);
+  }
+
+  void traverseDir(File dir, String pkgPrefix) {
+    for (File f : dir.listFiles()) {
+      if (f.isDirectory()) {
+        String prefix = f.getName();
+        if (pkgPrefix != null) {
+          prefix = pkgPrefix + '.' + prefix;
+        }
+        traverseDir(f, prefix);
+      } else {
+        String fname = f.getName();
+        if (fname.endsWith(".class")) {
+          if (f.canRead() && (f.length() > 0)) {
+            String clsName = fname.substring(0, fname.length() - 6);
+            if (pkgPrefix != null) {
+              clsName = pkgPrefix + '.' + clsName;
+            }
+
+            if (isAnalyzedClass(clsName)) {
+              addClassEntry(clsName);
+            }
+          } else {
+            log.warning("cannot read class file: " + fname);
+          }
+        }
+      }
+    }
+  }
+
+  void traverseJar(File jar) {
+    try {
+      JarFile jf = new JarFile(jar);
+      for (Enumeration<JarEntry> entries = jf.entries(); entries.hasMoreElements();) {
+        JarEntry e = entries.nextElement();
+        if (!e.isDirectory()) {
+          String eName = e.getName();
+          if (eName.endsWith(".class")) {
+            if (e.getSize() > 0) {
+              String clsName = eName.substring(0, eName.length() - 6);
+              clsName = clsName.replace('/', '.');
+              if (isAnalyzedClass(clsName)) {
+                addClassEntry(clsName);
+              }
+            } else {
+              log.warning("cannot read jar entry: " + eName);
+            }
+          }
+        }
+      }
+    } catch (IOException iox) {
+      iox.printStackTrace();
+    }
+  }
+
+
+  HashMap<String, Integer> getGlobalRequirementsMethods() {
+    HashMap<String, Integer> map = new HashMap<String, Integer>();
+
+    // <2do> this is extremely braindead, since inefficient
+    // it would be much better to do this with Class<?> instead of ClassInfo, but
+    // then we need a JPF- ClassLoader, since the path might be different. As a
+    // middle ground, we could use BCEL
+
+    for (ClassCoverage cc : classes.values()) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cc.className);
+      for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
+        AnnotationInfo ai = getRequirementsAnnotation(mi);
+        if (ai != null) {
+          for (String id : ai.getValueAsStringArray()) {
+            Integer n = map.get(id);
+            if (n == null) {
+              map.put(id, 1);
+            } else {
+              map.put(id, n + 1);
+            }
+          }
+        }
+      }
+    }
+
+    return map;
+  }
+
+  int computeTotalRequirementsMethods(HashMap<String, Integer> map) {
+    int n = 0;
+    for (Integer i : map.values()) {
+      n += i;
+    }
+    return n;
+  }
+
+  private void computeCoverages(String packageFilter, List<Map.Entry<String, ClassCoverage>> clsEntries, Coverage cls, Coverage mth, Coverage branch, Coverage block, Coverage line, Coverage insn) {
+    for (Map.Entry<String, ClassCoverage> e : clsEntries) {
+      if (e.getKey().startsWith(packageFilter)) {
+        ClassCoverage cc = e.getValue();
+
+        if (cc.isInterface()) {
+          continue; // no code
+        }
+
+        cls.total++;
+        
+        if (cc.covered) {
+          cls.covered++;
+        }
+
+        insn.add(cc.getCoveredInsn());
+        line.add(cc.getCoveredLines());
+        block.add(cc.getCoveredBasicBlocks());
+        branch.add(cc.getCoveredBranches());
+        mth.add(cc.getCoveredMethods());
+      }
+    }
+  }
+
+
+  /**
+   * print uncovered source ranges
+   */
+
+  //-------- the listener interface
+  @Override
+  public void classLoaded(VM vm, ClassInfo ci) {
+    String clsName = ci.getName();
+
+    if (loadedOnly) {
+      if (isAnalyzedClass(clsName)) {
+        addClassEntry(clsName);
+      }
+    }
+
+    ClassCoverage cc = classes.get(clsName);
+    if (cc != null) {
+      cc.setLoaded(ci);
+    }
+  }
+  MethodInfo lastMi = null;
+  MethodCoverage lastMc = null;
+
+  MethodCoverage getMethodCoverage(Instruction insn) {
+
+    if (!insn.isExtendedInstruction()) {
+      MethodInfo mi = insn.getMethodInfo();
+      if (mi != lastMi) {
+        lastMc = null;
+        lastMi = mi;
+        ClassInfo ci = mi.getClassInfo();
+        if (ci != null) {
+          ClassCoverage cc = classes.get(ci.getName());
+          if (cc != null) {
+            lastMc = cc.getMethodCoverage(mi);
+          }
+        }
+      }
+
+      return lastMc;
+    }
+
+    return null;
+  }
+  HashMap<String, HashSet<MethodCoverage>> requirements;
+
+  void updateRequirementsCoverage(String[] ids, MethodCoverage mc) {
+    if (requirements == null) {
+      requirements = new HashMap<String, HashSet<MethodCoverage>>();
+    }
+
+    for (String id : ids) {
+      HashSet<MethodCoverage> mcs = requirements.get(id);
+      if (mcs == null) {
+        mcs = new HashSet<MethodCoverage>();
+        requirements.put(id, mcs);
+      }
+
+      if (!mcs.contains(mc)) {
+        mcs.add(mc);
+      }
+    }
+  }
+
+  AnnotationInfo getRequirementsAnnotation(MethodInfo mi) {
+    // <2do> should probably look for overridden method annotations
+    return mi.getAnnotation("gov.nasa.jpf.Requirement");
+  }
+
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    MethodCoverage mc = getMethodCoverage(executedInsn);
+
+    if (mc != null) {
+      mc.setExecuted(ti, executedInsn);
+
+      if (showRequirements) {
+        if (executedInsn.getPosition() == 0) { // first insn in method, check for Requirements
+          AnnotationInfo ai = getRequirementsAnnotation(mc.getMethodInfo());
+          if (ai != null) {
+            String[] ids = ai.getValueAsStringArray();
+            updateRequirementsCoverage(ids, mc);
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void choiceGeneratorSet(VM vm, ChoiceGenerator<?> newCG) {
+    /*** should be an option
+    Instruction insn = vm.getLastInstruction();
+    MethodCoverage mc = getMethodCoverage(vm);
+    mc.setCGed(vm.getLastThreadInfo(),insn);
+     ***/
+  }
+
+  //-------- the publisher interface
+  private Publisher publisher;
+  private ArrayList<Map.Entry<String, ClassCoverage>> clsEntries;
+
+
+  abstract class Publish {
+    PrintWriter pw;
+
+    Publish () {}
+    Publish (Publisher p){
+      pw = p.getOut();
+    }
+
+    abstract void publish();
+    abstract void printClassCoverages();
+    abstract void printRequirementsCoverage();
+  }
+
+  class PublishConsole extends Publish {
+    PublishConsole (ConsolePublisher p){
+      super(p);
+    }
+
+    @Override
+       void publish() {
+      publisher.publishTopicStart("coverage statistics");
+
+      printClassCoverages();
+
+      if (showRequirements) {
+        printRequirementsCoverage();
+      }
+
+    }
+
+    void printCoverage (Coverage cov){
+      int nTotal = cov.total();
+      int nCovered = cov.covered();
+
+      String s;
+      if (nTotal <= 0) {
+        s = " -  ";
+      } else {
+        s = String.format("%.2f (%d/%d)", ((double) nCovered / nTotal), nCovered, nTotal);
+      }
+      pw.print(String.format("%1$-18s", s));
+    }
+
+
+    @Override
+       void printClassCoverages() {
+      String space = "  ";
+      Coverage clsCoverage = new Coverage(0, 0);
+      Coverage mthCoverage = new Coverage(0, 0);
+      Coverage bbCoverage = new Coverage(0, 0);
+      Coverage lineCoverage = new Coverage(0, 0);
+      Coverage insnCoverage = new Coverage(0, 0);
+      Coverage branchCoverage = new Coverage(0, 0);
+
+      computeCoverages("", clsEntries, clsCoverage, mthCoverage, branchCoverage, bbCoverage, lineCoverage, insnCoverage);
+
+      pw.println();
+      pw.println("-------------------------------------------- class coverage ------------------------------------------------");
+      pw.println("bytecode            line                basic-block         branch              methods             location");
+      pw.println("------------------------------------------------------------------------------------------------------------");
+
+      // Write Body
+      for (Map.Entry<String, ClassCoverage> e : clsEntries) {
+        ClassCoverage cc = e.getValue();
+
+        printCoverage(cc.getCoveredInsn());
+        pw.print(space);
+
+        printCoverage(cc.getCoveredLines());
+        pw.print(space);
+
+        printCoverage(cc.getCoveredBasicBlocks());
+        pw.print(space);
+
+        printCoverage(cc.getCoveredBranches());
+        pw.print(space);
+
+        printCoverage(cc.getCoveredMethods());
+        pw.print(space);
+
+        pw.println(e.getKey());
+
+        if (showMethods) {
+          printMethodCoverages(cc);
+        }
+      }
+
+      pw.println();
+      pw.println("------------------------------------------------------------------------------------------------------------");
+
+      printCoverage(insnCoverage);
+      pw.print(space);
+      printCoverage(lineCoverage);
+      pw.print(space);
+      printCoverage(bbCoverage);
+      pw.print(space);
+      printCoverage(branchCoverage);
+      pw.print(space);
+      printCoverage(mthCoverage);
+      pw.print(space);
+      printCoverage(clsCoverage);
+      pw.println(" total");
+
+    }
+
+    @Override
+       void printRequirementsCoverage() {
+      HashMap<String, Integer> reqMethods = getGlobalRequirementsMethods();
+
+      String space = "  ";
+      Coverage bbAll = new Coverage(0, 0);
+      Coverage insnAll = new Coverage(0, 0);
+      Coverage branchAll = new Coverage(0, 0);
+      Coverage mthAll = new Coverage(0, 0);
+      Coverage reqAll = new Coverage(0, 0);
+
+      reqAll.total = reqMethods.size();
+      mthAll.total = computeTotalRequirementsMethods(reqMethods);
+
+      pw.println();
+      pw.println();
+      pw.println("--------------------------------- requirements coverage -----------------------------------");
+      pw.println("bytecode            basic-block         branch              methods             requirement");
+      pw.println("-------------------------------------------------------------------------------------------");
+
+      for (String id : Misc.getSortedKeyStrings(reqMethods)) {
+
+        Coverage bbCoverage = new Coverage(0, 0);
+        Coverage insnCoverage = new Coverage(0, 0);
+        Coverage branchCoverage = new Coverage(0, 0);
+        Coverage reqMth = new Coverage(reqMethods.get(id), 0);
+
+        if (requirements != null && requirements.containsKey(id)) {
+          reqAll.covered++;
+          for (MethodCoverage mc : requirements.get(id)) {
+            insnCoverage.add(mc.getCoveredInsn());
+            bbCoverage.add(mc.getCoveredBasicBlocks());
+            branchCoverage.add(mc.getCoveredBranches());
+
+            mthAll.covered++;
+            reqMth.covered++;
+          }
+
+
+          printCoverage(insnCoverage);
+          pw.print(space);
+          printCoverage(bbCoverage);
+          pw.print(space);
+          printCoverage(branchCoverage);
+          pw.print(space);
+          printCoverage(reqMth);
+          pw.print("\"" + id + "\"");
+
+
+          pw.println();
+
+          if (showMethods) {
+            for (MethodCoverage mc : requirements.get(id)) {
+
+              pw.print(space);
+              printCoverage(mc.getCoveredInsn());
+              pw.print(space);
+              printCoverage(mc.getCoveredBasicBlocks());
+              pw.print(space);
+              printCoverage(mc.getCoveredBranches());
+              pw.print(space);
+
+              pw.print(mc.getMethodInfo().getFullName());
+              pw.println();
+            }
+          }
+        } else { // requirement not covered
+          pw.print(" -                   -                   -                  ");
+
+          printCoverage(reqMth);
+          pw.print("\"" + id + "\"");
+          pw.println();
+        }
+
+        insnAll.add(insnCoverage);
+        bbAll.add(bbCoverage);
+        branchAll.add(branchCoverage);
+      }
+
+      pw.println();
+      pw.println("------------------------------------------------------------------------------------------");
+
+      printCoverage(insnAll);
+      pw.print(space);
+      printCoverage(bbAll);
+      pw.print(space);
+      printCoverage(branchAll);
+      pw.print(space);
+      printCoverage(mthAll);
+      pw.print(space);
+      printCoverage(reqAll);
+      pw.print(" total");
+
+      pw.println();
+    }
+
+    void printMethodCoverages(ClassCoverage cc) {
+      String space = "  ";
+      boolean result = true;
+
+      if (cc.methods == null) {
+        return;
+      }
+
+      ArrayList<Map.Entry<MethodInfo, MethodCoverage>> mthEntries =
+              Misc.createSortedEntryList(cc.methods, new Comparator<Map.Entry<MethodInfo, MethodCoverage>>() {
+
+        @Override
+               public int compare(Map.Entry<MethodInfo, MethodCoverage> o1,
+                Map.Entry<MethodInfo, MethodCoverage> o2) {
+          int a = o2.getValue().getCoveredInsn().percent();
+          int b = o1.getValue().getCoveredInsn().percent();
+
+          if (a == b) {
+            return o2.getKey().getUniqueName().compareTo(o1.getKey().getUniqueName());
+          } else {
+            return a - b;
+          }
+        }
+      });
+
+      Coverage emptyCoverage = new Coverage(0, 0);
+
+      for (Map.Entry<MethodInfo, MethodCoverage> e : mthEntries) {
+        MethodCoverage mc = e.getValue();
+        MethodInfo mi = mc.getMethodInfo();
+        Coverage insnCoverage = mc.getCoveredInsn();
+        Coverage lineCoverage = mc.getCoveredLines();
+        Coverage branchCoverage = mc.getCoveredBranches();
+
+        result = result && insnCoverage.isFullyCovered();
+
+
+        pw.print(space);
+        printCoverage(insnCoverage);
+
+        pw.print(space);
+        printCoverage(lineCoverage);
+
+        pw.print(space);
+        printCoverage(mc.getCoveredBasicBlocks());
+
+        pw.print(space);
+        printCoverage(branchCoverage);
+
+        pw.print(space);
+        printCoverage(emptyCoverage);
+
+        pw.print(space);
+        pw.print(mi.getLongName());
+        pw.println();
+
+        if (showMethodBodies &&
+                (!insnCoverage.isFullyCovered() || !branchCoverage.isFullyCovered())) {
+          printBodyCoverage(mc);
+        }
+      }
+    }
+
+    void printBodyCoverage(MethodCoverage mc) {
+      MethodInfo mi = mc.getMethodInfo();
+      Instruction[] code = mi.getInstructions();
+      BitSet cov = mc.getExecutedInsn();
+      int i, start = -1;
+
+      BitSet handlers = mc.getHandlers();
+
+      if (excludeHandlers) {
+        cov.andNot(handlers);
+      }
+
+      for (i = 0; i < code.length; i++) {
+        if (!cov.get(i)) { // not covered
+          if (start == -1) {
+            start = i;
+          }
+        } else { // covered
+          if (start != -1) {
+            printSourceRange(code, handlers, start, i - 1, "");
+            start = -1;
+          }
+        }
+      }
+      if (start != -1) {
+        printSourceRange(code, handlers, start, i - 1, "");
+      }
+
+      // now print the missing branches
+      BitSet branches = mc.getBranches();
+      lastStart = -1; // reset in case condition and branch are in same line
+      for (i = 0; i < code.length; i++) {
+        if (branches.get(i)) {
+          String prefix = "";
+          BitSet bTrue = mc.branchTrue;
+          BitSet bFalse = mc.branchFalse;
+          if (bTrue != null) { // means we have condition bit sets
+            boolean cTrue = bTrue.get(i);
+            boolean cFalse = bFalse.get(i);
+            if (cTrue) {
+              prefix = cFalse ? "" : "F "; // covered or false missing
+            } else {
+              prefix = cFalse ? "T " : "N "; // true or both missing
+            }
+          } else {
+            prefix = "N ";                   // not covered at all
+          }
+
+          if (prefix != null) {
+            printSourceRange(code, handlers, i, i, prefix);
+          }
+        }
+      }
+    }
+
+    // there might be several ranges within the same source line
+    int lastStart = -1;
+
+    void printSourceRange(Instruction[] code, BitSet handlers,
+            int start, int end, String prefix) {
+
+      int line = code[start].getLineNumber();
+
+      if (lastStart == line) {
+        return;
+      }
+
+      lastStart = line;
+
+      printLocation(prefix, "at", code[start].getSourceLocation(), handlers.get(start) ? "x" : "");
+
+      if (line != code[end].getLineNumber()) {
+        printLocation(prefix, "..", code[end].getSourceLocation(), handlers.get(end) ? "x" : "");
+      }
+    // we need the "(..)" wrapper so that Eclipse parses this
+    // as a source location when displaying the report in a console
+    }
+
+    private void printLocation(String prefix, String at, String location, String suffix) {
+
+      printBlanks(pw, 84);
+      pw.print(prefix);
+      pw.print(at);
+      pw.print(' ');
+      pw.print(location);
+      pw.print(' ');
+      pw.println(suffix);
+    }
+
+    void printBlanks(PrintWriter pw, int n) {
+      for (int i = 0; i < n; i++) {
+        pw.print(' ');
+      }
+    }
+
+  }
+
+
+  @Override
+  public void publishFinished(Publisher publisher) {
+
+    if (clsEntries == null) {
+      clsEntries = Misc.createSortedEntryList(classes, new Comparator<Map.Entry<String, ClassCoverage>>() {
+
+        @Override
+               public int compare(Map.Entry<String, ClassCoverage> o1,
+                Map.Entry<String, ClassCoverage> o2) {
+          return o2.getKey().compareTo(o1.getKey());
+        }
+      });
+    }
+
+    this.publisher = publisher;
+
+    if (publisher instanceof ConsolePublisher) {
+      new PublishConsole((ConsolePublisher) publisher).publish();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/DeadlockAnalyzer.java b/src/main/gov/nasa/jpf/listener/DeadlockAnalyzer.java
new file mode 100644 (file)
index 0000000..e434d17
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.ListIterator;
+import java.util.Stack;
+
+/**
+ * example of a listener that creates property specific traces. The interesting
+ * thing is that it does so without the need to store steps, i.e. it maintains
+ * it's own transition stack.
+ * this is still work in progress, analyzing the trace can be much more
+ * elaborate (we just dump up to a max history size for now)
+ * 
+ * <2do> DeadlockAnalyzer output can be confusing if a reorganizing
+ * ThreadList is used (which reassigns thread ids) 
+ */
+public class DeadlockAnalyzer extends ListenerAdapter {
+
+  enum OpType { block, lock, unlock, wait, notify, notifyAll, started, terminated };
+  static String[] opTypeMnemonic = { "B", "L", "U", "W", "N", "A", "S", "T" };
+  
+  static class ThreadOp {  
+    ThreadInfo ti;
+    ElementInfo ei;
+    Instruction insn;
+    
+    // kind of redundant, but there might be context attributes in addition
+    // to the insn itself
+    OpType opType;
+    
+    // we could identify this with the insn, but only in case this is
+    // a transition boundary, which is far less general than we can be
+    int stateId;
+    ThreadOp prevTransition;
+    ThreadOp prevOp;
+
+    ThreadOp (ThreadInfo ti, ElementInfo ei, OpType type) {
+      this.ti = ti;
+      this.ei = ei;
+      insn = getReportInsn(ti); // we haven't had the executeInsn notification yet
+      opType = type;
+      
+      prevOp = null;
+    }
+
+    Instruction getReportInsn(ThreadInfo ti){
+      StackFrame frame = ti.getTopFrame();
+      if (frame != null) {
+        Instruction insn = frame.getPC();
+        if (insn instanceof EXECUTENATIVE) {
+          frame = frame.getPrevious();
+          if (frame != null) {
+            insn = frame.getPC();
+          }
+        }
+
+        return insn;
+      } else {
+        return null;
+      }
+    }
+
+    void printLocOn (PrintWriter pw) {
+      pw.print(String.format("%6d", new Integer(stateId)));
+      
+      if (insn != null) {
+        pw.print(String.format(" %18.18s ", insn.getMnemonic()));
+        pw.print(insn.getFileLocation());
+        String line = insn.getSourceLine();
+        if (line != null){
+          pw.print( " : ");
+          pw.print(line.trim());
+          //pw.print(insn);
+        }
+      }
+    }
+    
+    void printOn (PrintWriter pw){
+      pw.print( stateId);
+      pw.print( " : ");
+      pw.print( ti.getName());
+      pw.print( " : ");
+      pw.print( opType.name());
+      pw.print( " : ");
+      pw.println(ei);
+    }
+    
+    @Override
+       public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append( stateId);
+      sb.append( " : ");
+      sb.append( ti.getName());
+      sb.append( " : ");
+      sb.append( opType.name());
+      sb.append( " : ");
+      sb.append(ei);
+      return sb.toString();
+    }
+    
+    void printColumnOn(PrintWriter pw, Collection<ThreadInfo> tlist){
+      for (ThreadInfo t : tlist) {
+        if (ti == t) {
+          if (opType == OpType.started || opType == OpType.terminated) {
+            pw.print(String.format("   %1$s    ", opTypeMnemonic[opType.ordinal()]));
+          } else {
+            pw.print(String.format("%1$s:%2$-5x ", opTypeMnemonic[opType.ordinal()], ei.getObjectRef()));
+          }
+          //break;
+        } else {
+          pw.print("   |    ");
+        }
+      }      
+    }
+  }
+  
+  ThreadOp lastOp;
+  ThreadOp lastTransition;
+  
+  int maxHistory;
+  String format;
+  
+  VM vm;
+  Search search;
+  
+  public DeadlockAnalyzer (Config config, JPF jpf){
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+    
+    maxHistory = config.getInt("deadlock.max_history", Integer.MAX_VALUE);
+    format = config.getString("deadlock.format", "essential");
+    
+    vm = jpf.getVM();
+    search = jpf.getSearch();
+  }
+  
+  boolean requireAllOps() {
+    return (format.equalsIgnoreCase("essential"));
+  }
+  
+  void addOp (ThreadInfo ti, ElementInfo ei, OpType opType){
+    ThreadOp op = new ThreadOp(ti, ei, opType);
+    if (lastOp == null){
+      lastOp = op;
+    } else {
+      assert lastOp.stateId == 0;
+      
+      op.prevOp = lastOp;
+      lastOp = op;
+    }
+  }
+  
+  void printRawOps (PrintWriter pw) {
+    int i=0;
+    
+    for (ThreadOp tOp = lastTransition; tOp != null; tOp = tOp.prevTransition){
+      for (ThreadOp op = tOp; op != null; op=op.prevOp) {
+        if (i++ >= maxHistory){
+          pw.println("...");
+          return;
+        }
+        op.printOn(pw);
+      }
+    }
+  }
+      
+  /**
+   * include all threads that are currently blocked or waiting, and
+   * all the threads that had the last interaction with them. Note that
+   * we do this completely on the basis of the recorded ThreadOps, i.e.
+   * don't rely on when this is called
+   */
+  void printEssentialOps(PrintWriter pw) {
+    LinkedHashSet<ThreadInfo> threads = new LinkedHashSet<ThreadInfo>();
+    ArrayList<ThreadOp> ops = new ArrayList<ThreadOp>();
+    HashMap<ElementInfo,ThreadInfo> waits = new HashMap<ElementInfo,ThreadInfo>();
+    HashMap<ElementInfo,ThreadInfo> blocks = new HashMap<ElementInfo,ThreadInfo>();
+    HashSet<ThreadInfo> runnables = new HashSet<ThreadInfo>();
+    
+    // collect all relevant threads and ops
+    for (ThreadOp trans = lastTransition; trans != null; trans = trans.prevTransition){
+      for (ThreadOp tOp = trans; tOp != null; tOp = tOp.prevOp) {
+        OpType ot = tOp.opType;
+        ThreadInfo oti = tOp.ti;
+        
+        if (ot == OpType.wait || ot == OpType.block) {
+          if (!runnables.contains(oti) && !threads.contains(oti)){
+            HashMap<ElementInfo, ThreadInfo> map = (ot == OpType.block) ? blocks : waits;
+            threads.add(oti);
+            map.put(tOp.ei, oti);
+            ops.add(tOp);
+          }
+          
+        } else if (ot == OpType.notify || ot == OpType.notifyAll || ot == OpType.lock) {
+          HashMap<ElementInfo, ThreadInfo> map = (ot == OpType.lock) ? blocks : waits;
+          ThreadInfo ti = map.get(tOp.ei);
+          
+          if (ti != null && ti != oti){
+            if (!threads.contains(oti)){
+              threads.add(oti);
+            }
+            map.remove(tOp.ei);
+            ops.add(tOp);
+          }
+          
+          runnables.add(oti);
+
+        } else if (ot == OpType.unlock) {
+          // not relevant
+          runnables.add(oti);
+          
+        } else if (ot == OpType.terminated || ot == OpType.started) {
+          ops.add(tOp); // might be removed later-on
+        }
+      }
+    }
+
+    // remove all starts/terminates of irrelevant threads
+    for (ListIterator<ThreadOp> it = ops.listIterator(); it.hasNext(); ){
+      ThreadOp tOp = it.next();
+      if (tOp.opType == OpType.terminated || tOp.opType == OpType.started) {
+        if (!threads.contains(tOp.ti)){
+          it.remove();
+        }
+      }
+    }
+    
+    // now we are ready to print
+    printHeader(pw,threads);
+
+    for (ThreadOp tOp : ops) {
+      tOp.printColumnOn(pw,threads);
+      tOp.printLocOn(pw);
+      pw.println();          
+    }
+  }
+    
+  
+  Collection<ThreadInfo> getThreadList() {
+    ArrayList<ThreadInfo> tcol = new ArrayList<ThreadInfo>();
+    boolean allOps = requireAllOps();
+    int i=0;
+    
+    prevTrans:
+    for (ThreadOp tOp = lastTransition; tOp != null; tOp = tOp.prevTransition){
+      i++;
+      if (!allOps && (i >= maxHistory)){
+        break;
+      }
+      
+      for (ThreadInfo ti : tcol) {
+        if (ti == tOp.ti) continue prevTrans;
+      }
+      tcol.add(tOp.ti);
+    }
+    
+    return tcol;
+  }
+  
+  void printHeader (PrintWriter pw, Collection<ThreadInfo> tlist){
+    for (ThreadInfo ti : tlist){
+      pw.print(String.format("  %1$2d    ", ti.getId()));
+    }
+    pw.print(" trans      insn          loc                : stmt");
+    pw.println();
+        
+    for (int i=0; i<tlist.size(); i++){
+      pw.print("------- ");
+    }
+    pw.print("---------------------------------------------------");
+    pw.println();
+  }
+
+  
+  void printColumnOps (PrintWriter pw){
+    int i = 0;
+    Collection<ThreadInfo> tlist = getThreadList();
+    printHeader(pw,tlist);
+    
+    // and now the data
+    for (ThreadOp tOp = lastTransition; tOp != null; tOp = tOp.prevTransition){
+      for (ThreadOp op = tOp; op != null; op=op.prevOp) {
+        if (i++ >= maxHistory){
+          pw.println("...");
+          return;
+        }
+        
+        op.printColumnOn(pw,tlist);
+        op.printLocOn(pw);
+        pw.println();
+      }
+    }
+  }
+    
+  /**
+   * this is the workhorse - filter which ops should be shown, and which
+   * are irrelevant for the deadlock
+   */
+  boolean showOp (ThreadOp op, ThreadInfo[] tlist,
+                  boolean[] waitSeen, boolean[] notifySeen,
+                  boolean[] blockSeen, boolean[] lockSeen,
+                  Stack<ElementInfo>[] unlocked) {
+    ThreadInfo ti = op.ti;
+    ElementInfo ei = op.ei;
+    int idx;
+    for (idx=0; idx < tlist.length; idx++){
+      if (tlist[idx] == ti) break;
+    }
+    
+    // we could delegate this to the enum type, but let's not be too fancy
+    switch (op.opType) {
+    case block:
+      // only report the last one if thread is blocked
+      if (ti.isBlocked()) {
+        if (!blockSeen[idx]) {
+          blockSeen[idx] = true;
+          return true;
+        }        
+      }
+      return false;
+    
+    case unlock:
+      unlocked[idx].push(ei);
+      return false;
+      
+    case lock:
+      // if we had a corresponding unlock, ignore
+      if (!unlocked[idx].isEmpty() && (unlocked[idx].peek() == ei)) {
+        unlocked[idx].pop();
+        return false;
+      }
+      
+      // only report the last one if there is a thread that's currently blocked on it
+      for (int i=0; i<tlist.length; i++){
+        if ((i != idx) && tlist[i].isBlocked() && (tlist[i].getLockObject() == ei)) {
+          if (!lockSeen[i]){
+            lockSeen[i] = true;
+            return true;
+          }
+        }
+      }
+      
+      return false;
+      
+    case wait:
+      if (ti.isWaiting()){ // only show the last one if this is a waiting thread
+        if (!waitSeen[idx]) {
+          waitSeen[idx] = true;
+          return true;
+        }
+      }
+      
+      return false;
+      
+    case notify:
+    case notifyAll:
+      // only report the last one if there's a thread waiting on it
+      for (int i=0; i<tlist.length; i++){
+        if ((i != idx) && tlist[i].isWaiting() && (tlist[i].getLockObject() == ei)) {
+          if (!notifySeen[i]) {
+            notifySeen[i] = true;
+            return true;
+          }
+        }
+      }
+
+      return false;
+      
+    case started:
+    case terminated:
+      return true;
+    }
+    
+    return false;
+  }
+
+  void storeLastTransition(){
+    if (lastOp != null) {
+      int stateId = search.getStateId();
+      ThreadInfo ti = lastOp.ti;
+
+      for (ThreadOp op = lastOp; op != null; op = op.prevOp) {
+        assert op.stateId == 0;
+
+        op.stateId = stateId;
+      }
+
+      lastOp.prevTransition = lastTransition;
+      lastTransition = lastOp;
+
+      lastOp = null;
+    }
+  }
+
+  //--- VM listener interface
+  
+  @Override
+  public void objectLocked (VM vm, ThreadInfo ti, ElementInfo ei) {
+    addOp(ti, ei, OpType.lock);
+  }
+
+  @Override
+  public void objectUnlocked (VM vm, ThreadInfo ti, ElementInfo ei) {
+    addOp(ti, ei, OpType.unlock);
+  }
+
+  @Override
+  public void objectWait (VM vm, ThreadInfo ti, ElementInfo ei) {
+    addOp(ti, ei, OpType.wait);
+  }
+
+  @Override
+  public void objectNotify (VM vm, ThreadInfo ti, ElementInfo ei) {
+    addOp(ti, ei, OpType.notify);
+  }
+
+  @Override
+  public void objectNotifyAll (VM vm, ThreadInfo ti, ElementInfo ei) {
+    addOp(ti, ei, OpType.notifyAll);
+  }
+
+  @Override
+  public void threadBlocked (VM vm, ThreadInfo ti, ElementInfo ei){
+    addOp(ti, ei, OpType.block);
+  }
+  
+  @Override
+  public void threadStarted (VM vm, ThreadInfo ti){
+    addOp(ti, null, OpType.started);    
+  }
+  
+  @Override
+  public void threadTerminated (VM vm, ThreadInfo ti){
+    addOp(ti, null, OpType.terminated);
+  }
+  
+  //--- SearchListener interface
+
+  @Override
+  public void stateAdvanced (Search search){
+    if (search.isNewState()) {
+      storeLastTransition();
+    }
+  }
+
+  @Override
+  public void stateBacktracked (Search search){
+    int stateId = search.getStateId();
+    while ((lastTransition != null) && (lastTransition.stateId > stateId)){
+      lastTransition = lastTransition.prevTransition;
+    }
+    lastOp = null;
+  }
+  
+  // for HeuristicSearches. Ok, that's braindead but at least no need for cloning
+  HashMap<Integer,ThreadOp> storedTransition = new HashMap<Integer,ThreadOp>();
+  
+  @Override
+  public void stateStored (Search search) {
+    // always called after stateAdvanced
+    storedTransition.put(search.getStateId(), lastTransition);
+  }
+  
+  @Override
+  public void stateRestored (Search search) {
+    int stateId = search.getStateId();
+    ThreadOp op = storedTransition.get(stateId);
+    if (op != null) {
+      lastTransition = op;
+      storedTransition.remove(stateId);  // not strictly required, but we don't come back
+    }
+  }
+  
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {
+    PrintWriter pw = publisher.getOut();
+    publisher.publishTopicStart("thread ops " + publisher.getLastErrorId());
+    
+    if ("column".equalsIgnoreCase(format)){
+      printColumnOps(pw);
+    } else if ("essential".equalsIgnoreCase(format)) {
+      printEssentialOps(pw);
+    } else {
+      printRawOps(pw);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/DistributedSimpleDot.java b/src/main/gov/nasa/jpf/listener/DistributedSimpleDot.java
new file mode 100644 (file)
index 0000000..d41a8c4
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.jvm.bytecode.DIRECTCALLRETURN;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.LockInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.GlobalSchedulingPoint;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * This is a Graphviz dot-file generator similar to SimpleDot. It is useful in
+ * the case of Multiprocess applications. It distinguishes local choices from global
+ * choices.
+ * 
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class DistributedSimpleDot extends SimpleDot {
+
+  static final String MP_START_NODE_ATTRS = "shape=octagon,fillcolor=green";
+  static final String MP_NODE_ATTRS = "shape=octagon,fillcolor=azure2";
+  
+  protected String mpNodeAttrs;
+  protected String mpStartNodeAttrs;
+  
+  protected Instruction insn;
+  
+  public DistributedSimpleDot (Config config, JPF jpf) {
+    super(config, jpf);
+    
+    mpNodeAttrs = config.getString("dot.mp_node_attr", MP_NODE_ATTRS);
+    startNodeAttrs = config.getString("dot.mp_start_node_attr", MP_START_NODE_ATTRS);
+  }
+
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo currentThread, Instruction nextInstruction, Instruction executedInstruction) {
+    insn = executedInstruction;
+  }
+  
+  @Override
+  public void stateAdvanced(Search search){
+    int id = search.getStateId();
+    long edgeId = ((long)lastId << 32) | id;
+    
+    String prcId = "P"+Integer.toString(search.getVM().getCurrentApplicationContext().getId());
+    if (id <0 || seenEdges.contains(edgeId)){
+      return; // skip the root state and property violations (reported separately)
+    }
+    
+    String lastInst = getNextCG();
+    String choice =  prcId+"."+getLastChoice();
+    
+    if (search.isErrorState()) {
+      String eid = "e" + search.getNumberOfErrors();
+      printTransition(getStateId(lastId), eid, choice, getError(search));
+      printErrorState(eid);
+      lastErrorId = eid;
+
+    } else if (search.isNewState()) {
+      
+      if (search.isEndState()) {
+        printTransition(getStateId(lastId), getStateId(id), choice, lastInst);
+        printEndState(getStateId(id));
+      } else {
+        printTransition(getStateId(lastId), getStateId(id), choice, lastInst);
+        printMultiProcessState(getStateId(id));
+      }
+
+    } else { // already visited state
+      printTransition(getStateId(lastId), getStateId(id), choice, lastInst);
+    }
+
+    seenEdges.add(edgeId);
+    lastId = id;
+  }
+  
+  @Override
+  protected String getNextCG(){
+    if (insn instanceof EXECUTENATIVE) {
+      return getNativeExecCG((EXECUTENATIVE)insn);
+
+    } else if (insn instanceof JVMFieldInstruction) { // shared object field access
+      return getFieldAccessCG((JVMFieldInstruction)insn);
+
+    } else if (insn instanceof LockInstruction){ // monitor_enter
+      return getLockCG((LockInstruction)insn);
+
+    } else if (insn instanceof JVMInvokeInstruction){ // sync method invoke
+      return getInvokeCG((JVMInvokeInstruction)insn);
+    } else if(insn instanceof DIRECTCALLRETURN && vm.getCurrentThread().getNextPC()==null) {
+      return "return";
+    }
+
+    return insn.getMnemonic(); // our generic fallback
+  }
+  
+  protected void printMultiProcessState(String stateId){
+    if(GlobalSchedulingPoint.isGlobal(vm.getNextChoiceGenerator())) {
+      pw.print(stateId);
+
+      pw.print(" [");
+      pw.print(mpNodeAttrs);
+      pw.print(']');
+
+      pw.println("  // multiprc state");
+    }
+  }
+  
+  @Override
+  protected String getNativeExecCG (EXECUTENATIVE insn){
+    MethodInfo mi = insn.getExecutedMethod();
+    String s = mi.getName();
+
+    if (s.equals("start")) {
+      s = lastTi.getName() + ".start";
+    } else if (s.equals("wait")) {
+      s = lastTi.getName() + ".wait";
+    }
+
+    return s;
+  }
+  
+  @Override
+  protected String getLastChoice() {
+    ChoiceGenerator<?> cg = vm.getChoiceGenerator();
+    Object choice = cg.getNextChoice();
+
+    if (choice instanceof ThreadInfo){
+       return ((ThreadInfo) choice).getName();
+    } else {
+      return choice.toString();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/EndlessLoopDetector.java b/src/main/gov/nasa/jpf/listener/EndlessLoopDetector.java
new file mode 100644 (file)
index 0000000..ef53d0a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * little listener that tries to detect endless while() loops by counting
+ * backjumps, breaking transitions if the count exceeds a threshold, and
+ * then checking if program states match. If they do, there would be no progress
+ * in this thread.
+ */
+public class EndlessLoopDetector extends IdleFilter {
+
+  boolean foundEndlessLoop = false;
+
+  public EndlessLoopDetector(Config config) {
+    super(config);
+
+    action = Action.BREAK;
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    if (brokeTransition && search.isVisitedState()) {
+      foundEndlessLoop = true;
+    }
+  }
+
+  @Override
+  public boolean check(Search search, VM vm) {
+    return !foundEndlessLoop;
+  }
+
+  @Override
+  public void reset () {
+    foundEndlessLoop = false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/ErrorTraceGenerator.java b/src/main/gov/nasa/jpf/listener/ErrorTraceGenerator.java
new file mode 100644 (file)
index 0000000..94139f1
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.annotation.JPFOption;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+
+import java.io.PrintWriter;
+
+/** 
+ * A lightweight listener to generate the error trace by printing
+ * the program instructions at transition boundaries. The idea is to have
+ * a shorter trace output that only shows the choices
+ */
+public class ErrorTraceGenerator extends PropertyListenerAdapter implements PublisherExtension {
+
+  protected ChoiceGenerator<?>[] trace;
+
+  @JPFOption(type = "Boolean", key = "etg.show_insn", defaultValue = "true", comment = "print instruction that caused CG")
+  protected boolean showInsn = true;
+  
+  @JPFOption(type = "Boolean", key = "etg.show_loc", defaultValue = "true", comment = "print source location that caused CG")
+  protected boolean showLoc = true;
+
+  @JPFOption(type = "Boolean", key = "etg.show_src", defaultValue = "true", comment = "print source line that caused CG")
+  protected boolean showSrc = true;
+  
+       public ErrorTraceGenerator(Config conf, JPF jpf) {
+               jpf.addPublisherExtension(ConsolePublisher.class, this);
+    
+    showInsn = conf.getBoolean("etg.show_insn", showInsn);
+    showSrc = conf.getBoolean("etg.show_src", showLoc);
+    showLoc = conf.getBoolean("etg.show_loc", showSrc);
+       }
+
+  @Override
+  public void publishPropertyViolation (Publisher p){
+    PrintWriter pw = p.getOut();
+    
+    p.publishTopicStart("error trace choices");
+
+    if (trace != null){
+      int i=0;
+      for (ChoiceGenerator<?> cg : trace){
+        int tid = cg.getThreadInfo().getId();
+        Instruction insn = cg.getInsn();
+
+        if (!cg.isCascaded()){
+          pw.printf("#%2d [tid=%2d] ", i++, tid);
+        } else {
+          pw.print("             ");
+        }
+        
+        pw.println(cg);
+        
+        if (!cg.isCascaded()){
+          if (showLoc){
+            String loc = insn.getFilePos();
+            if (loc == null){
+              loc = "[no file]";
+            }
+            pw.print("\tat ");
+            pw.print(loc);
+            
+            pw.print(" in ");
+            pw.println( insn.getMethodInfo());
+          }
+          
+          if (showInsn) {
+            pw.printf("\tinstruction: [pc=%d] %s\n", insn.getPosition(), insn);
+          }
+
+          if (showSrc) {
+            String srcLine = insn.getSourceLine();
+            if (srcLine == null){
+              srcLine = "[no source]";
+            } else {
+              srcLine = srcLine.trim();
+            }
+            pw.print("\tsource: ");
+            pw.println( srcLine);
+          }
+        }
+      }
+    }
+  }
+  
+  @Override
+  public void propertyViolated(Search search) {
+    VM vm = search.getVM();
+    SystemState ss = vm.getSystemState();
+    trace = ss.getChoiceGenerators();
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/listener/ExceptionInjector.java b/src/main/gov/nasa/jpf/listener/ExceptionInjector.java
new file mode 100644 (file)
index 0000000..85a002e
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+import java.util.HashMap;
+
+/**
+ * listener to inject exceptions according to user specifications. This
+ * tool is meant to be used for exception handler verification, esp. if
+ * exceptions thrown by 3rd party code would be hard to produce.
+ *
+ * Exceptions are specified as a list of xSpec'@'location pairs.
+ *
+ * ExceptionSpec is specified as a class name, with optional details parameter. If no
+ * package is specified, either java.lang or default package are assumed
+ *
+ * Location can be 
+ *   - class:line
+ *   - fully qualified method (callee that is supposed to throw, which is
+ *     NOT executed in this case)
+ *   - fully qualified method ':' lineOffset
+ *
+ * for line/offest based locations, either the first or last insn associated
+ * with this line (depending on ei.throwFirst=true|false) is not executed
+ * but replaced with throwing the exception.
+ *
+ * Method body line offsets count from the first statement line in the method body
+ *
+ * Examples:
+ *   IOException@x.Foobar:42
+ *   NullPointerException@x.SomeClass.computeSomething(Ljava/lang/String;I)
+ *   y.MyException("something went wrong")@x.SomeClass.foo(D):10
+ */
+
+public class ExceptionInjector extends ListenerAdapter {
+
+  boolean throwFirst; // for location targets, throw on first insn associated with line
+
+  static class ExceptionEntry {
+    Instruction insn;
+    ExceptionSpec xSpec;
+    Location loc;
+
+    ExceptionEntry next;  // there might be more than one for one class
+
+    ExceptionEntry (ExceptionSpec xSpec, Location loc, ExceptionEntry next){
+      this.xSpec = xSpec;
+      this.loc = loc;
+      this.next = next;
+    }
+
+    String getLocationClassName() {
+      return loc.className;
+    }
+
+    String getMethod() {
+      return loc.method;
+    }
+
+    int getLine() {
+      return loc.line;
+    }
+
+    ClassInfo getExceptionClassInfo(ThreadInfo ti) {
+      return ClassLoaderInfo.getCurrentResolvedClassInfo(xSpec.xClsName);
+    }
+
+    String getExceptionDetails() {
+      return xSpec.details;
+    }
+
+    @Override
+       public String toString() {
+      return xSpec.toString() + '@' + loc.toString();
+    }
+  }
+
+  static class ExceptionSpec {
+    String xClsName;
+    String details;
+
+    ExceptionSpec (String xClsName, String details){
+      this.xClsName = xClsName;
+      this.details = details;
+    }
+    
+    @Override
+       public String toString() {
+      if (details == null){
+        return xClsName;
+      } else {
+        StringBuilder sb = new StringBuilder(xClsName);
+        sb.append('(');
+        if (!details.isEmpty()){
+          sb.append('"');
+          sb.append(details);
+          sb.append('"');
+        }
+        sb.append(')');
+        return sb.toString();
+      }
+    }
+  }
+
+  static class Location {
+    String className;
+    String method; // name + signature
+    int line;
+
+    Location (String className, String method, int line){
+      this.className = className;
+      this.method = method;
+      this.line = line;
+    }
+
+    @Override
+       public String toString() {
+      StringBuilder sb = new StringBuilder(className);
+      if (method != null){
+        sb.append('.');
+        sb.append(method);
+      }
+      if (line >= 0){
+        sb.append(':');
+        sb.append(line);
+      }
+      return sb.toString();
+    }
+  }
+
+  // these two are used to process classes at loadtime
+  HashMap<String,ExceptionEntry> targetClasses = new HashMap<String,ExceptionEntry>();
+  HashMap<String,ExceptionEntry> targetBases = new HashMap<String,ExceptionEntry>();
+  
+  // methods and instructions to watch for at runtime will have ExceptionEntry attrs
+
+
+  public ExceptionInjector (Config config, JPF jpf){
+    throwFirst = config.getBoolean("ei.throw_first", false);
+    String[] xSpecs = config.getStringArray("ei.exception", new char[] {';'});
+
+    if (xSpecs != null){
+      for (String xSpec : xSpecs){
+        if (!parseException(xSpec)){
+          throw new JPFConfigException("invalid exception spec: " + xSpec);
+        }
+      }
+    }
+
+    printEntries();
+  }
+
+  boolean parseException (String xSpec){
+    int i = xSpec.indexOf('@');
+    if (i > 0){
+      String typeSpec = xSpec.substring(0, i).trim();
+      String locSpec = xSpec.substring(i+1).trim();
+
+      ExceptionSpec type = parseType(typeSpec);
+      if (type != null){
+        Location loc = parseLocation(locSpec);
+        if (loc != null){
+          String cls = loc.className;
+          int line = loc.line;
+          if (line >= 0){
+            targetClasses.put(cls, new ExceptionEntry(type,loc,targetClasses.get(cls)));
+          } else {
+            targetBases.put(cls, new ExceptionEntry(type,loc,targetBases.get(cls)));
+          }
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  ExceptionSpec parseType (String spec){
+    String cls = null;
+    String details = null;
+
+    int i = spec.indexOf('(');
+    if (i > 0){
+      cls = spec.substring(0, i);
+
+      int j = spec.lastIndexOf(')');
+      if (spec.charAt(i+1) == '"'){
+        i++;
+      }
+      if (spec.charAt(j-1) == '"'){
+        j--;
+      }
+      details = spec.substring(i+1, j);
+      if (details.isEmpty()){
+        details = null;
+      }
+
+    } else if (i < 0) {  // no details
+      cls = spec;
+    }
+
+    if (cls != null){
+      return new ExceptionSpec( cls,details);
+    }
+
+    return null;
+  }
+
+  Location parseLocation (String spec){
+    int i = spec.indexOf('(');
+    if (i > 0){  // we have a method name
+      int j = spec.lastIndexOf('.', i);  // get class part
+      if (j > 0){
+        String cls = spec.substring(0, j).trim();
+        i = spec.indexOf(':');
+        if (i > 0){
+
+          String mth = Types.getSignatureName(spec.substring(j+1, i));
+
+          try {
+            int line = Integer.parseInt(spec.substring(i + 1));
+            if (!cls.isEmpty() && !mth.isEmpty() && line >= 0){
+              return new Location(cls, mth, line);
+            }
+          } catch (NumberFormatException nfx) {
+            return null;
+          }
+        } else {
+          String mth = Types.getSignatureName(spec.substring(j+1));
+          return new Location(cls,mth, -1);
+        }
+      }
+
+    } else { // no method
+      i = spec.indexOf(':');  // but we need a line number
+      if (i > 0){
+        String cls = spec.substring(0, i).trim();
+        try {
+          int line = Integer.parseInt(spec.substring(i+1));
+          if (!cls.isEmpty() && line >= 0){
+            return new Location (cls, null, line);
+          }
+        } catch (NumberFormatException nfx){
+          return null;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  boolean checkTargetInsn (ExceptionEntry e, MethodInfo mi, int[] ln, int line){
+    if ((ln[0] <= line) && (ln[ln.length - 1] >= line)) {
+      for (int i = 0; i < ln.length; i++) {
+        if (ln[i] == line) {
+          if (!throwFirst) {
+            while ((i++ < ln.length) && (ln[i] == line));
+            i--;
+          }
+
+          mi.getInstruction(i).addAttr(e);
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * get the target insns/methods
+   */
+  @Override
+  public void classLoaded (VM vm, ClassInfo loadedClass){
+
+    nextClassEntry:
+    for (ExceptionEntry e = targetClasses.get(loadedClass.getName()); e != null; e = e.next){
+      String method = e.getMethod();
+      int line = e.getLine();
+
+      if (method != null){  // method or method/line-offset
+        for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()){
+          if (mi.getUniqueName().startsWith(method)){
+            if (line >= 0){ // line offset
+              int[] ln = mi.getLineNumbers();
+              line += ln[0];
+              if (checkTargetInsn(e,mi,ln,line)){
+                continue nextClassEntry;
+              }
+            }
+          }
+        }
+
+      } else { // absolute line number
+        if (line >= 0){
+          for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()) {
+            int[] ln = mi.getLineNumbers();
+            if (checkTargetInsn(e, mi, ln, line)) {
+              continue nextClassEntry;
+            }
+          }
+        }
+      }
+    }
+
+    if (targetBases != null){
+      for (; loadedClass != null; loadedClass = loadedClass.getSuperClass()) {
+        nextBaseEntry:
+        for (ExceptionEntry e = targetBases.get(loadedClass.getName()); e != null; e = e.next){
+          String method = e.getMethod();
+          for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()){
+            if (mi.getUniqueName().startsWith(method)){
+              mi.addAttr(e);
+              continue nextBaseEntry;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+
+    ExceptionEntry e = insnToExecute.getAttr(ExceptionEntry.class);
+    if ((e == null) && insnToExecute instanceof JVMInvokeInstruction){
+      MethodInfo mi = ((JVMInvokeInstruction) insnToExecute).getInvokedMethod();
+      e = mi.getAttr(ExceptionEntry.class);
+    }
+
+    if (e != null){
+      Instruction nextInsn = ti.createAndThrowException(e.getExceptionClassInfo(ti), e.getExceptionDetails());
+      ti.skipInstruction(nextInsn);
+      return;
+    }
+  }
+  
+  // for debugging purposes
+  void printEntries () {
+    for (ExceptionEntry e : targetClasses.values()){
+      System.out.println(e);
+    }
+    for (ExceptionEntry e : targetBases.values()){
+      System.out.println(e);
+    }
+  }
+
+  /**
+  public static void main (String[] args){
+    Config conf = JPF.createConfig(args);
+    ExceptionInjector ei = new ExceptionInjector(conf,null);
+
+    ei.parseException("x.y.Zang(\"bang\")@z.Foo.doit(Ljava/lang/Object;I)");
+    ei.printEntries();
+  }
+  **/
+}
diff --git a/src/main/gov/nasa/jpf/listener/ExecTracker.java b/src/main/gov/nasa/jpf/listener/ExecTracker.java
new file mode 100644 (file)
index 0000000..5dab34a
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.annotation.JPFOption;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * Listener tool to monitor JPF execution. This class can be used as a drop-in replacement for JPF, which is called by
+ * ExecTracker. ExecTracker is mostly a VMListener of 'instructionExecuted' and a SearchListener of 'stateAdvanced' and
+ * 'statehBacktracked'
+ * 
+ * NOTE - the ExecTracker is machine type agnostic
+ */
+
+public class ExecTracker extends ListenerAdapter {
+  
+  @JPFOption(type = "Boolean", key = "et.print_insn", defaultValue = "true", comment = "print executed bytecode instructions") 
+  boolean printInsn = true;
+  
+  @JPFOption(type = "Boolean", key = "et.print_src", defaultValue = "false", comment = "print source lines")
+  boolean printSrc = false;
+  
+  @JPFOption(type = "Boolean", key = "et.print_mth", defaultValue = "false", comment = "print executed method names")
+  boolean printMth = false;
+  
+  @JPFOption(type = "Boolean", key = "et.skip_init", defaultValue = "true", comment = "do not log execution before entering main()")
+  boolean skipInit = false;
+  
+  boolean showShared = false;
+  
+  PrintWriter out;
+  String lastLine;
+  MethodInfo lastMi;
+  String linePrefix;
+  
+  boolean skip;
+  MethodInfo miMain; // just to make init skipping more efficient
+  
+  public ExecTracker (Config config) {
+    /** @jpfoption et.print_insn : boolean - print executed bytecode instructions (default=true). */
+    printInsn = config.getBoolean("et.print_insn", true);
+
+    /** @jpfoption et.print_src : boolean - print source lines (default=false). */
+    printSrc = config.getBoolean("et.print_src", false);
+
+    /** @jpfoption et.print_mth : boolean - print executed method names (default=false). */
+    printMth = config.getBoolean("et.print_mth", false);
+
+    /** @jpfoption et.skip_init : boolean - do not log execution before entering main() (default=true). */
+    skipInit = config.getBoolean("et.skip_init", true);
+    
+    showShared = config.getBoolean("et.show_shared", true);
+    
+    if (skipInit) {
+      skip = true;
+    }
+    
+    out = new PrintWriter(System.out, true);
+  }
+  
+  /******************************************* SearchListener interface *****/
+  
+  @Override
+  public void stateRestored(Search search) {
+    int id = search.getStateId();
+    out.println("----------------------------------- [" +
+                       search.getDepth() + "] restored: " + id);
+  }
+    
+  //--- the ones we are interested in
+  @Override
+  public void searchStarted(Search search) {
+    out.println("----------------------------------- search started");
+    if (skipInit) {
+      ThreadInfo tiCurrent = ThreadInfo.getCurrentThread();
+      miMain = tiCurrent.getEntryMethod();
+      
+      out.println("      [skipping static init instructions]");
+    }
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    int id = search.getStateId();
+    
+    out.print("----------------------------------- [" +
+                     search.getDepth() + "] forward: " + id);
+    if (search.isNewState()) {
+      out.print(" new");
+    } else {
+      out.print(" visited");
+    }
+    
+    if (search.isEndState()) {
+      out.print(" end");
+    }
+    
+    out.println();
+    
+    lastLine = null; // in case we report by source line
+    lastMi = null;
+    linePrefix = null;
+  }
+
+  @Override
+  public void stateProcessed (Search search) {
+    int id = search.getStateId();
+    out.println("----------------------------------- [" +
+                       search.getDepth() + "] done: " + id);
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    int id = search.getStateId();
+
+    lastLine = null;
+    lastMi = null;
+
+    out.println("----------------------------------- [" +
+                       search.getDepth() + "] backtrack: " + id);
+  }
+  
+  @Override
+  public void searchFinished(Search search) {
+    out.println("----------------------------------- search finished");
+  }
+
+  /******************************************* VMListener interface *********/
+
+  @Override
+  public void gcEnd(VM vm) {
+    out.println("\t\t # garbage collection");
+  }
+
+  //--- the ones we are interested in
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    
+    if (skip) {
+      MethodInfo mi = executedInsn.getMethodInfo();
+      if (mi == miMain) {
+        skip = false; // start recording
+      } else {
+        return;  // skip
+      }
+    }
+
+    int nNoSrc = 0;
+    
+    if (linePrefix == null) {
+      linePrefix = Integer.toString( ti.getId()) + " : ";
+    }
+    
+    // that's pretty redundant to what is done in the ConsolePublisher, but we don't want 
+    // presentation functionality in Step anymore
+    if (printSrc) {
+      String line = executedInsn.getSourceLine();
+      if (line != null){
+        if (nNoSrc > 0) {
+          out.println("            [" + nNoSrc + " insn w/o sources]");
+        }
+
+        if (!line.equals(lastLine)) {
+          out.print("            [");
+          out.print(executedInsn.getFileLocation());
+          out.print("] : ");
+          out.println(line.trim());
+        }
+        
+        nNoSrc = 0;
+        
+      } else { // no source
+        nNoSrc++;
+      }
+      
+      lastLine = line;
+    }
+    
+    if (printInsn) {      
+      if (printMth) {
+        MethodInfo mi = executedInsn.getMethodInfo();
+        if (mi != lastMi){
+          ClassInfo mci = mi.getClassInfo();
+          out.print("      ");
+          if (mci != null) {
+            out.print(mci.getName());
+            out.print(".");
+          }
+          out.println(mi.getUniqueName());
+          lastMi = mi;
+        }
+      }
+      
+      out.print( linePrefix);
+      
+      out.printf("[%04x]   ", executedInsn.getPosition());
+      
+      out.println( executedInsn.toPostExecString());
+    }
+  }
+
+  @Override
+  public void threadStarted(VM vm, ThreadInfo ti) {
+    out.println( "\t\t # thread started: " + ti.getName() + " index: " + ti.getId());
+  }
+
+  @Override
+  public void threadTerminated(VM vm, ThreadInfo ti) {
+    out.println( "\t\t # thread terminated: " + ti.getName() + " index: " + ti.getId());
+  }
+  
+  @Override
+  public void exceptionThrown (VM vm, ThreadInfo ti, ElementInfo ei) {
+    MethodInfo mi = ti.getTopFrameMethodInfo();
+    out.println("\t\t\t\t # exception: " + ei + " in " + mi);
+  }
+  
+  @Override
+  public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+    out.println("\t\t # choice: " + currentCG);
+    
+    //vm.dumpThreadStates();
+  }
+  
+  @Override
+  public void objectExposed (VM vm, ThreadInfo currentThread, ElementInfo fieldOwnerObject, ElementInfo exposedObject) {
+    if (showShared){
+      String msg = "\t\t # exposed " + exposedObject;
+      if (fieldOwnerObject != null){
+        String ownerStatus = "";
+        if (fieldOwnerObject.isShared()){
+          ownerStatus = "shared ";
+        } else if (fieldOwnerObject.isExposed()){
+          ownerStatus = "exposed ";
+        }
+        
+        msg += " through " + ownerStatus + fieldOwnerObject;
+      }
+      out.println(msg);
+    }
+  }
+  
+  @Override
+  public void objectShared (VM vm, ThreadInfo currentThread, ElementInfo sharedObject) {
+    if (showShared){
+      out.println("\t\t # shared " + sharedObject);
+    }
+  }
+  
+  
+  /****************************************** private stuff ******/
+
+  void filterArgs (String[] args) {
+    for (int i=0; i<args.length; i++) {
+      if (args[i] != null) {
+        if (args[i].equals("-print-lines")) {
+          printSrc = true;
+          args[i] = null;
+        }
+      }
+    }
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/listener/HeapTracker.java b/src/main/gov/nasa/jpf/listener/HeapTracker.java
new file mode 100644 (file)
index 0000000..32b62b9
--- /dev/null
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.DynamicObjectArray;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.SourceRef;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Stack;
+
+/**
+ * HeapTracker - property-listener class to check heap utilization along all
+ * execution paths (e.g. to verify heap bounds)
+ */
+public class HeapTracker extends PropertyListenerAdapter {
+
+  static class PathStat implements Cloneable {
+    int nNew = 0;
+    int nReleased = 0;
+    int heapSize = 0;  // in bytes
+
+    @Override
+       public Object clone() {
+      try {
+        return super.clone();
+      } catch (CloneNotSupportedException e) {
+        return null;
+      }
+    }
+  }
+
+  static class TypeStat {
+    String typeName;
+    int nAlloc;
+    int nReleased;
+
+    TypeStat (String typeName){
+      this.typeName = typeName;
+    }
+  }
+
+  PathStat stat = new PathStat();
+  Stack<PathStat> pathStats = new Stack<PathStat>();
+
+  DynamicObjectArray<SourceRef> loc = new DynamicObjectArray<SourceRef>();
+
+  HashMap<String,TypeStat> typeStat = new HashMap<String,TypeStat>();
+
+  int maxState;
+  int nForward;
+  int nBacktrack;
+
+  int nElemTotal;
+  int nGcTotal;
+  int nSharedTotal;
+  int nImmutableTotal;
+
+  int nElemMax = Integer.MIN_VALUE;
+  int nElemMin = Integer.MAX_VALUE;
+  int nElemAv;
+
+  int pElemSharedMax = Integer.MIN_VALUE;
+  int pElemSharedMin = Integer.MAX_VALUE;
+  int pElemSharedAv;
+
+  int pElemImmutableMax = Integer.MIN_VALUE;
+  int pElemImmutableMin = Integer.MAX_VALUE;
+  int pElemImmutableAv;
+
+  int nReleased;
+  int nReleasedTotal;
+  int nReleasedAv;
+  int nReleasedMax = Integer.MIN_VALUE;
+  int nReleasedMin = Integer.MAX_VALUE;
+
+  int maxPathHeap = Integer.MIN_VALUE;
+  int maxPathNew = Integer.MIN_VALUE;
+  int maxPathReleased = Integer.MIN_VALUE;
+  int maxPathAlive = Integer.MIN_VALUE;
+
+  int initHeap = 0;
+  int initNew = 0;
+  int initReleased = 0;
+  int initAlive = 0;
+
+
+  boolean showTypeStats;
+  int maxTypesShown;
+
+  // used as a property check
+  int maxHeapSizeLimit;
+  int maxLiveLimit;
+  boolean throwOutOfMemory = false;
+
+  StringSetMatcher includes, excludes;
+
+  void updateMaxPathValues() {
+      if (stat.heapSize > maxPathHeap) {
+        maxPathHeap = stat.heapSize;
+      }
+
+      if (stat.nNew > maxPathNew) {
+        maxPathNew = stat.nNew;
+      }
+
+      if (stat.nReleased > maxPathReleased) {
+        maxPathReleased = stat.nReleased;
+      }
+
+      int nAlive = stat.nNew - stat.nReleased;
+      if (nAlive > maxPathAlive) {
+        maxPathAlive = nAlive;
+      }
+  }
+
+  void allocTypeStats (ElementInfo ei) {
+    String typeName = ei.getClassInfo().getName();
+    TypeStat ts = typeStat.get(typeName);
+    if (ts == null) {
+      ts = new TypeStat(typeName);
+      typeStat.put(typeName, ts);
+    }
+    ts.nAlloc++;
+  }
+
+  void releaseTypeStats (ElementInfo ei) {
+    String typeName = ei.getClassInfo().getName();
+    TypeStat ts = typeStat.get(typeName);
+    if (ts != null) {
+      ts.nReleased++;
+    }
+  }
+
+
+  public HeapTracker (Config config, JPF jpf) {
+    maxHeapSizeLimit = config.getInt("heap.size_limit", -1);
+    maxLiveLimit = config.getInt("heap.live_limit", -1);
+    throwOutOfMemory = config.getBoolean("heap.throw_exception");
+    showTypeStats = config.getBoolean("heap.show_types");
+    maxTypesShown = config.getInt("heap.max_types", 20);
+
+    includes = StringSetMatcher.getNonEmpty(config.getStringArray("heap.include"));
+    excludes = StringSetMatcher.getNonEmpty(config.getStringArray("heap.exclude"));
+
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+
+  /******************************************* abstract Property *****/
+
+  /**
+   * return 'false' if property is violated
+   */
+  @Override
+  public boolean check (Search search, VM vm) {
+    if (throwOutOfMemory) {
+      // in this case we don't want to stop the program, but see if it
+      // behaves gracefully - don't report a property violation
+      return true;
+    } else {
+      if ((maxHeapSizeLimit >= 0) && (stat.heapSize > maxHeapSizeLimit)) {
+        return false;
+      }
+      if ((maxLiveLimit >=0) && ((stat.nNew - stat.nReleased) > maxLiveLimit)) {
+        return false;
+      }
+
+      return true;
+    }
+  }
+
+  @Override
+  public String getErrorMessage () {
+    return "heap limit exceeded: " + stat.heapSize + " > " + maxHeapSizeLimit;
+  }
+
+  /******************************************* SearchListener interface *****/
+  @Override
+  public void searchStarted(Search search) {
+    super.searchStarted(search);
+
+    updateMaxPathValues();
+    pathStats.push(stat);
+
+    initHeap = stat.heapSize;
+    initNew = stat.nNew;
+    initReleased = stat.nReleased;
+    initAlive = initNew - initReleased;
+
+    stat = (PathStat)stat.clone();
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+
+    if (search.isNewState()) {
+      int id = search.getStateId();
+
+      if (id > maxState) maxState = id;
+
+      updateMaxPathValues();
+      pathStats.push(stat);
+      stat = (PathStat)stat.clone();
+
+      nForward++;
+    }
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    nBacktrack++;
+
+    if (!pathStats.isEmpty()){
+      stat = pathStats.pop();
+    }
+  }
+
+  /******************************************* PublisherExtension interface ****/
+  @Override
+  public void publishFinished (Publisher publisher) {
+    PrintWriter pw = publisher.getOut();
+    publisher.publishTopicStart("heap statistics");
+
+    pw.println("heap statistics:");
+    pw.println("  states:         " + maxState);
+    pw.println("  forwards:       " + nForward);
+    pw.println("  backtrack:      " + nBacktrack);
+    pw.println();
+    pw.println("  gc cycles:      " + nGcTotal);
+    pw.println();
+    pw.println("  max Objects:    " + nElemMax);
+    pw.println("  min Objects:    " + nElemMin);
+    pw.println("  avg Objects:    " + nElemAv);
+    pw.println();
+    pw.println("  max% shared:    " + pElemSharedMax);
+    pw.println("  min% shared:    " + pElemSharedMin);
+    pw.println("  avg% shared:    " + pElemSharedAv);
+    pw.println();
+    pw.println("  max% immutable: " + pElemImmutableMax);
+    pw.println("  min% immutable: " + pElemImmutableMin);
+    pw.println("  avg% immutable: " + pElemImmutableAv);
+    pw.println();
+    pw.println("  max released:   " + nReleasedMax);
+    pw.println("  min released:   " + nReleasedMin);
+    pw.println("  avg released:   " + nReleasedAv);
+
+    pw.println();
+    pw.print(  "  max path heap (B):   " + maxPathHeap);
+    pw.println(" / " + (maxPathHeap - initHeap));
+    pw.print(  "  max path alive:      " + maxPathAlive);
+    pw.println(" / " + (maxPathAlive - initAlive));
+    pw.print(  "  max path new:        " + maxPathNew);
+    pw.println(" / " + (maxPathNew - initNew));
+    pw.print(  "  max path released:   " + maxPathReleased);
+    pw.println(" / " + (maxPathReleased - initReleased));
+
+    if (showTypeStats) {
+      pw.println();
+      pw.println("  type allocation statistics:");
+
+      ArrayList<Map.Entry<String,TypeStat>> list =
+        Misc.createSortedEntryList(typeStat, new Comparator<Map.Entry<String,TypeStat>>() {
+          @Override
+               public int compare (Map.Entry<String,TypeStat> e1,
+                              Map.Entry<String,TypeStat> e2) {
+          return Integer.signum(e1.getValue().nAlloc - e2.getValue().nAlloc);
+        }});
+
+      int i=0;
+      for (Map.Entry<String,TypeStat> e : list) {
+        TypeStat ts = e.getValue();
+        pw.print("  ");
+        pw.print(String.format("%1$9d : ", ts.nAlloc));
+        pw.println(ts.typeName);
+
+        if (i++ > maxTypesShown) {
+          pw.println("  ...");
+          break;
+        }
+      }
+    }
+  }
+
+
+  /******************************************* VMListener interface *********/
+  @Override
+  public void gcBegin(VM vm) {
+    /**
+     System.out.println();
+     System.out.println( "----- gc cycle: " + vm.getDynamicArea().getGcNumber()
+     + ", state: " + vm.getStateId());
+     **/
+  }
+
+  @Override
+  public void gcEnd(VM vm) {
+    Heap heap = vm.getHeap();
+
+    int n = 0;
+    int nShared = 0;
+    int nImmutable = 0;
+
+    for (ElementInfo ei : heap.liveObjects()) {
+      n++;
+
+      if (ei.isShared()) nShared++;
+      if (ei.isImmutable()) nImmutable++;
+
+      //printElementInfo(ei);
+    }
+
+    nElemTotal += n;
+    nGcTotal++;
+
+    if (n > nElemMax) nElemMax = n;
+    if (n < nElemMin) nElemMin = n;
+
+    int pShared = (nShared * 100) / n;
+    int pImmutable = (nImmutable * 100) / n;
+
+    if (pShared > pElemSharedMax) pElemSharedMax = pShared;
+    if (pShared < pElemSharedMin) pElemSharedMin = pShared;
+
+    nSharedTotal += nShared;
+    nImmutableTotal += nImmutable;
+
+    pElemSharedAv = (nSharedTotal * 100) / nElemTotal;
+    pElemImmutableAv = (nImmutableTotal * 100) / nElemTotal;
+
+    if (pImmutable > pElemImmutableMax) pElemImmutableMax = pImmutable;
+    if (pImmutable < pElemImmutableMin) pElemImmutableMin = pImmutable;
+
+    nElemAv = nElemTotal / nGcTotal;
+    nReleasedAv = nReleasedTotal / nGcTotal;
+
+    if (nReleased > nReleasedMax) nReleasedMax = nReleased;
+    if (nReleased < nReleasedMin) nReleasedMin = nReleased;
+
+    nReleased = 0;
+  }
+
+  boolean isRelevantType (ElementInfo ei) {
+    String clsName = ei.getClassInfo().getName();
+    return StringSetMatcher.isMatch(clsName, includes, excludes);
+  }
+
+  @Override
+  public void objectCreated(VM vm, ThreadInfo ti, ElementInfo ei) {
+    int idx = ei.getObjectRef();
+    int line = ti.getLine();
+    MethodInfo mi = ti.getTopFrameMethodInfo();
+    SourceRef sr = null;
+
+    if (!isRelevantType(ei)) {
+      return;
+    }
+
+    if (mi != null) {
+      ClassInfo mci = mi.getClassInfo();
+      if (mci != null) {
+        String file = mci.getSourceFileName();
+        if (file != null) {
+          sr = new SourceRef(file, line);
+        } else {
+          sr = new SourceRef(mci.getName(), line);
+        }
+      }
+    }
+
+    // means references with null loc are from synthetic methods
+    loc.set(idx, sr);
+
+    stat.nNew++;
+    stat.heapSize += ei.getHeapSize();
+
+    // update the type statistics
+    if (showTypeStats) {
+      allocTypeStats(ei);
+    }
+
+
+    // check if we should simulate an OutOfMemoryError
+    if (throwOutOfMemory) {
+      if (((maxHeapSizeLimit >=0) && (stat.heapSize > maxHeapSizeLimit)) ||
+          ((maxLiveLimit >=0) && ((stat.nNew - stat.nReleased) > maxLiveLimit))){
+        vm.getHeap().setOutOfMemory(true);
+      }
+    }
+  }
+
+  @Override
+  public void objectReleased(VM vm, ThreadInfo ti, ElementInfo ei) {
+
+    if (!isRelevantType(ei)) {
+      return;
+    }
+
+    nReleasedTotal++;
+    nReleased++;
+
+    if (showTypeStats) {
+      releaseTypeStats(ei);
+    }
+
+    stat.nReleased++;
+    stat.heapSize -= ei.getHeapSize();
+  }
+
+  /****************************************** private stuff ******/
+  protected void printElementInfo(ElementInfo ei) {
+    boolean first = false;
+
+    System.out.print( ei.getObjectRef());
+    System.out.print( ": ");
+    System.out.print( ei.getClassInfo().getName());
+    System.out.print( "  [");
+
+    if (ei.isShared()) {
+      System.out.print( "shared");
+      first = false;
+    }
+    if (ei.isImmutable()) {
+      if (!first) System.out.print(' ');
+      System.out.print( "immutable");
+    }
+    System.out.print( "] ");
+
+    SourceRef sr = loc.get(ei.getObjectRef());
+    if (sr != null) {
+      System.out.println(sr);
+    } else {
+      System.out.println("?");
+    }
+  }
+
+
+  static void printUsage () {
+    System.out.println("HeapTracker - a JPF listener tool to report and check heap utilization");
+    System.out.println("usage: java gov.nasa.jpf.tools.HeapTracker <jpf-options> <heapTracker-options> <class>");
+    System.out.println("       +heap.size_limit=<num> : report property violation if heap exceeds <num> bytes");
+    System.out.println("       +heap.live_limit=<num> : report property violation if more than <num> live objects");
+    System.out.println("       +heap.classes=<regEx> : only report instances of classes matching <regEx>");
+    System.out.println("       +heap.throw_exception=<bool>: throw a OutOfMemoryError instead of reporting property violation");
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/listener/IdleFilter.java b/src/main/gov/nasa/jpf/listener/IdleFilter.java
new file mode 100644 (file)
index 0000000..ece2e4a
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.ArrayStoreInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.DynamicObjectArray;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.logging.Logger;
+
+/**
+ * simple combined listener that checks if a thread seems to do idle loops that
+ * might starve other threads or JPF. The most classical case is a "busy wait" loop
+ * like
+ *
+ *   for (long l=0; l<1000000; l++);
+ *
+ * which would give us a pretty long path. Even worse, things like
+ *
+ *   while (true);
+ *
+ * would (just like in a normal VM) never terminate in JPF, even though people
+ * familiar with model checking would expect state matching. Only that without
+ * a transition break, JPF has no reason to match states, so we have to
+ * automatically add a break on the backjump. We shouldn't add one on every
+ * backjump though because that might cause a lot of overhead in programs that
+ * do terminate.
+ *
+ * IdleFilter has two options:
+ *   idle.max_backjumps : sets the number of backjumps after which we break
+ *   idle.action : what to do if max_backjumps are exceeded in the same thread
+ *                 on the same location and stackframe
+ *     warn : only print warning for backjumps exceeding the max_backjumps
+ *     break : break the transition to allow state matching
+ *     prune : unconditionally prune the search
+ *     jump : jump past the backjump (this is dangerous if the loop has side effects)
+ */
+public class IdleFilter extends PropertyListenerAdapter {
+
+  static Logger log = JPF.getLogger("gov.nasa.jpf.listener.IdleFilter");
+
+  static class ThreadStat {
+    String tname;
+
+    int backJumps;
+
+    boolean isCleared = false;
+
+    int loopStartPc;
+
+    int loopEndPc;
+
+    int loopStackDepth;
+
+    ThreadStat(String tname) {
+      this.tname = tname;
+    }
+  }
+
+  static enum Action { JUMP, PRUNE, BREAK, YIELD, WARN }
+
+  DynamicObjectArray<ThreadStat> threadStats = new DynamicObjectArray<ThreadStat>(4,16);
+
+  ThreadStat ts;
+
+  // we use this to remember that we just broke the transition
+  boolean brokeTransition;
+
+  int maxBackJumps;
+  Action action;
+
+
+  // ----------------------------------------------------- SearchListener
+  // interface
+
+  public IdleFilter(Config config) {
+    maxBackJumps = config.getInt("idle.max_backjumps", 500);
+
+    String act = config.getString("idle.action", "break");
+    if ("warn".equalsIgnoreCase(act)){
+      action = Action.WARN;
+    } else if ("break".equalsIgnoreCase(act)){
+      action = Action.BREAK;
+    } else if ("yield".equalsIgnoreCase(act)){
+      action = Action.YIELD;
+    } else if ("prune".equalsIgnoreCase(act)){
+      action = Action.PRUNE;
+    } else if ("jump".equalsIgnoreCase(act)){
+      action = Action.JUMP;
+    } else {
+      throw new JPFConfigException("unknown IdleFilter action: " +act);
+    }
+
+  }
+  
+  @Override
+  public void stateAdvanced(Search search) {
+    ts.backJumps = 0;
+    ts.isCleared = false;
+    ts.loopStackDepth = 0;
+    ts.loopStartPc = ts.loopEndPc = 0;
+
+    brokeTransition = false;
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    ts.backJumps = 0;
+    ts.isCleared = false;
+    ts.loopStackDepth = 0;
+    ts.loopStartPc = ts.loopEndPc = 0;
+  }
+
+  // ----------------------------------------------------- VMListener interface
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+
+    int tid = ti.getId();
+    ts = threadStats.get(tid);
+    if (ts == null) {
+      ts = new ThreadStat(ti.getName());
+      threadStats.set(tid, ts);
+    }
+
+    if (executedInsn.isBackJump()) {
+      ts.backJumps++;
+
+      int loopStackDepth = ti.getStackDepth();
+      int loopPc = nextInsn.getPosition();
+
+      if ((loopStackDepth != ts.loopStackDepth) || (loopPc != ts.loopStartPc)) {
+        // new loop, reset
+        ts.isCleared = false;
+        ts.loopStackDepth = loopStackDepth;
+        ts.loopStartPc = loopPc;
+        ts.loopEndPc = executedInsn.getPosition();
+        ts.backJumps = 0;
+        
+      } else {
+        if (!ts.isCleared) {
+          if (ts.backJumps > maxBackJumps) {
+
+            ti.reschedule("idleFilter"); // this breaks the executePorStep loop
+            MethodInfo mi = executedInsn.getMethodInfo();
+            ClassInfo ci = mi.getClassInfo();
+            int line = mi.getLineNumber(executedInsn);
+            String file = ci.getSourceFileName();
+
+            switch (action) {
+              case JUMP:
+                // pretty bold, we jump past the loop end and go on from there
+
+                Instruction next = executedInsn.getNext();
+                ti.setNextPC(next);
+
+                log.warning("jumped past loop in: " + ti.getName() +
+                        "\n\tat " + ci.getName() + "." + mi.getName() + "(" + file + ":" + line + ")");
+                break;
+
+              case PRUNE:
+                // cut this sucker off - we declare this a visited state
+                vm.ignoreState();
+                log.warning("pruned thread: " + ti.getName() +
+                        "\n\tat " + ci.getName() + "." + mi.getName() + "(" + file + ":" + line + ")");
+                break;
+
+              case BREAK:
+                // just break the transition and let the state matching take over
+                brokeTransition = true;
+                ti.breakTransition("breakIdleLoop");
+
+                log.warning("breaks transition on suspicious loop in thread: " + ti.getName() +
+                        "\n\tat " + ci.getName() + "." + mi.getName() + "(" + file + ":" + line + ")");
+
+                break;
+
+              case YIELD:
+                // give other threads a chance to run
+                brokeTransition = true;
+                ti.reschedule("rescheduleIdleLoop");
+
+                log.warning("yield on suspicious loop in thread: " + ti.getName() +
+                        "\n\tat " + ci.getName() + "." + mi.getName() + "(" + file + ":" + line + ")");
+
+                break;
+                
+              case WARN:
+                log.warning("detected suspicious loop in thread: " + ti.getName() +
+                        "\n\tat " + ci.getName() + "." + mi.getName() + "(" + file + ":" + line + ")");
+                break;
+
+            }
+          }
+        }
+      }
+
+    } else if (!ts.isCleared) {
+      // if we call methods or set array elements inside the loop in question,
+      // we assume this is not an idle loop and terminate the checks
+      // <2do> this is too restrictive - we should leave this to state matching
+      
+      if ((executedInsn instanceof JVMInvokeInstruction)
+          || (executedInsn instanceof ArrayStoreInstruction)) {
+        int stackDepth = ti.getStackDepth();
+        int pc = executedInsn.getPosition();
+
+        if (stackDepth == ts.loopStackDepth) {
+          if ((pc >= ts.loopStartPc) && (pc < ts.loopEndPc)) {
+            ts.isCleared = true;
+          }
+        }
+      }
+    }
+  }
+  
+  // thread ids are reused, so we have to clean up
+  @Override
+  public void threadTerminated (VM vm, ThreadInfo ti){
+    int tid = ti.getId();
+    threadStats.set(tid, null);
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/InsnCounter.java b/src/main/gov/nasa/jpf/listener/InsnCounter.java
new file mode 100644 (file)
index 0000000..4dddbdd
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * simple tools to gather statistics about instructions executed by JPF.
+ * InsnCounter is mostly a VMListener that observes 'instructionExecuted'
+ */
+public class InsnCounter extends ListenerAdapter {
+
+  String[] opCodes = new String[500];
+  int[] counts = new int[500];
+  int   total;
+  
+  //----------------------------------------- SearchKistener interface
+  @Override
+  public void searchFinished(Search search) {
+    reportStatistics();
+  }
+    
+  //----------------------------------------------------- VMListener interface
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    int bc = executedInsn.getByteCode();
+    
+    if (opCodes[bc] == null) {
+      opCodes[bc] = executedInsn.getMnemonic();
+    }
+    counts[bc]++;
+    total++;
+  }
+
+  
+  //----------------------------------------------------- internal stuff
+  void reportStatistics () {
+    int[] sorted = getSortedCounts();
+    int i;
+    
+    int total = 0;
+    
+    for (i=0; i<sorted.length; i++) {
+      int idx = sorted[i];
+      String opc = opCodes[idx];
+            
+      if (counts[idx] > 0) {
+        System.out.print( i);
+        System.out.print( "  ");
+        System.out.print( opc);
+        System.out.print( " : ");
+        System.out.println( counts[idx]);
+        
+        total += counts[idx];
+      } else {
+        break;
+      }
+    }
+    
+    System.out.println();
+    System.out.println("total number of executed instructions: " + total);
+  }
+  
+  int[] getSortedCounts () {
+    int[] sorted = new int[256];
+    int last = -1;
+    int i, j;
+    
+    for (i=0; i<256; i++) {
+      int c = counts[i];
+      if (c > 0) {
+        for (j=0; j<last; j++) {
+          if (counts[sorted[j]] < c) {
+            System.arraycopy(sorted, j, sorted, j+1, (last-j));
+            break;
+          }
+        }
+        sorted[j] = i;
+        last++;
+      }
+    }
+    
+    return sorted;
+  }
+  
+  void filterArgs (String[] args) {
+    // we don't have any yet
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/listener/LockedStackDepth.java b/src/main/gov/nasa/jpf/listener/LockedStackDepth.java
new file mode 100644 (file)
index 0000000..e9eabf0
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.DFSearch;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.heuristic.BFSHeuristic;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A listener that tracks information about the stack depth of when a lock is first acquired.  If 
+ *
+ * Writing a test for this class is very difficult.  Hence, a lot of asserts are added.
+ */
+public class LockedStackDepth extends ListenerAdapter
+{
+   private static final Logger  s_logger    = JPF.getLogger(LockedStackDepth.class.getName());
+   private static final Integer EMPTY[]     = new Integer[0];
+   private static final int     THREAD_FLAG = 0x80000000;
+   
+   private final HashMap<Integer, Operation> m_operations = new HashMap<Integer, Operation>();
+   private final HashMap<Integer, Integer>   m_state      = new HashMap<Integer, Integer>();
+   private final HashMap<Operation, Integer> m_index      = new HashMap<Operation, Integer>();
+   private final ArrayList<Operation>        m_apply      = new ArrayList<Operation>();
+   private       Operation                   m_current;
+
+   public int getLockedStackDepth(ElementInfo lock)
+   {
+      Integer result;
+      int lockIndex;
+      
+      lockIndex = lock.getObjectRef();
+      result    = m_state.get(makeKey(lock));
+      
+      if (s_logger.isLoggable(Level.INFO))
+         s_logger.info("Depth = " + result + " | Lock Index = " + lockIndex + " | Lock = " + lock);
+      
+      if (result == null)
+         return(-1);
+      
+      assert result >= 0;
+      
+      return(result);
+   }
+   
+   public List<ElementInfo> getLockedInTopFrame(ThreadInfo thread)
+   {
+      ArrayList<ElementInfo> result;
+      ElementInfo lock;
+      int threadDepth;
+      
+      threadDepth = thread.getStackDepth();
+      result      = new ArrayList<ElementInfo>();
+      
+      for (Integer key : m_state.keySet())
+      {
+         if (key < 0)
+            continue;
+         
+         if (threadDepth != m_state.get(key))
+            continue;
+         
+         lock = thread.getElementInfo(key);
+         
+         if (lock == null)
+            continue;
+         
+         if (!lock.isLockedBy(thread))
+            continue;
+            
+         result.add(lock);
+      }
+      
+      return(result);
+   }
+
+   @Override
+   public void objectLocked(VM vm, ThreadInfo thread, ElementInfo ei)
+   {
+      ElementInfo lock;
+      Integer depth;
+
+      lock   = ei;
+
+      logStack(thread);
+
+      depth  = new Operation(thread, null).getOldDepth();
+
+      if (depth == null)
+         depth = thread.getStackDepth();
+
+      assert thread.getLockCount() == 0;
+      assert thread.getLockObject() == null;
+      assert lock.isLockedBy(thread);
+      
+      if (m_state.containsKey(makeKey(lock)))               // So that a breakpoint on the next line will only get hit if the assert will trigger.
+         assert !m_state.containsKey(makeKey(lock));
+            
+      assert !m_state.containsKey(makeKey(thread));
+      assert depth >= 0;
+
+      new Operation(lock, depth);
+   }
+
+   @Override
+   public void objectUnlocked(VM vm, ThreadInfo thread, ElementInfo ei)
+   {
+      ElementInfo lock;
+      Integer depth;
+
+      logStack(thread);
+
+      lock   = ei;
+      depth  = new Operation(lock, null).getOldDepth();
+
+      assert !m_state.containsKey(makeKey(lock));
+      assert !m_state.containsKey(makeKey(thread));
+      assert depth >= 0;
+
+      if (thread.isWaiting())
+      {
+         assert !lock.isLockedBy(thread);
+         assert lock.getLockCount() == 0;
+         assert thread.getLockCount() > 0;
+         assert thread.getLockObject() == lock;
+         new Operation(thread, depth);
+      }
+      else
+      {
+         assert lock.isLockedBy(thread);
+         assert lock.getLockCount() > 0;
+         assert thread.getLockCount() == 0;
+         assert thread.getLockObject() == null;
+      }
+   }
+
+   @Override
+   public void searchStarted(Search search)
+   {
+      m_operations.clear();
+      m_state.clear();
+      
+      m_current = null;
+   }
+
+   @Override
+   public void stateAdvanced(Search search)
+   {
+      Integer id;
+      
+      id = search.getStateId();
+      
+      if (!m_operations.containsKey(id))       // Don't overwrite the original chain of Operations to get to the same state.  The original chain is more likely to be shorter.
+         m_operations.put(id, m_current);
+
+      if (s_logger.isLoggable(Level.FINE))
+         s_logger.fine("State Advanced: " + id);
+      
+      logState();
+   }
+
+   @Override
+   public void stateProcessed(Search search)
+   {
+      Integer id;
+
+      if (!(search instanceof DFSearch))  // Can't remove from m_operations since Search could go back to the state.
+         if (!(search instanceof BFSHeuristic))
+            return;
+
+      id = search.getStateId();
+
+      m_operations.remove(id);            // DFSearch won't ever revisit this state.  It is safe to remove and allow for cleanup.
+
+      if (s_logger.isLoggable(Level.FINE))
+         s_logger.fine("State Processed: " + id);
+   }
+
+   @Override
+   public void stateBacktracked(Search search)
+   {
+      switchTo(search);
+   }
+
+   @Override
+   public void stateRestored(Search search)
+   {
+      switchTo(search);
+   }
+   
+   private void switchTo(Search search)
+   {
+      Operation next;
+      Integer id;
+      
+      id   = search.getStateId();
+      next = m_operations.get(id);
+
+      if (s_logger.isLoggable(Level.FINE))
+         s_logger.fine("State Switching: " + id);
+
+      assert (id <= 0) || (m_operations.containsKey(id));
+      
+      switchTo(next);
+      
+      m_current = next;
+
+      logState();
+
+      if (s_logger.isLoggable(Level.FINE))
+         s_logger.fine("State Switched:  " + id);
+   }
+   
+   private void switchTo(Operation next)
+   {
+      Operation operation;
+      Integer index;
+      int i;
+      
+      for (operation = next; operation != null; operation = operation.getParent())  // Go through all of the operations leading back to the root.
+      {
+         m_index.put(operation, m_apply.size());  // Keep track of the index into m_apply where operation is found
+         m_apply.add(operation);
+      }
+      
+      index = null;
+      
+      for (operation = m_current; operation != null; operation = operation.getParent())  // Go through all of the operations leading back to the root.
+      {
+         index = m_index.get(operation);
+         
+         if (index != null)        // If a common ancestor is found, stop going back.
+            break;
+         
+         operation.revert();       // Revert the operation since it isn't common to both states.
+      }
+      
+      if (index == null)
+         index = m_apply.size();   // No common ancestor found.  Must apply all of the operations.
+      
+      for (i = index; --i >= 0; )  // Apply all of the operations required to get back to the "next" state.
+         m_apply.get(i).apply();
+
+      m_index.clear();
+      m_apply.clear();
+   }
+   
+   private void logState()
+   {
+      StringBuilder message;
+      String type;
+      Integer key, keys[], depth;
+      int i;
+      
+      if (!s_logger.isLoggable(Level.FINER))
+         return;
+
+      message = new StringBuilder();
+      keys    = m_state.keySet().toArray(EMPTY);
+      
+      Arrays.sort(keys);
+      message.append("State | Size = ");
+      message.append(keys.length);
+      
+      for (i = 0; i < keys.length; i++)
+      {
+         key   = keys[i];
+         depth = m_state.get(key);
+
+         if ((key & THREAD_FLAG) != 0)
+            type = "Thread";
+         else
+            type = "Lock";
+
+         message.append('\n');
+         message.append("Depth = ");
+         message.append(depth);
+         message.append(" | Key = ");
+         message.append(key & ~THREAD_FLAG);
+         message.append(" | ");
+         message.append(type);
+      }
+      
+      s_logger.finer(message.toString());
+   }
+   
+   private void logStack(ThreadInfo thread)
+   {
+      if (!s_logger.isLoggable(Level.FINEST))
+         return;
+      
+      s_logger.finest(thread.getStackTrace());
+   }
+   
+   private static int makeKey(ElementInfo lock)
+   {
+      return(lock.getObjectRef());
+   }
+   
+   private static int makeKey(ThreadInfo thread)
+   {
+      return(thread.getThreadObjectRef() ^ THREAD_FLAG);
+   }
+   
+   private class Operation
+   {
+      private final Operation m_parent;
+      private final Integer   m_key;
+      private final Integer   m_oldDepth;
+      private final Integer   m_newDepth;
+      
+      public Operation(ElementInfo lock, Integer newDepth)
+      {
+         this(makeKey(lock), newDepth);
+      }
+      
+      public Operation(ThreadInfo thread, Integer newDepth)
+      {
+         this(makeKey(thread), newDepth);
+      }
+      
+      private Operation(Integer key, Integer newDepth)
+      {
+         m_parent   = m_current;
+         m_current  = this;
+         m_key      = key;
+         m_newDepth = newDepth;
+         m_oldDepth = m_state.get(key);
+         
+         apply();
+      }
+      
+      public Operation getParent()
+      {
+         return(m_parent);
+      }
+      
+      public Integer getOldDepth()
+      {
+         return(m_oldDepth);
+      }
+      
+      public Integer getNewDepth()
+      {
+         return(m_newDepth);
+      }
+      
+      public void apply()
+      {
+         change(m_newDepth);
+         log("Apply ");
+      }
+      
+      public void revert()
+      {
+         change(m_oldDepth);
+         log("Revert");
+      }
+      
+      private void change(Integer depth)
+      {
+         Integer previous;
+         
+         if (depth == null)
+            m_state.remove(m_key);
+         else
+         {
+            previous = m_state.put(m_key, depth);
+            
+            assert previous == null;
+         }
+      }
+      
+      private void log(String header)
+      {
+         String message, subheader, depthStr, type;
+         Integer depth;
+         
+         if (!s_logger.isLoggable(Level.FINE))
+            return;
+
+         if (m_newDepth != null)
+         {
+            subheader = "Add   ";
+            depth     = m_newDepth;
+         }
+         else
+         {
+            subheader = "Remove";
+            depth     = m_oldDepth;
+         }
+
+         depthStr = String.valueOf(depth);
+         
+         switch (depthStr.length())
+         {
+            case 1: depthStr = "   " + depthStr; break;
+            case 2: depthStr = "  " + depthStr; break;
+            case 3: depthStr = " " + depthStr; break;
+            default: break;
+         }
+         
+         if ((m_key & THREAD_FLAG) != 0)
+            type = "Thread";
+         else
+            type = "Lock";
+         
+         message = header + " " + subheader + " | Depth = " + depthStr + " | Key = " + (m_key & ~THREAD_FLAG) + " | " + type;
+            
+         s_logger.fine(message);
+      }
+   }
+}
diff --git a/src/main/gov/nasa/jpf/listener/LogConsole.java b/src/main/gov/nasa/jpf/listener/LogConsole.java
new file mode 100644 (file)
index 0000000..bf84b46
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * simple logging facility that listens on a socket (e.g. for log output display)
+ */
+public class LogConsole {
+  
+  static int DEF_PORT = 20000; // keep this in sync with the gov.nasa.jpf.util.LogHandler
+  
+  class ShutdownHook implements Runnable {
+    @Override
+       public void run () {
+      if (running) {
+        // not very threadsafe, but worst thing that can happen is we close twice
+        killed = true;
+        System.out.println("\nLogConsole killed, shutting down");
+      }
+      try {
+        cs.close();
+        ss.close();
+      } catch (Throwable t) {
+        // not much we can do here anyway
+      }
+    }
+  }
+  
+  
+  boolean running;
+  
+  int port;
+  boolean autoclose;
+  boolean killed;
+  
+  ServerSocket ss;
+  Socket cs;
+  
+  public void run () {
+    running = true;
+    Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook()));
+    
+    if (port == 0) {
+      port = DEF_PORT;
+    }
+    
+    try {
+      ss = new ServerSocket(port);
+      
+      try {          
+        do {
+          System.out.println("LogConsole listening on port: " + port);
+
+          cs = ss.accept();
+          BufferedReader in = new BufferedReader( new InputStreamReader(cs.getInputStream()));
+          String msg; 
+          
+          System.out.println("LogConsole connected");
+          System.out.println("--------------------------------------------------------------------------------");
+          try {
+            
+            while ((msg = in.readLine()) != null) {
+              System.out.println(msg);
+            }
+            
+            System.out.println("--------------------------------------------------------------------------------");            
+            System.out.println("LogConsole disconnected");
+          } catch (IOException iox) {
+            System.err.println(iox);
+          }
+
+          in.close();
+          cs.close();
+        } while (!autoclose);
+
+        System.out.println("LogConsole closing");
+        
+      } catch (IOException iox) {
+        if (!killed) {
+          System.err.println("Error: LogConsole accept failed on port: " + port);
+        }
+      }
+      
+    } catch (IOException iox) {
+      System.err.println("Error: LogConsole cannot listen on port: " + port);
+    }
+    
+    running = false;
+  }
+
+  public void showUsage () {
+    System.out.println("LogConsole: socket based console logger");
+    System.out.println("     usage: java gov.nasa.jpf.tools.LogConsole {flags} [<port>]");
+    System.out.println("      args: -help         show this message");
+    System.out.println("            -autoclose    close the application upon disconnect");
+    System.out.println("            <port>        optional port number, default: " + DEF_PORT);
+  }
+  
+  boolean processArgs (String[] args) {
+    for (int i=0; i<args.length; i++) {
+      if (args[i].charAt(0) == '-') {
+        if (args[i].equals("-autoclose")) {
+          args[i] = null;
+          autoclose = true;
+        } else if (args[i].equals("-help")) {
+          showUsage();
+          return false;
+        } else {
+          System.err.println("Warning: unknown argument (see -help for usage): " + args[i]);
+        }
+      } else {
+        if (args[i].matches("[0-9]+")) {
+          if (port != 0) {
+            System.err.println("Error: only one port parameter allowed (see -help for usage): " + args[i]);
+            return false;
+          }
+          
+          try {
+            port = Integer.parseInt(args[i]);
+          } catch (NumberFormatException nfx) {
+            System.err.println("Error: illegal port spec: " + args[i]);
+            return false;
+          }
+        } else {
+          System.out.println("Error: unknown argument: " + args[i]);
+          return false;
+        }
+      }
+    }
+    
+    return true;
+  }
+
+  public static void main (String[] args) {
+    LogConsole console = new LogConsole();
+    
+    if (console.processArgs(args)) {
+      console.run();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/MethodAnalyzer.java b/src/main/gov/nasa/jpf/listener/MethodAnalyzer.java
new file mode 100644 (file)
index 0000000..e176cca
--- /dev/null
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.InstanceInvocation;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+
+/**
+ * analyzes call/execute sequences of methods
+ * closely modeled after the DeadlockAnalyzer, i.e. keeps it's own
+ * log and doesn't require full instruction trace
+ * 
+ * <2do> this needs to be refactored with DeadlockAnalyzer - the whole
+ * trace mgnt (except of the printing) can be made generic
+ */
+public class MethodAnalyzer extends ListenerAdapter {
+  
+  enum OpType { CALL (">  "),                 // invokeX breaks transition (e.g. blocked sync)
+                EXECUTE (" - "),              // method entered method after transition break
+                CALL_EXECUTE (">- "),         // call & enter within same transition
+                RETURN ("  <"),               // method returned
+                EXEC_RETURN (" -<"),          // enter & return in consecutive ops
+                CALL_EXEC_RETURN (">-<");     // call & enter & return in consecutive ops
+    String code;
+    OpType (String code){ this.code = code; }
+  };
+
+  static class MethodOp {
+    OpType type;
+    
+    ThreadInfo ti;
+    ElementInfo ei;
+    Instruction insn; // the caller
+    MethodInfo mi;    // the callee
+    int stackDepth;
+    
+    // this is used to keep our own trace
+    int stateId = 0;
+    MethodOp prevTransition;
+    MethodOp p;   // prev during execution
+    
+    MethodOp (OpType type, MethodInfo mi, ThreadInfo ti, ElementInfo ei, int stackDepth){
+      this.type = type;
+      this.ti = ti;
+      this.mi = mi;
+      this.ei = ei;
+      this.stackDepth = stackDepth;
+    }
+
+    MethodOp clone (OpType newType){
+      MethodOp op = new MethodOp(newType, mi, ti, ei, stackDepth);
+      op.p = p;
+      return op;
+    }
+
+    boolean isMethodEnter() {
+      return (type == OpType.CALL_EXECUTE) || (type == OpType.EXECUTE);
+    }
+
+    boolean isSameMethod(MethodOp op) {
+      return (mi == op.mi) && (ti == op.ti) && (ei == op.ei) && (stackDepth == op.stackDepth);
+    }
+
+    void printOn(PrintWriter pw, MethodAnalyzer analyzer) {
+      pw.print(ti.getId());
+      pw.print(": ");
+      
+      pw.print(type.code);
+      pw.print(' ');
+
+      if (analyzer.showDepth){
+        for (int i = 0; i < stackDepth; i++) {
+          pw.print('.');
+        }
+        pw.print(' ');
+      }
+
+      if (!mi.isStatic()){
+        if (ei.getClassInfo() != mi.getClassInfo()){ // method is in superclass
+          pw.print(mi.getClassName());
+          pw.print('<');
+          pw.print(ei);
+          pw.print('>');
+        } else { // method is in concrete object class
+          pw.print(ei);
+        }
+      } else {
+        pw.print(mi.getClassName());
+      }
+
+      pw.print('.');
+      
+      pw.print(Types.getDequalifiedMethodSignature(mi.getUniqueName()));
+    }
+    
+    @Override
+       public String toString() {
+      return "Op {" + ti.getName() + ',' + type.code +
+                   ',' + mi.getFullName() + ',' + ei + '}';
+    }
+  }
+
+  // report options
+
+  StringSetMatcher includes = null; //  means all
+  StringSetMatcher excludes = null; //  means none
+
+  int maxHistory;
+  String format;
+  boolean skipInit;
+  boolean showDepth;
+  boolean showTransition;
+  boolean showCompleted;
+
+  // execution environment
+
+  VM vm;
+  Search search;
+
+  OpType opType;
+  
+  // this is used to keep our own trace
+  MethodOp lastOp;
+  MethodOp lastTransition;
+  boolean isFirstTransition = true;
+
+  // this is set after we call revertAndFlatten during reporting
+  // (we can't call revertAndFlatten twice since it is destructive, but
+  // we might have to report several times in case we have several publishers)
+  MethodOp firstOp = null;
+  
+  // for HeuristicSearches. Ok, that's braindead but at least no need for cloning
+  HashMap<Integer,MethodOp> storedTransition = new HashMap<Integer,MethodOp>();
+
+  
+  public MethodAnalyzer (Config config, JPF jpf){
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+    
+    maxHistory = config.getInt("method.max_history", Integer.MAX_VALUE);
+    format = config.getString("method.format", "raw");
+    skipInit = config.getBoolean("method.skip_init", true);
+    showDepth = config.getBoolean("method.show_depth", false);
+    showTransition = config.getBoolean("method.show_transition", false);
+    
+    includes = StringSetMatcher.getNonEmpty(config.getStringArray("method.include"));
+    excludes = StringSetMatcher.getNonEmpty(config.getStringArray("method.exclude"));
+    
+    vm = jpf.getVM();
+    search = jpf.getSearch();
+  }
+
+
+  void addOp (VM vm, OpType opType, MethodInfo mi, ThreadInfo ti, ElementInfo ei, int stackDepth){
+    if (!(skipInit && isFirstTransition)) {
+      MethodOp op = new MethodOp(opType, mi, ti, ei, stackDepth);
+      if (lastOp == null){
+        lastOp = op;
+      } else {
+        op.p = lastOp;
+        lastOp = op;
+      }
+    }
+  }
+
+  boolean isAnalyzedMethod (MethodInfo mi){
+    if (mi != null){
+      String mthName = mi.getFullName();
+      return StringSetMatcher.isMatch(mthName, includes, excludes);
+    } else {
+      return false;
+    }
+  }
+
+  void printOn (PrintWriter pw) {
+    MethodOp start = firstOp;
+    int lastStateId  = Integer.MIN_VALUE;
+    int transition = skipInit ? 1 : 0;
+    int lastTid = start.ti.getId();
+    
+    for (MethodOp op = start; op != null; op = op.p) {
+
+      if (showTransition) {
+        if (op.stateId != lastStateId) {
+          lastStateId = op.stateId;
+          pw.print("------------------------------------------ #");
+          pw.println(transition++);
+        }
+      } else {
+        int tid = op.ti.getId();
+        if (tid != lastTid) {
+          lastTid = tid;
+          pw.println("------------------------------------------");
+        }
+      }
+      
+      op.printOn(pw, this);
+      pw.println();
+    }
+  }
+
+  // warning - this rotates pointers in situ, i.e. destroys the original structure
+  MethodOp revertAndFlatten (MethodOp start) {
+
+    MethodOp last = null;
+    MethodOp prevTransition = start.prevTransition;
+
+    for (MethodOp op = start; op != null;) {
+      MethodOp opp = op.p;
+      op.p = last;
+      
+      if (opp == null) {
+        if (prevTransition == null) {
+          return op;
+        } else {
+          last = op;
+          op = prevTransition;
+          prevTransition = op.prevTransition;
+        }
+      } else {
+        last = op;
+        op = opp;
+      }
+    }
+
+    return null;
+  }
+  
+  //--- SearchListener interface
+  // <2do> this is the same as DeadlockAnalyzer, except of xxOp type -> refactor
+  @Override
+  public void stateAdvanced (Search search){
+    
+    if (search.isNewState() && (lastOp != null)) {
+      int stateId = search.getStateId();
+      
+      for (MethodOp op=lastOp; op != null; op=op.p) {
+        op.stateId = stateId;
+      }
+      
+      lastOp.prevTransition = lastTransition;
+      lastTransition = lastOp;
+    }
+    
+    lastOp = null;
+    isFirstTransition = false;
+  }
+  
+  @Override
+  public void stateBacktracked (Search search){
+    int stateId = search.getStateId();
+    while ((lastTransition != null) && (lastTransition.stateId > stateId)){
+      lastTransition = lastTransition.prevTransition;
+    }
+    lastOp = null;
+  }
+  
+  @Override
+  public void stateStored (Search search) {
+    // always called after stateAdvanced
+    storedTransition.put(search.getStateId(), lastTransition);
+  }
+  
+  @Override
+  public void stateRestored (Search search) {
+    int stateId = search.getStateId();
+    MethodOp op = storedTransition.get(stateId);
+    if (op != null) {
+      lastTransition = op;
+      storedTransition.remove(stateId);  // not strictly required, but we don't come back
+    }
+  }
+
+
+  //--- VMlistener interface
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo thread, Instruction nextInsn, Instruction executedInsn) {
+    ThreadInfo ti;
+    MethodInfo mi;
+    ElementInfo ei = null;
+    
+    if (executedInsn instanceof JVMInvokeInstruction) {
+      JVMInvokeInstruction call = (JVMInvokeInstruction)executedInsn;
+      ti = thread;
+      mi = call.getInvokedMethod(ti);
+            
+      if (isAnalyzedMethod(mi)) {
+        OpType type;
+
+        // check if this was actually executed, or is a blocked sync call
+        if (ti.getNextPC() == call) { // re-executed -> blocked or overlayed
+          type = OpType.CALL;
+
+        } else { // executed
+          if (ti.isFirstStepInsn()) {
+            type = OpType.EXECUTE;
+          } else {
+            type = OpType.CALL_EXECUTE;
+          }
+        }
+
+        if (call instanceof InstanceInvocation) {
+          ei = ((InstanceInvocation)call).getThisElementInfo(ti);
+        }
+        
+        addOp(vm,type,mi,ti,ei, ti.getStackDepth());
+      }
+      
+    } else if (executedInsn instanceof JVMReturnInstruction) {
+      JVMReturnInstruction ret = (JVMReturnInstruction)executedInsn;
+      ti = thread;
+      StackFrame frame = ret.getReturnFrame();
+      mi = frame.getMethodInfo();
+
+      if (isAnalyzedMethod(mi)) {
+        if (!mi.isStatic()) {
+          int ref = frame.getThis();
+          if (ref != MJIEnv.NULL) {
+            ei = ti.getElementInfo(ref);
+          }
+        }
+        
+        addOp(vm,OpType.RETURN,mi,ti,ei, ti.getStackDepth()+1); // postExec-> frame already popped
+      }
+    }
+  }
+  
+  //--- the PubisherExtension part
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {
+
+    if (firstOp == null && lastTransition != null){ // do this just once
+      firstOp = revertAndFlatten(lastTransition);
+    }
+
+    if (firstOp == null){
+      return;
+    }
+
+    PrintWriter pw = publisher.getOut();
+    publisher.publishTopicStart("method ops " + publisher.getLastErrorId());
+
+
+    printOn(pw);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/MethodTracker.java b/src/main/gov/nasa/jpf/listener/MethodTracker.java
new file mode 100644 (file)
index 0000000..e52e477
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.INVOKESPECIAL;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.VirtualInvocation;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * simple tool to log method invocations
+ *
+ * at this point, it doesn't do fancy things yet, but gives a more high
+ * level idea of what got executed by JPF than the ExecTracker
+ */
+public class MethodTracker extends ListenerAdapter {
+
+  static final String INDENT = "  ";
+
+  MethodInfo lastMi;
+  PrintWriter out;
+
+  public MethodTracker (Config conf, JPF jpf) {
+    out = new PrintWriter(System.out, true);
+  }
+
+  void logMethodCall(ThreadInfo ti, MethodInfo mi, int stackDepth) {
+    out.print(ti.getId());
+    out.print(":");
+
+    for (int i=0; i<stackDepth%80; i++) {
+      out.print(INDENT);
+    }
+
+    if (mi.isMJI()) {
+      out.print("native ");
+    }
+
+    out.print(mi.getFullName());
+
+    if (ti.isFirstStepInsn()) {
+      out.print("...");
+    }
+
+    out.println();
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute) {
+    MethodInfo mi = insnToExecute.getMethodInfo();
+
+    if (mi != lastMi) {
+      logMethodCall(ti, mi, ti.getStackDepth());
+      lastMi = mi;
+
+    } else if (insnToExecute instanceof JVMInvokeInstruction) {
+      MethodInfo callee;
+
+      // that's the only little gist of it - if this is a VirtualInvocation,
+      // we have to dig the callee out by ourselves (it's not known
+      // before execution)
+
+      if (insnToExecute instanceof VirtualInvocation) {
+        VirtualInvocation callInsn = (VirtualInvocation)insnToExecute;
+        int objref = callInsn.getCalleeThis(ti);
+        if (objref != MJIEnv.NULL){
+          callee = callInsn.getInvokedMethod(ti, objref);
+        } else {
+          return; // this is causing a NPE, so don't report it as a unknown callee
+        }
+
+      } else if (insnToExecute instanceof INVOKESPECIAL) {
+        INVOKESPECIAL callInsn = (INVOKESPECIAL)insnToExecute;
+        callee = callInsn.getInvokedMethod(ti);
+
+      } else {
+        JVMInvokeInstruction callInsn = (JVMInvokeInstruction)insnToExecute;
+        callee = callInsn.getInvokedMethod(ti);
+      }
+
+      if (callee != null) {
+        if (callee.isMJI()) {
+          logMethodCall(ti, callee, ti.getStackDepth()+1);
+        }
+      } else {
+        out.println("ERROR: unknown callee of: " + insnToExecute);
+      }
+    }
+  }
+
+  /*
+   * those are not really required, but mark the transition boundaries
+   */
+  @Override
+  public void stateRestored(Search search) {
+    int id = search.getStateId();
+    out.println("----------------------------------- [" +
+                       search.getDepth() + "] restored: " + id);
+  }
+
+  //--- the ones we are interested in
+  @Override
+  public void searchStarted(Search search) {
+    out.println("----------------------------------- search started");
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    int id = search.getStateId();
+
+    out.print("----------------------------------- [" +
+                     search.getDepth() + "] forward: " + id);
+    if (search.isNewState()) {
+      out.print(" new");
+    } else {
+      out.print(" visited");
+    }
+
+    if (search.isEndState()) {
+      out.print(" end");
+    }
+
+    out.println();
+
+    lastMi = null;
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    int id = search.getStateId();
+
+    lastMi = null;
+
+    out.println("----------------------------------- [" +
+                       search.getDepth() + "] backtrack: " + id);
+  }
+
+  @Override
+  public void searchFinished(Search search) {
+    out.println("----------------------------------- search finished");
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/NoStateCycles.java b/src/main/gov/nasa/jpf/listener/NoStateCycles.java
new file mode 100644 (file)
index 0000000..9b39585
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+
+
+/**
+ * If there is a cycle in the states of the program, JPF doesn't execute the
+ * already visited states.  A cycle in the states may represent the ability for
+ * the program to hang.  This property finds and outputs cycles in the states
+ * so that they may be investigated.
+ *
+ * The following might need to be added to listener property...
+ *   gov.nasa.jpf.tools.SimpleIdleFilter
+ *   gov.nasa.jpf.tools.IdleFilter
+ */
+
+public class NoStateCycles extends PropertyListenerAdapter {
+
+  private final HashSet<Integer>   m_inStack = new HashSet<Integer>();
+  private final ArrayList<Integer> m_stack   = new ArrayList<Integer>();
+
+  private int m_cycleFound = -1;
+  private int m_stackPos   = -1;
+
+  public NoStateCycles(Config config) {
+    if (!config.getString("search.class").equals("gov.nasa.jpf.search.DFSearch"))
+      config.throwException("search.class must be gov.nasa.jpf.search.DFSearch");   // Or any class which does a depth first search.
+  }
+
+  @Override
+   public void stateAdvanced(Search search) {
+     SystemState state;
+     Integer id;
+
+     state = search.getVM().getSystemState();
+     if (state.isInitState())
+       return;
+
+     id = state.getId();
+
+     if ((m_stackPos < 0) && (m_inStack.contains(id))) {
+       m_cycleFound = id;
+
+       for (m_stackPos = m_stack.size() - 1; m_stackPos >= 0; m_stackPos--)
+         if (m_stack.get(m_stackPos).equals(id))
+           break;
+     }
+
+     m_stack.add(id);
+     m_inStack.add(id);
+   }
+
+  @Override
+   public void stateBacktracked(Search search) {
+     Integer id;
+     int pos;
+
+     pos = m_stack.size() - 1;
+     id  = m_stack.remove(pos);
+     m_inStack.remove(id);
+
+     if (m_stackPos >= pos)
+       m_stackPos = -1;
+   }
+
+  @Override
+   public boolean check(Search search, VM vm) {
+     return(m_cycleFound < 0);
+   }
+
+   @Override
+   public void reset () {
+     m_cycleFound = -1;
+   }
+
+   @Override
+  public String getErrorMessage () {
+     StringBuilder result;
+     int i, id;
+
+     result = new StringBuilder();
+     result.append("States:\n");
+
+     for (i = m_stack.size() - 1; i >= 0; i--) {
+       id = m_stack.get(i);
+
+       result.append("  ");
+       result.append(id);
+       result.append('\n');
+
+       if (id == m_cycleFound)
+         break;
+     }
+
+     return(result.toString());
+   }
+
+   @Override
+  public String getExplanation () {
+     return("Finds cycles in states.  A cycle suggests that the program might be able to hang.");
+   }
+}
diff --git a/src/main/gov/nasa/jpf/listener/NonSharedChecker.java b/src/main/gov/nasa/jpf/listener/NonSharedChecker.java
new file mode 100644 (file)
index 0000000..1a6654b
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.jvm.bytecode.ALOAD;
+import gov.nasa.jpf.jvm.bytecode.ARETURN;
+import gov.nasa.jpf.jvm.bytecode.ASTORE;
+import gov.nasa.jpf.jvm.bytecode.IFNONNULL;
+import gov.nasa.jpf.jvm.bytecode.IFNULL;
+import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.jvm.bytecode.MONITORENTER;
+import gov.nasa.jpf.jvm.bytecode.VirtualInvocation;
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ *
+ */
+public class NonSharedChecker extends ListenerAdapter {
+
+  boolean throwOnCycle = false;
+
+  static class Access {
+    ThreadInfo ti;
+    Access prev;
+
+    Access(ThreadInfo ti, Access prev){
+      this.ti = ti;
+      this.prev = prev;
+    }
+
+    // <2do> get a better hashCode for state hashing
+    public int hashCode() {
+      int h = ti.getId();
+      for (Access p = prev; p!= null; p = p.prev){
+        h = 31*h + p.ti.getId();
+      }
+      return h;
+    }
+    // but we don't care for equals()
+  }
+
+  public NonSharedChecker (Config conf){
+    throwOnCycle = conf.getBoolean("nonshared.throw_on_cycle");
+  }
+
+  boolean isNonShared(ElementInfo ei){
+    ClassInfo ci = ei.getClassInfo();
+    return (ci.getAnnotation("gov.nasa.jpf.annotation.NonShared") != null);
+  }
+
+  boolean checkLiveCycles (ElementInfo ei, ThreadInfo ti, Access ac){
+    if (ti == ac.ti){
+      return true; // Ok, fine - no need to record
+
+    } else {
+      boolean foundLiveOne = false;
+      for (Access a = ac; a != null; a = a.prev){
+        ThreadInfo t = a.ti;
+        if (t == ti){ // cycle detected
+          return !foundLiveOne;
+        }
+        foundLiveOne = (foundLiveOne || t.isAlive()); // <2do> maybe we should check for non-blocked threads
+      }
+
+      // new one, record it in the access history of the object
+      ac = new Access(ti, ac);
+      ei = ei.getModifiableInstance();
+      ei.setObjectAttr(ac);
+    }
+
+    return true;
+  }
+
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn){
+
+    ElementInfo ei = null;
+
+    if (ti.isFirstStepInsn()) {
+      return;
+    }
+
+    if (insn instanceof InstanceFieldInstruction){
+      ei = ((InstanceFieldInstruction)insn).peekElementInfo(ti);
+    } else if (insn instanceof VirtualInvocation){
+      ei = ((VirtualInvocation)insn).getThisElementInfo(ti);  // Outch - that's expensive
+    } else if (insn instanceof MONITORENTER ||
+               insn instanceof ASTORE ||
+               insn instanceof ARETURN ||
+               insn instanceof IFNONNULL ||
+               insn instanceof IFNULL) {
+      StackFrame frame = ti.getTopFrame();
+      int ref = frame.peek();
+      if (ref != -1){
+        ei = ti.getElementInfo(ref);
+      }
+    } else if (insn instanceof ALOAD){
+      StackFrame frame = ti.getTopFrame();
+      int ref = frame.getLocalVariable(((ALOAD)insn).getLocalVariableIndex());
+      if (ref != -1){
+        ei = ti.getElementInfo(ref);
+      }
+    }
+
+    if (ei != null){
+      Access ac = ei.getObjectAttr(Access.class);
+      if (ac != null){
+        if (!checkLiveCycles(ei,ti,ac)){
+          StringBuilder sb = new StringBuilder("NonShared object: ");
+          sb.append( ei);
+          sb.append(" accessed in live thread cycle: ");
+          sb.append( ti.getName());
+          for (Access a = ac; a != null; a = a.prev ){
+            sb.append(',');
+            sb.append(a.ti.getName());
+          }
+          String msg = sb.toString();
+
+          if (throwOnCycle){
+            ti.setNextPC( ti.createAndThrowException("java.lang.AssertionError", msg));
+          } else {
+            System.err.println("WARNING: " + msg);
+            System.err.println("\tat " + insn.getSourceLocation());
+          }
+          return;
+        }
+      }
+    }
+  }
+
+  @Override
+  public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei){
+    if (isNonShared(ei)){
+      ei.setObjectAttrNoClone(new Access(ti, null));
+    }
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/listener/NullTracker.java b/src/main/gov/nasa/jpf/listener/NullTracker.java
new file mode 100644 (file)
index 0000000..ac7bec8
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.ARETURN;
+import gov.nasa.jpf.jvm.bytecode.ASTORE;
+import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
+import gov.nasa.jpf.jvm.bytecode.PUTSTATIC;
+import gov.nasa.jpf.jvm.bytecode.RETURN;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.bytecode.FieldInstruction;
+import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
+import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction;
+import gov.nasa.jpf.vm.bytecode.InstructionInterface;
+import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
+import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
+import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
+import gov.nasa.jpf.vm.bytecode.ReturnValueInstruction;
+import gov.nasa.jpf.vm.bytecode.WriteInstruction;
+import java.io.PrintWriter;
+
+/**
+ * trace where nulls come from - which is either a GETFIELD/STATIC, an
+ * JVMInvokeInstruction, an LocalVariableInstruction or a missing init.
+ * 
+ * Record/accumulate the causes in an attribute and use the attribute 
+ * to explain NPEs
+ */
+public class NullTracker extends ListenerAdapter {
+
+  public static abstract class NullSource {
+    protected InstructionInterface insn;
+    protected ThreadInfo ti;
+    protected ElementInfo ei;
+    
+    protected NullSource cause;
+    
+    NullSource (ThreadInfo ti, InstructionInterface insn, ElementInfo ei){
+      this.ti = ti;
+      this.insn = insn;
+      this.ei = ei;
+    }
+    
+    public void setCause (NullSource cause){
+      this.cause = cause;
+    }
+    
+    abstract void printOn (PrintWriter pw);
+    
+    void printInsnOn (PrintWriter pw){
+      pw.printf("    instruction: [%04x] %s\n", insn.getPosition(), insn.toString());
+    }
+        
+    void printThreadInfoOn (PrintWriter pw){
+      pw.println("    executed by: " + ti.getName() + " (id=" + ti.getId() + ")");
+    }
+    
+    void printMethodInfoOn (PrintWriter pw, String msg, InstructionInterface instruction){
+      MethodInfo mi = instruction.getMethodInfo();
+      ClassInfo ci = mi.getClassInfo();
+      pw.println( msg + ci.getName() + '.' + mi.getLongName() + " (" + instruction.getFilePos() + ')');
+    }
+    
+    void printCauseOn (PrintWriter pw){
+      if (cause != null){
+        pw.println("set by: ");
+        cause.printOn(pw);
+      }
+    }
+  }
+  
+  
+  public static class LocalSource extends NullSource {
+    protected LocalVarInfo local;
+    
+    public LocalSource (ThreadInfo ti, LocalVariableInstruction insn, LocalVarInfo local){
+      super(ti, insn, null);
+      this.local = local;
+    }
+    
+    @Override
+    void printOn (PrintWriter pw){
+      printInsnOn(pw);
+      if (local != null){
+        pw.println("      for local: " + local.getName());
+      } else {
+        pw.println("     for local: #" + ((LocalVariableInstruction)insn).getLocalVariableSlot());
+      }
+      printMethodInfoOn(pw, "      in method: ", insn);
+      printThreadInfoOn(pw);
+      
+      printCauseOn(pw);
+    }
+  }
+  
+  public static class FieldSource extends NullSource {
+    public FieldSource (ThreadInfo ti, FieldInstruction insn, ElementInfo ei){
+      super(ti,insn,ei);
+    }
+    
+    @Override
+    void printOn (PrintWriter pw){
+      FieldInfo fi = ((FieldInstruction)insn).getFieldInfo();
+      MethodInfo mi = insn.getMethodInfo();
+            
+      printInsnOn(pw);
+      pw.println("      for field: " + fi.getFullName());
+      printMethodInfoOn(pw, "      in method: ", insn);
+      printThreadInfoOn(pw);
+      
+      printCauseOn(pw);
+    }
+  }
+
+  public static class MethodSource extends NullSource {
+    InvokeInstruction call;
+    
+    public MethodSource (ThreadInfo ti, InstructionInterface returnInsn, InvokeInstruction call, ElementInfo ei){
+      super(ti,returnInsn,ei);
+      this.call = call;
+    }
+    
+    @Override
+    void printOn (PrintWriter pw){            
+      printInsnOn(pw);
+      printMethodInfoOn(pw, "      of method: ", insn);
+      
+      if (ei != null){
+        pw.println("     for object: " + ei);
+      }
+      printMethodInfoOn(pw, "      called by: ", call);
+      printThreadInfoOn(pw);
+      
+      printCauseOn(pw);
+    }    
+  }
+  
+  public static class CtorSource extends MethodSource {
+    public CtorSource (ThreadInfo ti, Instruction returnInsn, InvokeInstruction call, ElementInfo ei){
+      super(ti,returnInsn,call, ei);
+    }
+    
+    @Override
+    void printOn (PrintWriter pw){ 
+      printMethodInfoOn(pw, "   missing init: ", insn);
+      
+      if (ei != null){
+        pw.println("     for object: " + ei);
+      }
+      printMethodInfoOn(pw, "      called by: ", call);
+      printThreadInfoOn(pw);
+      
+      printCauseOn(pw);
+    }    
+  }
+
+  //---------------------------------------------------------------------------------
+  
+  protected NullSource nullSource;
+  
+  public NullTracker (Config config, JPF jpf){
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+  
+  protected void checkCtorSourcePre (ThreadInfo ti, ReturnInstruction insn){
+    MethodInfo mi = insn.getMethodInfo();
+    if (mi.isCtor()) {
+      StackFrame callerFrame = null;
+      InvokeInstruction call = null;
+      ElementInfo ei = ti.getThisElementInfo();
+      ClassInfo ci = ei.getClassInfo();
+      int nInstance = ci.getNumberOfDeclaredInstanceFields();
+      
+      for (int i = 0; i < nInstance; i++) {
+        FieldInfo fi = ci.getDeclaredInstanceField(i);
+        if (fi.isReference()) {
+          int ref = ei.getReferenceField(fi);
+          if (ref == MJIEnv.NULL) {
+            ei = ei.getModifiableInstance();  // why do we need this in a ctor?
+            if (call == null) {
+              callerFrame = ti.getCallerStackFrame();
+              call = (InvokeInstruction) callerFrame.getPC();
+            }
+            NullSource attr = new CtorSource(ti, insn, call, ti.getThisElementInfo());
+            ei.setFieldAttr(fi, attr);
+          }
+        }
+      }
+    }
+  }
+  
+  protected void checkFieldSourcePre (ThreadInfo ti, WriteInstruction put){
+    FieldInfo fi = put.getFieldInfo();
+    if (fi.isReference()) {
+      StackFrame frame = ti.getTopFrame();
+      int valSlot = put.getValueSlot(frame);
+      int ref = frame.getSlot(valSlot);
+
+      if (ref == MJIEnv.NULL) { // field will be set to null
+        ElementInfo ei = put.getElementInfo(ti);
+        NullSource attr = new FieldSource(ti, (FieldInstruction)put, ei);
+
+        NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
+        if (cause != null) {
+          attr.setCause(cause);
+          frame.replaceSlotAttr(valSlot, cause, attr);
+        } else {
+          frame.addSlotAttr(valSlot, attr);
+        }
+      }
+    }    
+  }
+  
+  protected void checkMethodSourcePre (ThreadInfo ti, ReturnValueInstruction aret){
+    StackFrame frame = ti.getTopFrame();
+    int valSlot = aret.getValueSlot(frame);
+    int ref = frame.getSlot(valSlot);
+    
+    if (ref == MJIEnv.NULL) {
+      StackFrame callerFrame = ti.getCallerStackFrame();
+      InvokeInstruction call = (InvokeInstruction) callerFrame.getPC();
+      NullSource attr = new MethodSource(ti, aret, call, ti.getThisElementInfo());
+
+      NullSource cause = frame.getSlotAttr(valSlot, NullSource.class);
+      if (cause != null) {
+        attr.setCause(cause);
+        frame.replaceSlotAttr(valSlot,cause, attr);
+      } else {
+        frame.addSlotAttr(valSlot,attr);
+      }
+    }
+  }
+  
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insn) {
+    
+    if (insn instanceof ARETURN){
+      checkMethodSourcePre( ti, (ARETURN)insn);
+      
+    } else if (insn instanceof PUTFIELD || insn instanceof PUTSTATIC){
+      checkFieldSourcePre( ti, (WriteInstruction) insn);
+      
+    } else if (insn instanceof RETURN){
+      checkCtorSourcePre(ti, (RETURN) insn);
+    }
+  }
+
+  
+  protected void checkLocalSourcePost (ThreadInfo ti, LocalVariableInstruction insn){
+    int slotIdx = insn.getLocalVariableSlot();
+    StackFrame frame = ti.getTopFrame();
+    int ref = frame.getSlot(slotIdx);
+    if (ref == MJIEnv.NULL) {
+      LocalVarInfo lv = insn.getLocalVarInfo();
+      NullSource attr = new LocalSource(ti, insn, lv);
+
+      NullSource cause = frame.getSlotAttr(slotIdx, NullSource.class);
+      if (cause != null) {
+        attr.setCause(cause);
+        frame.replaceSlotAttr(slotIdx, cause, attr);
+      } else {
+        frame.addSlotAttr(slotIdx, attr);
+      }
+    }
+  }
+  
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction insn){
+    
+    // we need to do LocalVariableInstruction post exec since it did overwrite the attr if it had an immediate operand
+    if (insn instanceof ASTORE) {
+      checkLocalSourcePost( ti, (LocalVariableInstruction)insn);
+    }
+  }
+    
+  @Override
+  public void exceptionThrown(VM vm, ThreadInfo ti, ElementInfo thrownException) {
+    if (thrownException.instanceOf("Ljava/lang/NullPointerException;")){
+      StackFrame frame = ti.getTopFrame();
+      Instruction insn = ti.getPC();
+      
+      if (insn instanceof InstanceFieldInstruction){  // field access on null object
+        int objSlot = ((InstanceFieldInstruction)insn).getObjectSlot(frame);
+        NullSource attr = frame.getSlotAttr( objSlot,NullSource.class);
+        if (attr != null) {
+          nullSource = attr;
+        }
+        
+      } else if (insn instanceof InstanceInvokeInstruction) { // call on a null object
+        int objSlot = ((InstanceInvokeInstruction)insn).getObjectSlot(frame);
+        NullSource attr = frame.getSlotAttr( objSlot, NullSource.class);
+        if (attr != null) {
+          nullSource = attr;
+        }
+      }
+    }
+  }
+
+  
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {    
+    if (nullSource != null){ // otherwise we don't have anything to report
+      PrintWriter pw = publisher.getOut();
+      publisher.publishTopicStart("NullTracker " + publisher.getLastErrorId());
+
+      pw.println("null value set by: ");
+      nullSource.printOn(pw);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/NumericValueChecker.java b/src/main/gov/nasa/jpf/listener/NumericValueChecker.java
new file mode 100644 (file)
index 0000000..ee7da7e
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.*;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.FieldSpec;
+import gov.nasa.jpf.util.VarSpec;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * little listener that checks value ranges of specified numeric fields and local vars
+ *
+ * configuration examples:
+ *
+ *  range.fields=speed,..
+ *  range.speed.field=x.y.SomeClass.velocity
+ *  range.speed.min=300
+ *  range.speed.max=500
+ *
+ *  range.vars=altitude,..
+ *  range.altitude.var=x.y.SomeClass.computeTrajectory(int):a
+ *  range.altitude.min=125000
+ *
+ */
+public class NumericValueChecker extends PropertyListenerAdapter {
+
+  static abstract class RangeCheck {
+    double min, max;
+
+    RangeCheck (double min, double max){
+      this.min = min;
+      this.max = max;
+    }
+
+    String check (long v){
+      if (v < (long)min){
+        return String.format("%d < %d", v, (long)min);
+      } else if (v > (long)max){
+        return String.format("%d > %d", v, (long)max);
+      }
+      return null;
+    }
+    String check (double v){
+      if (v < min){
+        return String.format("%f < %f", v, min);
+      } else if (v > (long)max){
+        return String.format("%f > %f", v, max);
+      }
+      return null;
+    }
+  }
+
+  static class FieldCheck extends RangeCheck {
+    FieldSpec fspec;
+
+    FieldCheck (FieldSpec fspec, double min, double max){
+      super(min,max);
+      this.fspec = fspec;
+    }
+
+    boolean matches (FieldInfo fi){
+      return fspec.matches(fi);
+    }
+  }
+
+  static class VarCheck extends RangeCheck {
+    VarSpec vspec;
+
+    VarCheck (VarSpec vspec, double min, double max){
+      super(min,max);
+      this.vspec = vspec;
+    }
+
+    LocalVarInfo getMatch (MethodInfo mi, int pc, int slotIdx){
+      return vspec.getMatchingLocalVarInfo(mi, pc, slotIdx);
+    }
+  }
+  
+  class Visitor extends JVMInstructionVisitorAdapter {
+    
+    void checkFieldInsn (JVMFieldInstruction insn){
+      if (fieldChecks != null){
+        FieldInfo fi = insn.getFieldInfo();
+
+        for (int i = 0; i < fieldChecks.length; i++) {
+          FieldCheck fc = fieldChecks[i];
+          if (fc.matches(fi)) {
+            if (fi.isNumericField()) {
+              long lv = insn.getLastValue();
+              String errorCond = fi.isFloatingPointField()
+                      ? fc.check(Double.longBitsToDouble(lv)) : fc.check(lv);
+
+              if (errorCond != null) {
+                error = String.format("field %s out of range: %s\n\t at %s",
+                        fi.getFullName(), errorCond, insn.getSourceLocation());
+                vm.breakTransition("fieldValueOutOfRange"); // terminate this transition
+                break;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    void checkVarInsn (JVMLocalVariableInstruction insn){
+      if (varChecks != null){
+        ThreadInfo ti = ThreadInfo.getCurrentThread();
+        StackFrame frame = ti.getTopFrame();
+        int slotIdx = insn.getLocalVariableIndex();
+
+        for (int i = 0; i < varChecks.length; i++) {
+          VarCheck vc = varChecks[i];
+
+          MethodInfo mi = insn.getMethodInfo();
+          int pc = insn.getPosition()+1; // the scope would begin on the next insn, we are still at the xSTORE
+          LocalVarInfo lvar = vc.getMatch(mi, pc, slotIdx);
+          if (lvar != null) {
+            long v = lvar.getSlotSize() == 1 ? frame.getLocalVariable(slotIdx) : frame.getLongLocalVariable(slotIdx);
+            String errorCond = lvar.isFloatingPoint()
+                    ? vc.check(Double.longBitsToDouble(v)) : vc.check(v);
+
+            if (errorCond != null) {
+              error = String.format("local variable %s out of range: %s\n\t at %s",
+                      lvar.getName(), errorCond, insn.getSourceLocation());
+              vm.breakTransition("localVarValueOutOfRange"); // terminate this transition
+              break;
+            }
+          }
+        }
+      }
+    }
+
+    @Override
+    public void visit(PUTFIELD insn){
+      checkFieldInsn(insn);
+    }
+    @Override
+    public void visit(PUTSTATIC insn){
+      checkFieldInsn(insn);
+    }
+
+    @Override
+    public void visit(ISTORE insn){
+      checkVarInsn(insn);
+    }
+    @Override    
+    public void visit(LSTORE insn){
+      checkVarInsn(insn);
+    }
+    @Override
+    public void visit(FSTORE insn){
+      checkVarInsn(insn);
+    }
+    @Override
+    public void visit(DSTORE insn){
+      checkVarInsn(insn);
+    }
+
+  }
+
+
+  VM vm;
+  Visitor visitor;
+
+  // the stuff we monitor
+  FieldCheck[] fieldChecks;
+  VarCheck[] varChecks;
+
+  String error; // where we store errorCond details
+
+  public NumericValueChecker (Config conf){
+    visitor = new Visitor();
+
+    createFieldChecks(conf);
+    createVarChecks(conf);
+  }
+
+  private void createFieldChecks(Config conf){
+    String[] checkIds = conf.getCompactTrimmedStringArray("range.fields");
+    if (checkIds.length > 0){
+      fieldChecks = new FieldCheck[checkIds.length];
+
+      for (int i = 0; i < checkIds.length; i++) {
+        String id = checkIds[i];
+        FieldCheck check = null;
+        String keyPrefix = "range." + id;
+        String spec = conf.getString(keyPrefix + ".field");
+        if (spec != null) {
+          FieldSpec fs = FieldSpec.createFieldSpec(spec);
+          if (fs != null) {
+            double min = conf.getDouble(keyPrefix + ".min", Double.MIN_VALUE);
+            double max = conf.getDouble(keyPrefix + ".max", Double.MAX_VALUE);
+            check = new FieldCheck(fs, min, max);
+          }
+        }
+        if (check == null) {
+          throw new JPFConfigException("illegal field range check specification for " + id);
+        }
+        fieldChecks[i] = check;
+      }
+    }
+  }
+
+  private void createVarChecks(Config conf){
+    String[] checkIds = conf.getCompactTrimmedStringArray("range.vars");
+    if (checkIds.length > 0){
+      varChecks = new VarCheck[checkIds.length];
+
+      for (int i = 0; i < checkIds.length; i++) {
+        String id = checkIds[i];
+        VarCheck check = null;
+        String keyPrefix = "range." + id;
+        String spec = conf.getString(keyPrefix + ".var");
+        if (spec != null) {
+          VarSpec vs = VarSpec.createVarSpec(spec);
+          if (vs != null) {
+            double min = conf.getDouble(keyPrefix + ".min", Double.MIN_VALUE);
+            double max = conf.getDouble(keyPrefix + ".max", Double.MAX_VALUE);
+            check = new VarCheck(vs, min, max);
+          }
+        }
+        if (check == null) {
+          throw new JPFConfigException("illegal variable range check specification for " + id);
+        }
+        varChecks[i] = check;
+      }
+    }
+  }
+
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+    this.vm = vm;
+    ((JVMInstruction)executedInsn).accept(visitor);
+  }
+
+  @Override
+  public boolean check(Search search, VM vm) {
+    return (error == null);
+  }
+
+  @Override
+  public void reset () {
+    error = null;
+  }
+
+  @Override
+  public String getErrorMessage(){
+    return error;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/OOMEInjector.java b/src/main/gov/nasa/jpf/listener/OOMEInjector.java
new file mode 100644 (file)
index 0000000..1af58f1
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.NEW;
+import gov.nasa.jpf.util.LocationSpec;
+import gov.nasa.jpf.util.TypeSpec;
+import gov.nasa.jpf.vm.bytecode.NewInstruction;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * simulator for OutOfMemoryErrors. This can be configured to either
+ * fire for a specified location range (file:line) or specified types.
+ * Ranges are transitive, i.e. everything called from within it should also
+ * trigger.  
+ * 
+ * Since our only action is to inject OutOfMemoryErrors, we don't need
+ * to implement a Property interface
+ */
+public class OOMEInjector extends ListenerAdapter {
+
+  static class OOME {}
+  static OOME throwOOME = new OOME(); // we can reuse the same object as an attribute
+  
+  List<LocationSpec> locations = new ArrayList<LocationSpec>();
+  List<TypeSpec> types = new ArrayList<TypeSpec>();
+  
+  public OOMEInjector (Config config, JPF jpf){
+    String[] spec = config.getStringArray("oome.locations");
+    if (spec != null){
+      for (String s : spec){
+        LocationSpec locSpec = LocationSpec.createLocationSpec(s);
+        if (locSpec != null){
+          locations.add(locSpec);
+        }
+      }
+    }
+    
+    spec = config.getStringArray("oome.types");
+    if (spec != null){
+      for (String s : spec){
+        TypeSpec typeSpec = TypeSpec.createTypeSpec(s);
+        if (typeSpec != null){
+          types.add(typeSpec);
+        }
+      }      
+    }
+  }
+  
+  protected void markMatchingInstructions (MethodInfo mi, LocationSpec locSpec){
+    int first = locSpec.getFromLine();
+    int[] lineNumbers = mi.getLineNumbers();
+              
+    if (lineNumbers != null && first >= lineNumbers[0]){
+      int last = locSpec.getToLine();
+      for (int i=0; i<lineNumbers.length; i++){
+        int l = lineNumbers[i];
+        if (last < lineNumbers[i]){
+          return;
+        } else {
+          Instruction insn = mi.getInstruction(i);
+          insn.addAttr(throwOOME);                
+        }
+      }
+    }    
+  }
+  
+  @Override
+  public void classLoaded (VM vm, ClassInfo loadedClass){
+    String fname = loadedClass.getSourceFileName();
+    
+    for (TypeSpec typeSpec : types){
+      if (typeSpec.matches(loadedClass)){
+        loadedClass.addAttr(throwOOME);
+      }
+    }
+
+    // if we have a matching typespec this could be skipped, but maybe
+    // we also want to cover statis methods of this class
+    for (LocationSpec locSpec : locations){
+      if (locSpec.matchesFile(fname)){
+        for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()){
+          markMatchingInstructions(mi, locSpec);
+        }
+      }
+    }
+  }
+  
+  protected boolean checkCallerForOOM (StackFrame frame, Instruction insn){
+    // these refer to the calling code
+    return (insn.hasAttr(OOME.class) || frame.hasFrameAttr(OOME.class));
+  }
+  
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+    if (insnToExecute instanceof NewInstruction){
+      if (checkCallerForOOM(ti.getTopFrame(), insnToExecute)){
+        // we could use Heap.setOutOfMemory(true), but then we would have to reset
+        // if the app handles it so that it doesn't throw outside the specified locations.
+        // This would require more effort than throwing explicitly
+        Instruction nextInsn = ti.createAndThrowException("java.lang.OutOfMemoryError");
+        ti.skipInstruction(nextInsn);
+      }
+    }
+  }
+  
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction insn, Instruction executedInsn){
+    
+    if (executedInsn instanceof JVMInvokeInstruction){
+      StackFrame frame = ti.getTopFrame();
+      
+      if (frame.getPC() != executedInsn){ // means the call did succeed
+        if (checkCallerForOOM(frame.getPrevious(), executedInsn)){
+          frame.addFrameAttr(throwOOME); // propagate caller OOME context
+        }
+      }
+      
+    } else if (executedInsn instanceof NEW){
+      if (!types.isEmpty()){
+        int objRef = ((NEW) executedInsn).getNewObjectRef();
+        if (objRef != MJIEnv.NULL) {
+          ClassInfo ci = vm.getClassInfo(objRef);
+          if (ci.hasAttr(OOME.class)) {
+            Instruction nextInsn = ti.createAndThrowException("java.lang.OutOfMemoryError");
+            ti.setNextPC(nextInsn);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/ObjectTracker.java b/src/main/gov/nasa/jpf/listener/ObjectTracker.java
new file mode 100644 (file)
index 0000000..81f4885
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.util.IntSet;
+import gov.nasa.jpf.util.SortedArrayIntSet;
+import gov.nasa.jpf.util.StateExtensionClient;
+import gov.nasa.jpf.util.StateExtensionListener;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.bytecode.FieldInstruction;
+import gov.nasa.jpf.vm.bytecode.InstanceFieldInstruction;
+import gov.nasa.jpf.vm.bytecode.InstanceInvokeInstruction;
+import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * listener that keeps track of all operations on objects that are specified by
+ * reference value or types
+ */
+public class ObjectTracker extends ListenerAdapter implements StateExtensionClient {
+  
+  static class Attr {
+    // nothing here, just a tag
+  }
+  
+  static final Attr ATTR = new Attr(); // we need only one
+  
+  enum OpType { NEW, CALL, PUT, GET, FREE };
+  
+  static class LogRecord {
+    ElementInfo ei;
+    ThreadInfo ti;
+    Instruction insn;
+    OpType opType;
+    int stateId;
+    
+    LogRecord prev;
+    
+    LogRecord (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn, LogRecord prev){
+      this.opType = opType;
+      this.ei = ei;
+      this.ti = ti;
+      this.insn = insn;
+      this.prev = prev;
+      this.stateId = ti.getVM().getStateId();
+    }
+    
+    void printOn (PrintWriter pw){
+      if (prev != null && stateId != prev.stateId){
+        pw.printf("----------------------------------- [%d]\n", prev.stateId + 1);
+      }
+      
+      pw.print(ti.getId());
+      pw.print(": ");
+      
+      pw.printf("%-4s ", opType.toString().toLowerCase());
+      pw.print(ei);
+      pw.print('.');
+      
+      if (insn != null){        
+        if (insn instanceof FieldInstruction){
+          FieldInstruction finsn = (FieldInstruction)insn;
+          
+          String fname = finsn.getFieldName();
+          pw.print(fname);
+          
+        } else if (insn instanceof InvokeInstruction){
+          InvokeInstruction call = (InvokeInstruction)insn;
+          
+          String mthName = call.getInvokedMethodName();
+          
+          pw.print( Types.getDequalifiedMethodSignature(mthName));
+        }
+      }
+      
+      pw.println();
+    }
+  }
+  
+  protected LogRecord log; // needs to be state restored
+  
+  //--- log options  
+  protected StringSetMatcher includeClasses, excludeClasses; // type name patterns
+  protected IntSet trackedRefs;
+  
+  protected boolean logFieldAccess;
+  protected boolean logCalls;
+
+    
+  
+  //--- internal stuff
+  
+  public ObjectTracker (Config conf, JPF jpf) {
+    includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.include"));
+    excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("ot.exclude", new String[] { "*" }));
+
+    trackedRefs = new SortedArrayIntSet();
+    
+    int[] refs = conf.getIntArray("ot.refs");
+    if (refs != null){
+      for (int i=0; i<refs.length; i++){
+        trackedRefs.add(refs[i]);
+      }
+    }
+    
+    logCalls = conf.getBoolean("ot.log_calls", true);
+    logFieldAccess = conf.getBoolean("ot.log_fields", true);
+    
+    registerListener(jpf);
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+    
+  protected void log (OpType opType, ElementInfo ei, ThreadInfo ti, Instruction insn){
+    log = new LogRecord( opType, ei, ti, insn,  log);
+  }
+  
+  
+  //--- Listener interface
+  
+  @Override
+  public void classLoaded (VM vm, ClassInfo ci){
+    if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)){
+      ci.addAttr(ATTR);
+    }
+  }
+  
+  @Override
+  public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei) {
+    ClassInfo ci = ei.getClassInfo();
+    int ref = ei.getObjectRef();
+    
+    if (ci.hasAttr(Attr.class) || trackedRefs.contains(ref)){
+      // it's new, we don't need to call getModifiable
+      ei.addObjectAttr(ATTR);
+      log( OpType.NEW, ei, ti, ti.getPC());
+    }
+  }
+  
+  @Override
+  public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei) {
+    if (ei.hasObjectAttr(Attr.class)){
+      log( OpType.FREE, ei, ti, ti.getPC());      
+    }
+  }
+
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+    
+    if (logCalls && executedInsn instanceof InstanceInvokeInstruction){      
+      if (nextInsn != executedInsn){ // otherwise we didn't enter
+        InstanceInvokeInstruction call = (InstanceInvokeInstruction)executedInsn;
+
+        int ref = call.getCalleeThis(ti);
+        ElementInfo ei = ti.getElementInfo(ref);
+        
+        if (ei.hasObjectAttr(Attr.class)) {
+          log( OpType.CALL, ei, ti, executedInsn);
+        }
+      }
+      
+    } else if (logFieldAccess && executedInsn instanceof InstanceFieldInstruction){
+      if (nextInsn != executedInsn){ // otherwise we didn't enter
+        InstanceFieldInstruction finsn = (InstanceFieldInstruction) executedInsn;
+
+        StackFrame frame = ti.getTopFrame();
+        int idx = finsn.getObjectSlot(frame);
+        int ref = frame.getSlot(idx);
+        ElementInfo ei = ti.getElementInfo(ref);
+        
+        if (ei.hasObjectAttr(Attr.class)) {
+          OpType op = (executedInsn instanceof PUTFIELD) ? OpType.PUT : OpType.GET;
+          log( op, ei, ti, executedInsn);
+        }
+      }
+    }
+  }
+
+  //--- state store/restore
+  
+  @Override
+  public Object getStateExtension () {
+    return log;
+  }
+
+  @Override
+  public void restore (Object stateExtension) {
+    log = (LogRecord)stateExtension;
+  }
+
+  @Override
+  public void registerListener (JPF jpf) {
+    StateExtensionListener<Number> sel = new StateExtensionListener(this);
+    jpf.addSearchListener(sel);
+  }
+
+  
+  //--- reporting
+  
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {    
+    if (log != null){ // otherwise we don't have anything to report
+      PrintWriter pw = publisher.getOut();
+      publisher.publishTopicStart("ObjectTracker " + publisher.getLastErrorId());
+      printLogOn(pw);
+    }
+  }
+
+  protected void printLogOn (PrintWriter pw){
+    // the log can be quite long so we can't use recursion (Java does not optimize tail recursion)
+    List<LogRecord> logRecs = new ArrayList<LogRecord>();
+    for (LogRecord lr = log; lr != null; lr = lr.prev){
+      logRecs.add(lr);
+    }
+    
+    Collections.reverse(logRecs);
+    
+    for (LogRecord lr : logRecs){
+      lr.printOn(pw);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/OverlappingMethodAnalyzer.java b/src/main/gov/nasa/jpf/listener/OverlappingMethodAnalyzer.java
new file mode 100644 (file)
index 0000000..66f5c4c
--- /dev/null
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.ArrayDeque;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * this is a specialized MethodAnalyzer that looks for overlapping method
+ * calls on the same object from different threads.
+ * 
+ * <2do> transition reporting does not work yet
+ */
+public class OverlappingMethodAnalyzer extends MethodAnalyzer {
+
+  public OverlappingMethodAnalyzer (Config config, JPF jpf){
+    super(config,jpf);
+  }
+
+  MethodOp getReturnOp (MethodOp op, boolean withinSameThread){
+    MethodInfo mi = op.mi;
+    int stateId = op.stateId;
+    int stackDepth = op.stackDepth;
+    ElementInfo ei = op.ei;
+    ThreadInfo ti = op.ti;
+
+    for (MethodOp o = op.p; o != null; o = o.p){
+      if (withinSameThread && o.ti != ti){
+        break;
+      }
+
+      if ((o.mi == mi) && (o.ti == ti) && (o.stackDepth == stackDepth) && (o.ei == ei)){
+        return o;
+      }
+    }
+
+    return null;
+  }
+
+  // check if there is an open exec from another thread for the same ElementInfo
+  boolean isOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    ElementInfo ei = op.ei;
+
+    for (Map.Entry<ThreadInfo, Deque<MethodOp>> e : openExecs.entrySet()) {
+      if (e.getKey() != ti) {
+        Deque<MethodOp> s = e.getValue();
+        for (Iterator<MethodOp> it = s.descendingIterator(); it.hasNext();) {
+          MethodOp o = it.next();
+          if (o.ei == ei) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+  // clean up (if necessary) - both RETURNS and exceptions
+  void cleanUpOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    int stackDepth = op.stackDepth;
+
+    Deque<MethodOp> stack = openExecs.get(ti);
+    if (stack != null && !stack.isEmpty()) {
+      for (MethodOp o = stack.peek(); o != null && o.stackDepth >= stackDepth; o = stack.peek()) {
+        stack.pop();
+      }
+    }
+  }
+
+  void addOpenExec (HashMap<ThreadInfo,Deque<MethodOp>> openExecs, MethodOp op){
+    ThreadInfo ti = op.ti;
+    Deque<MethodOp> stack = openExecs.get(ti);
+
+    if (stack == null){
+      stack = new ArrayDeque<MethodOp>();
+      stack.push(op);
+      openExecs.put(ti, stack);
+
+    } else {
+      stack.push(op);
+    }
+  }
+
+  @Override
+  void printOn (PrintWriter pw) {
+    MethodOp start = firstOp;
+
+    HashMap<ThreadInfo,Deque<MethodOp>> openExecs = new HashMap<ThreadInfo,Deque<MethodOp>>();
+
+    int lastStateId  = -1;
+    int lastTid = start.ti.getId();
+
+    for (MethodOp op = start; op != null; op = op.p) {
+
+      cleanUpOpenExec(openExecs, op);
+
+      if (op.isMethodEnter()) {  // EXEC or CALL_EXEC
+        MethodOp retOp = getReturnOp(op, true);
+        if (retOp != null) { // completed, skip
+          if (!isOpenExec(openExecs, op)) {
+            op = retOp;
+            lastStateId = op.stateId;
+            continue;
+          }
+        } else { // this is an open method exec, record it
+          addOpenExec(openExecs, op);
+        }
+      }
+
+      op = consolidateOp(op);
+      
+      if (showTransition) {
+        if (op.stateId != lastStateId) {
+          if (lastStateId >= 0){
+            pw.print("------------------------------------------ #");
+            pw.println(lastStateId);
+          }
+        }
+        lastStateId = op.stateId;
+        
+      } else {
+        int tid = op.ti.getId();
+        if (tid != lastTid) {
+          lastTid = tid;
+          pw.println("------------------------------------------");
+        }
+      }
+      
+      op.printOn(pw, this);
+      pw.println();
+    }
+  }
+
+  MethodOp consolidateOp (MethodOp op){
+    for (MethodOp o = op.p; o != null; o = o.p){
+      if (showTransition && (o.stateId != op.stateId)){
+        break;
+      }
+      if (o.isSameMethod(op)){
+        switch (o.type) {
+          case RETURN:
+            switch (op.type){
+              case CALL_EXECUTE:
+                op = o.clone(OpType.CALL_EXEC_RETURN); break;
+              case EXECUTE:
+                op = o.clone(OpType.EXEC_RETURN); break;
+            }
+            break;
+          case EXEC_RETURN:
+            switch (op.type){
+              case CALL:
+                op = o.clone(OpType.CALL_EXEC_RETURN); break;
+            }
+            break;
+          case CALL_EXECUTE:  // simple loop
+            switch (op.type){
+              case CALL_EXEC_RETURN:
+                op = o;
+            }
+            break;
+        }
+      } else {
+        break;
+      }
+    }
+
+    return op;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/PathOutputMonitor.java b/src/main/gov/nasa/jpf/listener/PathOutputMonitor.java
new file mode 100644 (file)
index 0000000..abd6252
--- /dev/null
@@ -0,0 +1,460 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.Transition;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.logging.Logger;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * listener that monitors path output, matching it against specifications
+ * supplied as text files. Per default, this uses simple line-by-line
+ * regular expression matching, also supporting prefixes by means of
+ * special ". . ." ellipsis patterns. Each file can contain a number of
+ * path output specs, separated by "~~~~~" lines.
+ * 
+ *  The major purpose of this listener is to verify JPF state spaces,
+ *  but it can also be used as a functional property
+ */
+public class PathOutputMonitor extends PropertyListenerAdapter {
+  static final String SEPARATOR = "~~~";
+  static final String ELLIPSIS = "...";
+  
+  static Logger log = JPF.getLogger("gov.nasa.jpf.listener.PathOutputMonitor");
+
+  public interface PathOutputSpec {
+    boolean add (String spec);
+    boolean matches (String[] output);
+    void printOn (PrintWriter pw);
+    boolean isEmpty();
+  }
+  
+  static class VerbatimOutputSpec implements PathOutputSpec {
+    ArrayList<String> patterns = new ArrayList<String>();
+
+    @Override
+         public boolean add (String spec) {
+      patterns.add(spec);
+      return true;
+    }
+    
+    @Override
+         public boolean matches (String[] output) {
+      if ((output != null) && (output.length > 0)) {
+        Iterator<String> it = patterns.iterator();
+        for (String line : output) {
+          if (it.hasNext()) {
+            String p = it.next();
+            if (!p.equals(line)){
+              return false;
+            }
+          } else {
+            return false;
+          }
+        }
+        
+        return !it.hasNext();
+        
+      } else {
+        return patterns.isEmpty();        
+      }
+    }
+    
+    // sometimes, duck typing would be nice..
+    
+    @Override
+         public void printOn (PrintWriter pw) {
+      for (String p : patterns) {
+        pw.println(p.toString());
+      }
+    }
+    
+    @Override
+    public boolean isEmpty(){
+      return patterns.isEmpty();
+    }
+  }
+  
+  // simple regular expression matchers (could be a more sophisticated parser)
+  static class RegexOutputSpec implements PathOutputSpec {  
+    ArrayList<Pattern> patterns = new ArrayList<Pattern>();
+    
+    @Override
+         public boolean add (String spec) {
+      try {
+        Pattern p = Pattern.compile(spec);
+        patterns.add(p);
+      } catch (PatternSyntaxException psx) {
+        return false;
+      }
+      
+      return true;
+    }
+    
+    @Override
+         public boolean matches (String[] output) {
+      
+      if ((output != null) && (output.length > 0)) {
+        Iterator<Pattern> it = patterns.iterator();
+        for (String line : output) {
+          if (it.hasNext()) {
+            Pattern p = it.next();
+
+            if (p.toString().equals(ELLIPSIS)) {
+              return true;
+            }
+
+            Matcher m = p.matcher(line);
+            if (!m.matches()) {
+              return false;
+            }
+          } else {
+            return false;
+          }
+        }
+
+        return (!it.hasNext() || it.next().toString().equals(ELLIPSIS));          
+        
+      } else { // no output
+        return patterns.isEmpty();
+      }
+    }
+    
+    @Override
+         public void printOn (PrintWriter pw) {
+      for (Pattern p : patterns) {
+        pw.println(p.toString());
+      }
+    }
+    
+    @Override
+    public boolean isEmpty(){
+      return patterns.isEmpty();
+    }
+  }
+
+  //---- our instance data
+  VM vm;
+  
+  //--- this is where we store the outputs (line-wise)
+  // <2do> not very space efficient
+  List<String[]> pathOutputs = new ArrayList<String[]>();
+  
+  //--- config options
+  Class<? extends PathOutputSpec> psClass; 
+  boolean printOutput;
+  boolean deferOutput;
+  List<PathOutputSpec> anySpecs, allSpecs, noneSpecs;
+
+  //--- keep track of property violations
+  String errorMsg;
+  List<PathOutputSpec> violatedSpecs;
+  String[] offendingOutput;
+    
+  
+  public PathOutputMonitor (Config config, JPF jpf) {
+    vm = jpf.getVM();
+    vm.storePathOutput();
+    
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+    
+    printOutput = config.getBoolean("pom.print_output", true);
+    deferOutput = config.getBoolean("pom.defer_output", true);
+  
+    psClass = config.getClass("pom.output_spec.class", PathOutputSpec.class);
+
+    if (psClass == null) {
+      psClass = RegexOutputSpec.class;
+    }
+    
+    anySpecs = loadSpecs(config, "pom.any");
+    allSpecs = loadSpecs(config, "pom.all");
+    noneSpecs = loadSpecs(config, "pom.none");
+    
+    violatedSpecs = new ArrayList<PathOutputSpec>();
+  }
+
+  
+  List<PathOutputSpec> loadSpecs(Config conf, String key) {
+    String spec = conf.getString(key);
+    if (spec != null) {
+      if (spec.startsWith("\"")){ // spec is in-situ content (convenience method for test classes)
+        spec = spec.substring(1, spec.length()-1);
+        BufferedReader br = new BufferedReader( new StringReader(spec));
+        return readPathPatterns(br);
+        
+      } else { // spec is pathname of output sepc file
+        File file = new File(spec);
+        try {
+          BufferedReader br = new BufferedReader( new FileReader(file));
+          return readPathPatterns(br);
+          
+        } catch (FileNotFoundException fnfx){
+          log.warning("pattern file not found: " + spec);
+        }        
+      }
+    }
+    
+    return null;
+  }
+
+  PathOutputSpec createPathOutputSpec() {
+    try {
+      return psClass.newInstance();
+    } catch (Throwable t) {
+      log.severe("cannot instantiate PathoutputSpec class: " + t.getMessage());
+      return null;
+    }
+  }
+  
+  
+  
+  List<PathOutputSpec> readPathPatterns (BufferedReader br){  
+    ArrayList<PathOutputSpec> results = new ArrayList<PathOutputSpec>();
+    
+    // prefix pattern goes into file
+    
+    try {
+      PathOutputSpec ps = createPathOutputSpec();
+      
+      int lineno = 0;
+      for (String line=br.readLine(); true; line = br.readLine()) {
+        if (line == null) {
+          results.add(ps);
+          break;
+        }
+        lineno++;
+        
+        if (line.startsWith(SEPARATOR)) {
+          results.add(ps);
+          ps = createPathOutputSpec();
+        } else {
+          ps.add(line);
+        }
+      }
+            
+      br.close();
+    } catch (FileNotFoundException fnfx) {
+      return null;
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+      
+    return results;
+  }
+
+  String[] getLines (String output) {
+    ArrayList<String> lines = new ArrayList<String>();
+    BufferedReader br = new BufferedReader(new StringReader(output));
+    try {
+      for (String line = br.readLine(); line != null; line = br.readLine()) {
+        lines.add(line);
+      }
+    } catch (IOException iox) {
+      iox.printStackTrace();
+    }
+    
+    return lines.toArray(new String[lines.size()]);
+  }
+
+  boolean matchesAny (List<PathOutputSpec> outputSpecs, String[] lines) {
+    for (PathOutputSpec ps : outputSpecs) {
+      if (ps.matches(lines)) {
+        return true;
+      }
+    }
+
+    errorMsg = "unmatched output";
+    offendingOutput = lines;
+    
+    return false;
+  }
+    
+  boolean matchesNone (List<PathOutputSpec> outputSpecs, String[] lines) {
+    for (PathOutputSpec ps : outputSpecs) {
+      if (ps.matches(lines)) {
+        errorMsg = "illegal output (matching inverse spec)";
+        offendingOutput = lines;
+        violatedSpecs.add(ps);
+        
+        return false;
+      }
+    }
+        
+    return true;
+  } 
+  
+  boolean matchesAll (List<PathOutputSpec> outputSpecs, List<String[]> outputs) {
+    HashSet<PathOutputSpec> unmatched = new HashSet<PathOutputSpec>();
+    unmatched.addAll(outputSpecs);
+    
+    for (String[] lines : outputs) {
+      for (PathOutputSpec ps : outputSpecs) {
+        if (ps.matches(lines)) {
+           unmatched.remove(ps);
+          if (unmatched.isEmpty()) {
+            return true;
+          }
+        }
+      }
+    }
+
+    errorMsg = "unmatched specs (" + unmatched.size() + ')';
+    for (PathOutputSpec ps : unmatched) {
+      violatedSpecs.add(ps);
+    }
+    
+    return false;
+  }
+
+  
+  //----------- the listener interface
+  
+  @Override
+  public boolean check(Search search, VM vm) {
+    return (errorMsg == null);
+  }
+
+  @Override
+  public String getErrorMessage () {
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+
+    pw.println(errorMsg);
+    
+    if (offendingOutput != null) {
+      pw.println("offending output:");
+      for (String line : offendingOutput) {
+        pw.println(line);
+      }
+    }
+    
+    if (!violatedSpecs.isEmpty()) {
+      pw.println("violated specs:");
+      for (PathOutputSpec ps : violatedSpecs) {
+        ps.printOn(pw);
+        pw.println(SEPARATOR);
+      }
+      
+    }
+    
+    String s = sw.toString();
+    pw.close();
+    
+    return s;
+  }
+  
+  @Override
+  public void reset () {
+    errorMsg = null;
+    violatedSpecs.clear();
+    offendingOutput = null;
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    if (search.isEndState()) {
+      
+      Path path = vm.getPath();
+      if (path.hasOutput()) {
+        StringBuilder sb = null;
+        
+        if (deferOutput || (noneSpecs != null)) {
+          sb = new StringBuilder();
+          for (Transition t : path) {
+            String s = t.getOutput();
+            if (s != null){
+              sb.append(s);
+            }
+          }
+        }
+
+        String[] lines = getLines(sb.toString());
+        
+        if (deferOutput) {
+          pathOutputs.add(lines);
+          
+        } else if (printOutput){
+          for (Transition t : path) {
+            String s = t.getOutput();
+            if (s != null){
+              System.out.print(s); // <2do> don't use System.out
+            }            
+          }          
+        }
+      
+        // check safety properties
+        if (noneSpecs != null && !matchesNone(noneSpecs, lines)) {
+           log.warning("pom.none violated");
+        }
+        if (anySpecs != null && !matchesAny(anySpecs, lines)) {
+          log.warning("pom.any violated");
+       }        
+        
+      }
+    }
+  }
+  
+  @Override
+  public void searchFinished (Search search) {
+    if (allSpecs != null && !matchesAll(allSpecs, pathOutputs)) {
+      log.warning("pom.all violated");
+      search.error(this);
+    }
+  }
+  
+  @Override
+  public void publishFinished (Publisher publisher) {
+    
+    if (printOutput) {
+      PrintWriter pw = publisher.getOut();
+      publisher.publishTopicStart("path outputs");
+      for (String[] output : pathOutputs) {
+        for (String line : output) {
+          pw.println(line);
+        }
+        pw.println(SEPARATOR);
+      }
+    }    
+  }
+    
+}
diff --git a/src/main/gov/nasa/jpf/listener/Perturbator.java b/src/main/gov/nasa/jpf/listener/Perturbator.java
new file mode 100644 (file)
index 0000000..0eaac2b
--- /dev/null
@@ -0,0 +1,457 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.GETFIELD;
+import gov.nasa.jpf.jvm.bytecode.JVMInstanceFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.perturb.OperandPerturbator;
+import gov.nasa.jpf.util.FieldSpec;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.MethodSpec;
+import gov.nasa.jpf.util.SourceRef;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * listener that perturbs GETFIELD/GETSTATIC and JVMInvokeInstruction results
+ *
+ * NOTE - this listener initializes in two steps: (1) during listener construction
+ * it builds a list of classes it has to monitor, and (2) during class load
+ * time it further analyzes classes from this list to get the actual target
+ * objects (FieldInfos and MethodInfos) so that instruction monitoring is
+ * efficient enough.
+ *
+ * This means the listener always has to be instantiated BEFORE the respective
+ * target classes get loaded.
+ *
+ * configuration example:
+ *
+ *   # field getter example
+ *   perturb.fields = altitude,...
+ *   perturb.altitude.field = x.y.MyClass.alt
+ *   perturb.altitude.class = .perturb.IntOverUnder
+ *   perturb.altitude.location = MyClass.java:42
+ *   perturb.altitude.delta = 1
+ *
+ *   # method return value example
+ *   perturb.returns = velocity,...
+ *   perturb.velocity.method = x.y.MyClass.computeVelocity()
+ *   perturb.velocity.class = .perturb.IntOverUnder
+ *   perturb.velocity.delta = 50
+ *   
+ *   # method parameter perturbation example
+ *   perturb.params = foo, ...
+ *   perturb.foo.method = x.y.MyClass.send(int, float, boolean)
+ *   perturb.foo.location = MyClass.java:42
+ *   perturb.class = .perturb.dataAbstractor
+ *   
+ */
+
+public class Perturbator extends ListenerAdapter {
+
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.Perturbator");
+
+  public static class Perturbation {
+    SourceRef sref;    // location where field access should be perturbed
+    Class<? extends ChoiceGenerator<?>> cgType; // needs to be compatible with field type
+    OperandPerturbator perturbator;
+
+    Perturbation (OperandPerturbator perturbator, String loc){
+      this.perturbator = perturbator;
+
+      if (loc != null){
+        sref = new SourceRef(loc);
+      }
+    }
+  }
+
+  public static class FieldPerturbation extends Perturbation {
+    FieldSpec fieldSpec;
+
+    FieldPerturbation (FieldSpec fieldSpec, OperandPerturbator perturbator, String loc){
+      super(perturbator, loc);
+
+      this.fieldSpec = fieldSpec;
+    }
+  }
+
+  public static class ReturnPerturbation extends Perturbation {
+    MethodSpec mthSpec;
+
+    ReturnPerturbation (MethodSpec mthSpec, OperandPerturbator perturbator, String loc){
+      super(perturbator, loc);
+
+      this.mthSpec = mthSpec;
+    }
+  }
+  
+  public static class ParamsPerturbation extends Perturbation {
+       public MethodSpec mthSpec;
+       
+       ParamsPerturbation (MethodSpec mthSpec, OperandPerturbator perturbator, String loc) {
+               super(perturbator, loc);
+               
+               this.mthSpec = mthSpec;
+       }
+  }
+  
+  protected static Class<?>[] argTypes = { Config.class, String.class };
+
+  protected List<FieldPerturbation> fieldWatchList = new ArrayList<FieldPerturbation>();
+  protected HashMap<FieldInfo,FieldPerturbation> perturbedFields = new HashMap<FieldInfo,FieldPerturbation>();
+
+  protected List<ReturnPerturbation> returnWatchList = new ArrayList<ReturnPerturbation>();
+  protected HashMap<MethodInfo,ReturnPerturbation> perturbedReturns = new HashMap<MethodInfo,ReturnPerturbation>();
+  
+  protected List<ParamsPerturbation> paramsWatchList = new ArrayList<ParamsPerturbation>();
+  protected HashMap<MethodInfo, ParamsPerturbation> perturbedParams = new HashMap<MethodInfo, ParamsPerturbation>();
+
+  protected StackFrame savedFrame;
+  
+  public Perturbator (Config conf){
+
+    // in the ctor we only find out which classname patterns we have to watch
+    // for, and store them in a list (together with their partially initialized
+    // Perturbation instances) that is to be checked upon classLoaded notifications
+
+    // get the configured field perturbators
+    String[] fieldIds = conf.getCompactTrimmedStringArray("perturb.fields");
+    for (String id : fieldIds){
+      addToFieldWatchList(conf, id);
+    }
+
+    String[] returnIds = conf.getCompactTrimmedStringArray("perturb.returns");
+    for (String id : returnIds){
+      addToReturnWatchList(conf, id);
+    }
+    
+    String[] paramsIds = conf.getCompactTrimmedStringArray("perturb.params");
+    for (String id: paramsIds) {
+       addToParamsWatchList(conf, id);
+    }
+  }
+  
+  public boolean isMethodWatched(Instruction insn, MethodInfo mi) {
+    ParamsPerturbation e = perturbedParams.get(mi);
+    if (e != null && isRelevantCallLocation(insn, e)){
+      return true;
+    }
+    return false;
+  }
+  
+  protected void addToFieldWatchList (Config conf, String id){
+    String keyPrefix = "perturb." + id;
+
+    String fs = conf.getString(keyPrefix + ".field");
+    if (fs != null) {
+      FieldSpec fieldSpec = FieldSpec.createFieldSpec(fs);
+      if (fieldSpec != null){
+        Object[] args = {conf, keyPrefix};
+        OperandPerturbator perturbator = conf.getInstance(keyPrefix + ".class", OperandPerturbator.class, argTypes, args);
+        if (perturbator != null) {
+          String loc = conf.getString(keyPrefix + ".location");
+          FieldPerturbation p = new FieldPerturbation(fieldSpec, perturbator, loc);
+          fieldWatchList.add(p);
+        } else {
+          log.warning("invalid perturbator spec for ", keyPrefix);
+        }
+      } else {
+        log.warning("malformed field specification for ", keyPrefix);
+      }
+
+    } else {
+      log.warning("missing field specification for ", keyPrefix);
+    }
+  }
+
+  protected void addToReturnWatchList (Config conf, String id){
+    String keyPrefix = "perturb." + id;
+
+    String ms = conf.getString(keyPrefix + ".method");
+    if (ms != null) {
+      MethodSpec mthSpec = MethodSpec.createMethodSpec(ms);
+      if (mthSpec != null) {
+        Object[] args = {conf, keyPrefix};
+        OperandPerturbator perturbator = conf.getInstance(keyPrefix + ".class", OperandPerturbator.class, argTypes, args);
+        if (perturbator != null) {
+          String loc = conf.getString(keyPrefix + ".location");
+          ReturnPerturbation p = new ReturnPerturbation(mthSpec, perturbator, loc);
+          returnWatchList.add(p);
+        } else {
+          log.warning("invalid perturbator spec for ", keyPrefix);
+        }
+
+      } else {
+        log.warning("malformed method specification for ", keyPrefix);
+      }
+
+    } else {
+      log.warning("missing method specification for ", keyPrefix);
+    }
+  }
+
+  protected void addToParamsWatchList (Config conf, String id){
+    String keyPrefix = "perturb." + id;
+
+    String ms = conf.getString(keyPrefix + ".method");
+    if (ms != null) {
+      MethodSpec mthSpec = MethodSpec.createMethodSpec(ms);
+      if (mthSpec != null) {
+        Object[] args = {conf, keyPrefix};
+        OperandPerturbator perturbator = conf.getInstance(keyPrefix + ".class", OperandPerturbator.class, argTypes, args);        
+        if (perturbator != null) {
+          String loc = conf.getString(keyPrefix + ".location");
+          ParamsPerturbation p = new ParamsPerturbation(mthSpec, perturbator, loc);
+          paramsWatchList.add(p);
+        } else {
+          log.warning("invalid perturbator spec for ", keyPrefix);
+        }
+
+      } else {
+        log.warning("malformed method specification for ", keyPrefix);
+      }
+    } else {
+      log.warning("missing method specification for ", keyPrefix);
+    }
+  }
+
+  @Override
+  public void classLoaded (VM vm, ClassInfo loadedClass){
+    // this one takes the watchlists, finds out if the loaded class matches
+    // any of the watch entries, and in case it does fully initializes
+    // the corresponding Perturbation object with the target construct
+    // (MethodInfo, FieldInfo) we use to identify relevant ops during
+    // instruction execution notifications
+
+    String clsName = loadedClass.getName();
+
+    for (FieldPerturbation p : fieldWatchList){
+      FieldSpec fs = p.fieldSpec;
+      if (fs.isMatchingType(loadedClass)){
+        addFieldPerturbations( p, loadedClass, loadedClass.getDeclaredInstanceFields());
+        addFieldPerturbations( p, loadedClass, loadedClass.getDeclaredStaticFields());
+      }
+    }
+
+    for (ReturnPerturbation p : returnWatchList){
+      MethodSpec ms = p.mthSpec;
+      if (ms.isMatchingType(loadedClass)){
+        for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()){
+          if (ms.matches(mi)){
+            Class<? extends ChoiceGenerator<?>> returnCGType = mi.getReturnChoiceGeneratorType();
+            Class<? extends ChoiceGenerator<?>> perturbatorCGType = p.perturbator.getChoiceGeneratorType();
+            if (returnCGType.isAssignableFrom(perturbatorCGType)){
+              p.cgType = returnCGType;
+              perturbedReturns.put(mi, p);
+            } else {
+              log.warning("method " + mi + " not compatible with perturbator choice type " + perturbatorCGType.getName());
+            }
+          }
+        }
+      }
+    }
+
+    for (ParamsPerturbation p : paramsWatchList){
+      MethodSpec ms = p.mthSpec;
+      if (ms.isMatchingType(loadedClass)){
+        for (MethodInfo mi : loadedClass.getDeclaredMethodInfos()){
+          if (ms.matches(mi)){
+               // We simply associate the method with the parameters perturbator
+            Class<? extends ChoiceGenerator<?>> perturbatorCGType = p.perturbator.getChoiceGeneratorType();
+            p.cgType = perturbatorCGType;
+               perturbedParams.put(mi, p);
+          }
+        }
+      }
+    }
+  }
+
+  protected void addFieldPerturbations (FieldPerturbation p, ClassInfo ci, FieldInfo[] fieldInfos){
+    for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
+      if (p.fieldSpec.matches(fi)) {
+        Class<? extends ChoiceGenerator<?>> fieldCGType = fi.getChoiceGeneratorType();
+        Class<? extends ChoiceGenerator<?>> perturbatorCGType = p.perturbator.getChoiceGeneratorType();
+        if (fieldCGType.isAssignableFrom(perturbatorCGType)) {
+          p.cgType = fieldCGType;
+          perturbedFields.put(fi, p);
+        } else {
+          log.warning("field " + fi + " not compatible with perturbator choice type " + perturbatorCGType.getName());
+        }
+      }
+    }
+  }
+
+  protected boolean isRelevantCallLocation (ThreadInfo ti, Perturbation p){
+    if (p.sref == null){
+      // no caller location specified -> all calls relevant
+      return true;
+    } else {
+      StackFrame caller = ti.getCallerStackFrame();
+      if (caller != null) {
+        Instruction invokeInsn = caller.getPC();
+        return p.sref.equals(invokeInsn.getFilePos());
+      } else {
+        return false;
+      }
+    }
+  }
+  
+  protected boolean isRelevantCallLocation (Instruction invokeInsn, Perturbation p) {
+       // For parameter perturbation, we are about to enter a method
+       // and hence can directly use the invoke instruction to get the file
+       // location of the call
+       if (p.sref == null)
+               return true;
+       else
+               return p.sref.equals(invokeInsn.getFilePos());
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+    
+    if (insnToExecute instanceof GETFIELD){
+      FieldInfo fi = ((JVMInstanceFieldInstruction)insnToExecute).getFieldInfo();
+      FieldPerturbation e = perturbedFields.get(fi);
+
+      if (e != null) {  // managed field
+        if (isMatchingInstructionLocation(e,insnToExecute)) {
+          if (!ti.isFirstStepInsn()){
+            // save the current stackframe so that we can restore it before
+            // we re-enter
+            savedFrame = ti.getTopFrame().clone();
+          }
+        }
+      }
+
+    } else if (insnToExecute instanceof JVMReturnInstruction){
+      MethodInfo mi = insnToExecute.getMethodInfo();
+      ReturnPerturbation e = perturbedReturns.get(mi);
+
+      if (e != null && isRelevantCallLocation(ti, e)){
+        SystemState ss = vm.getSystemState();
+
+        if (!ti.isFirstStepInsn()){
+          // first time, create & set CG but DO NOT enter the insn since it would
+          // pop the callee stackframe and modify the caller stackframe
+          // note that we don't need to enter in order to get the perturbation base
+          // value because its already on the operand stack
+          ChoiceGenerator<?> cg = e.perturbator.createChoiceGenerator("perturbReturn", ti.getTopFrame(), new Integer(0));
+          if (ss.setNextChoiceGenerator(cg)){
+            ti.skipInstruction(insnToExecute);
+          }
+        } else {
+          // re-executing, modify the operand stack top and enter
+          ChoiceGenerator<?> cg = ss.getCurrentChoiceGenerator("perturbReturn", e.cgType);
+          if (cg != null) {
+            e.perturbator.perturb(cg, ti.getTopFrame());
+          }
+        }
+      }
+    } else if (insnToExecute instanceof JVMInvokeInstruction) {
+       // first get the method info object corresponding to the invoked method
+       // We can't use getMethodInfo as the method returned may not be the actual
+       // method invoked, but rather its caller
+       MethodInfo mi = ((JVMInvokeInstruction) insnToExecute).getInvokedMethod();
+       ParamsPerturbation e = perturbedParams.get(mi);
+       
+      if (e != null && isRelevantCallLocation(insnToExecute, e)){
+        SystemState ss = vm.getSystemState();
+
+        if (!ti.isFirstStepInsn()) {
+               // first time, create and set CG and skip instruction as we want the instruction
+               // to be executed with the parameter choices we like instead of the ones that
+               // were passed in
+          ChoiceGenerator<?> cg = e.perturbator.createChoiceGenerator(mi.getFullName(), ti.getTopFrame(), mi);
+          // check if the cg returned is null. If it is then we don't want to enter this
+          // method as we are done exploring it
+          if (cg != null) {
+            log.info("--- Creating choice generator: " + mi.getFullName() + " for thread: " + ti);
+            if (ss.setNextChoiceGenerator(cg)) {
+              ti.skipInstruction(insnToExecute);
+            }
+          }
+        } else {
+          // re-executing, modify the operands on stack and enter
+          ChoiceGenerator<?> cg = ss.getChoiceGenerator(mi.getFullName());
+          if (cg != null) {
+            log.info("--- Using choice generator: " + mi.getFullName() + " in thread: " + ti);
+            e.perturbator.perturb(cg, ti.getTopFrame());
+          }
+        }
+      }
+    }
+  }
+
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    
+    if (executedInsn instanceof GETFIELD){
+      FieldInfo fi = ((JVMInstanceFieldInstruction)executedInsn).getFieldInfo();
+      FieldPerturbation p = perturbedFields.get(fi);
+      if (p != null){
+        if (isMatchingInstructionLocation(p, executedInsn)) {  // none or managed filePos
+          StackFrame frame = ti.getTopFrame();
+          SystemState ss = vm.getSystemState();
+
+          if (ti.isFirstStepInsn()) { // retrieve value from CG and replace it on operand stack
+            ChoiceGenerator<?> cg = ss.getCurrentChoiceGenerator( "perturbGetField", p.cgType);
+            if (cg != null) {
+              p.perturbator.perturb(cg, frame);
+            } else {
+              log.warning("wrong choice generator type ", cg);
+            }
+
+          } else { // first time around, create&set the CG and reexecute
+            ChoiceGenerator<?> cg = p.perturbator.createChoiceGenerator( "perturbGetField", frame, new Integer(0));
+            if (ss.setNextChoiceGenerator(cg)){
+              assert savedFrame != null;
+              // we could more efficiently restore the stackframe
+              // to pre-exec state from last 'this' or classobject ref, but then
+              // we have to deal with different field value sizes
+              ti.setTopFrame(savedFrame);
+              ti.setNextPC(executedInsn); // reexecute
+
+              savedFrame = null;
+            }
+          }
+        }
+      }
+    } 
+  }
+  
+  protected boolean isMatchingInstructionLocation (Perturbation p, Instruction insn){
+    return p.sref == null || p.sref.equals(insn.getFilePos());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/PreciseRaceDetector.java b/src/main/gov/nasa/jpf/listener/PreciseRaceDetector.java
new file mode 100644 (file)
index 0000000..084dea5
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.PropertyListenerAdapter;
+import gov.nasa.jpf.vm.bytecode.ArrayElementInstruction;
+import gov.nasa.jpf.vm.bytecode.FieldInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.bytecode.ReadOrWriteInstruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * This is a Race Detection Algorithm that is precise in its calculation of races, i.e. no false warnings.
+ * It exploits the fact that every thread choice selection point could be due to a possible race. It just runs
+ * through all the thread choices and checks whether there are more than one thread trying to read & write to the
+ * same field of an object.
+ *
+ * Current limitation is that it is only sound, i.e. will not miss a race, if the sync-detection is switched off
+ * during model checking. This is due to the fact that the sync-detection guesses that an acess is lock-protected
+ * when it in reality might not be.
+ *
+ * The listener also checks races for array elements, but in order to do so you have to set
+ * "cg.threads.break_arrays=true" (note that it is false by default because this can cause serious state
+ * explosion)
+ *
+ * This algorithm came out of a discussion with Franck van Breugel and Sergey Kulikov from the University of York.
+ * All credits for it goes to Franck and Sergey, all the bugs are mine.
+ *
+ * NOTE - the PreciseRaceDetector is machine type agnostic
+ *
+ * Author: Willem Visser
+ *
+ */
+
+public class PreciseRaceDetector extends PropertyListenerAdapter {
+
+  static class Race {
+    Race prev;   // linked list
+
+    ThreadInfo ti1, ti2;
+    ReadOrWriteInstruction insn1, insn2;
+    ElementInfo ei;
+    boolean isRead1, isRead2;
+
+    boolean isRace() {
+      return insn2 != null && ti1 != null && ti2 != null && ( ! ti1.equals(ti2) );
+    }
+
+    void printOn(PrintWriter pw){
+      pw.print("  ");
+      pw.print( ti1.getName());
+      pw.print(" at ");
+      pw.println(insn1.getSourceLocation());
+      String line = insn1.getSourceLine();
+      if (line != null){
+        pw.print("\t\t\"" + line.trim());
+      }
+      pw.print("\"  ");
+      pw.print( insn1.isRead() ? "READ:  " : "WRITE: ");
+      pw.println(insn1);
+
+      if (insn2 != null){
+        pw.print("  ");
+        pw.print(ti2.getName());
+        pw.print(" at ");
+        pw.println(insn2.getSourceLocation());
+        line = insn2.getSourceLine();
+        if (line != null){
+          pw.print("\t\t\"" + line.trim());
+        }
+        pw.print("\"  ");
+        pw.print( insn2.isRead() ? "READ:  " : "WRITE: ");
+        pw.println(insn2);
+      }
+    }
+  }
+
+  static class FieldRace extends Race {
+    FieldInfo   fi;
+
+    static Race check (Race prev, ThreadInfo ti,  ReadOrWriteInstruction insn, ElementInfo ei, FieldInfo fi){
+      for (Race r = prev; r != null; r = r.prev){
+        if (r instanceof FieldRace){
+          FieldRace fr = (FieldRace)r;
+          if (fr.ei == ei && fr.fi == fi){
+            
+            if (!((FieldInstruction)fr.insn1).isRead() || !((FieldInstruction)insn).isRead()){
+              fr.ti2 = ti;
+              fr.insn2 = insn;
+              return fr;
+            }
+          }
+        }
+      }
+
+      FieldRace fr = new FieldRace();
+      fr.ei = ei;
+      fr.ti1 = ti;
+      fr.insn1 = insn;
+      fr.fi = fi;
+      fr.prev = prev;
+      return fr;
+    }
+
+    @Override
+       void printOn(PrintWriter pw){
+      pw.print("race for field ");
+      pw.print(ei);
+      pw.print('.');
+      pw.println(fi.getName());
+
+      super.printOn(pw);
+    }
+  }
+
+  static class ArrayElementRace extends Race {
+    int idx;
+
+    static Race check (Race prev, ThreadInfo ti, ReadOrWriteInstruction insn, ElementInfo ei, int idx){
+      for (Race r = prev; r != null; r = r.prev){
+        if (r instanceof ArrayElementRace){
+          ArrayElementRace ar = (ArrayElementRace)r;
+          if (ar.ei == ei && ar.idx == idx){
+            if (!((ArrayElementInstruction)ar.insn1).isRead() || !((ArrayElementInstruction)insn).isRead()){
+              ar.ti2 = ti;
+              ar.insn2 = insn;
+              return ar;
+            }
+          }
+        }
+      }
+
+      ArrayElementRace ar = new ArrayElementRace();
+      ar.ei = ei;
+      ar.ti1 = ti;
+      ar.insn1 = insn;
+      ar.idx = idx;
+      ar.prev = prev;
+      return ar;
+    }
+
+    @Override
+       void printOn(PrintWriter pw){
+      pw.print("race for array element ");
+      pw.print(ei);
+      pw.print('[');
+      pw.print(idx);
+      pw.println(']');
+
+      super.printOn(pw);
+    }
+  }
+
+  // this is where we store if we detect one
+  protected Race race;
+
+
+  // our matchers to determine which code we have to check
+  protected StringSetMatcher includes = null; //  means all
+  protected StringSetMatcher excludes = null; //  means none
+
+
+  public PreciseRaceDetector (Config conf) {
+    includes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.include"));
+    excludes = StringSetMatcher.getNonEmpty(conf.getStringArray("race.exclude"));
+  }
+  
+  @Override
+  public boolean check(Search search, VM vm) {
+    return (race == null);
+  }
+
+  @Override
+  public void reset() {
+    race = null;
+  }
+
+
+  @Override
+  public String getErrorMessage () {
+    if (race != null){
+      StringWriter sw = new StringWriter();
+      PrintWriter pw = new PrintWriter(sw);
+      race.printOn(pw);
+      pw.flush();
+      return sw.toString();
+      
+    } else {
+      return null;
+    }
+  }
+
+  protected boolean checkRace (ThreadInfo[] threads){
+    Race candidate = null;
+
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      Instruction insn = ti.getPC();
+      MethodInfo mi = insn.getMethodInfo();
+
+      if (StringSetMatcher.isMatch(mi.getBaseName(), includes, excludes)) {
+        if (insn instanceof FieldInstruction) {
+          FieldInstruction finsn = (FieldInstruction) insn;
+          FieldInfo fi = finsn.getFieldInfo();
+          ElementInfo ei = finsn.peekElementInfo(ti);
+
+          candidate = FieldRace.check(candidate, ti, finsn, ei, fi);
+
+        } else if (insn instanceof ArrayElementInstruction) {
+          ArrayElementInstruction ainsn = (ArrayElementInstruction) insn;
+          ElementInfo ei = ainsn.peekArrayElementInfo(ti);
+
+          // these insns have been through their top half since they created CGs, but they haven't
+          // removed the operands from the stack
+          int idx = ainsn.peekIndex(ti);
+
+          candidate = ArrayElementRace.check(candidate, ti, ainsn, ei, idx);
+        }
+      }
+
+      if (candidate != null && candidate.isRace()){
+        race = candidate;
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  //----------- our VMListener interface
+
+  // All we rely on here is that the scheduler breaks transitions at all
+  // insns that could be races. We then just have to look at all currently
+  // executed insns and don't rely on any past-exec info, PROVIDED that we only
+  // use execution parameters (index or reference values) that are retrieved
+  // from the operand stack, and not cached in the insn from a previous exec
+  // (all the insns we look at are pre-exec, i.e. don't have their caches
+  // updated yet)
+  @Override
+  public void choiceGeneratorSet(VM vm, ChoiceGenerator<?> newCG) {
+
+    if (newCG instanceof ThreadChoiceFromSet) {
+      ThreadInfo[] threads = ((ThreadChoiceFromSet)newCG).getAllThreadChoices();
+      checkRace(threads);
+    }
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute) {
+    if (race != null) {
+      // we're done, report as quickly as possible
+      //ti.skipInstruction();
+      ti.breakTransition("dataRace");
+    }
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/listener/ReferenceLocator.java b/src/main/gov/nasa/jpf/listener/ReferenceLocator.java
new file mode 100644 (file)
index 0000000..d32f8bc
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInstanceFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.InstanceInvocation;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * tiny utility listener that can be used to find out where a certain
+ * object (specified by reference) gets allocated or accessed (call or field),
+ * and when it gets gc'ed
+ */
+public class ReferenceLocator extends ListenerAdapter {
+  
+  PrintWriter pw;
+  int[] createRefs;
+  int[] releaseRefs;
+  int[] useRefs;
+  
+  public ReferenceLocator (Config conf){
+    createRefs = sort( conf.getIntArray("refloc.create"));
+    releaseRefs = sort( conf.getIntArray("refloc.release"));
+    useRefs = sort( conf.getIntArray("refloc.use"));
+    
+    // <2do> we might want to configure output destination
+    pw = new PrintWriter(System.out, true);
+  }
+  
+  protected int[] sort(int[] a){
+    if (a != null){
+      Arrays.sort(a);
+    }
+    return a;
+  }
+  
+  protected void printLocation(String msg, ThreadInfo ti){
+    pw.println(msg);
+    for (StackFrame frame : ti) {
+      pw.print("\tat ");
+      pw.println(frame.getStackTraceInfo());
+    }
+
+    pw.println();
+  }
+  
+  @Override
+  public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei){
+    int ref = ei.getObjectRef();
+    
+    if (createRefs != null && Arrays.binarySearch(createRefs, ref) >= 0){    
+      printLocation("[ReferenceLocator] object " + ei + " created at:", ti);
+    } 
+  }
+  
+  @Override
+  public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei){
+    int ref = ei.getObjectRef();
+    
+    if (releaseRefs != null && Arrays.binarySearch(releaseRefs, ref) >= 0){
+      pw.println("[ReferenceLocator] object " + ei + " released");
+    }
+  }
+  
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+    
+    if (useRefs != null){
+      if (executedInsn instanceof InstanceInvocation) {
+        int ref = ((InstanceInvocation)executedInsn).getCalleeThis(ti);
+        if (Arrays.binarySearch(useRefs, ref) >= 0){
+          printLocation("[ReferenceLocator] call on object " + ti.getElementInfo(ref) + " at:", ti);
+        }
+      } else if (executedInsn instanceof JVMInstanceFieldInstruction){
+        int ref = ((JVMInstanceFieldInstruction)executedInsn).getLastThis();
+        if (Arrays.binarySearch(useRefs, ref) >= 0){
+          printLocation("[ReferenceLocator] field access of " + ti.getElementInfo(ref) + " at:", ti);          
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/SearchStats.java b/src/main/gov/nasa/jpf/listener/SearchStats.java
new file mode 100644 (file)
index 0000000..2a97b4b
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.heuristic.HeuristicSearch;
+
+import java.io.PrintStream;
+
+/**
+ * An alternative to SearchMonitor that just reports statistics at the end.
+ */
+public class SearchStats extends ListenerAdapter {
+  PrintStream out = System.out;
+  
+  long time;
+  long startTime;
+  long startFreeMemory;
+  
+  int searchLevel=0;
+  int maxSearchLevel=0;
+  
+  int newStates;
+  int endStates;
+  int backtracks;
+  int revisitedStates;
+  int processedStates;
+  int restoredStates;
+  
+  int steps;
+
+  long maxMemory;
+  long totalMemory;
+  long freeMemory;
+    
+  boolean isHeuristic = false;
+  int queueSize = 0;
+  
+  int currentHeapCount = 0;
+  int maxHeapCount = 0;
+  
+  /*
+   * SearchListener interface
+   */  
+  @Override
+  public void stateAdvanced(Search search) {
+    steps += search.getTransition().getStepCount();
+   
+    if (isHeuristic)
+       queueSize = ((HeuristicSearch)(search)).getQueueSize();
+    
+    if (search.isNewState()) {
+      searchLevel = search.getDepth();
+      if (searchLevel > maxSearchLevel)
+       maxSearchLevel = searchLevel;
+      
+      newStates++; 
+      
+      currentHeapCount = search.getVM().getHeap().size();
+      
+      if (currentHeapCount > maxHeapCount)
+        maxHeapCount = currentHeapCount;
+      
+      if (search.isEndState()) {
+        endStates++;
+      }
+    } else {
+      revisitedStates++;
+    }
+  }
+
+  @Override
+  public void stateProcessed(Search search) {
+    processedStates++;
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    searchLevel = search.getDepth();
+    backtracks++;
+  }
+
+  @Override
+  public void stateRestored(Search search) {
+    searchLevel = search.getDepth();
+    restoredStates++;
+  }
+
+  @Override
+  public void propertyViolated(Search search) {
+  }
+
+  @Override
+  public void searchStarted(Search search) {
+    if (search instanceof HeuristicSearch) {
+      isHeuristic = true;
+    }
+    
+    startTime = System.currentTimeMillis();
+    
+    Runtime rt = Runtime.getRuntime();
+    startFreeMemory = rt.freeMemory();
+    totalMemory = rt.totalMemory();
+    maxMemory = rt.maxMemory();
+  }
+
+  @Override
+  public void searchConstraintHit(Search search) {
+  }
+
+  void reportRuntime () {
+    long td = time - startTime;
+    
+    int h = (int) (td / 3600000);
+    int m = (int) (td / 60000) % 60;
+    int s = (int) (td / 1000) % 60;
+    
+    out.print("  abs time:          ");
+    if (h < 10) out.print('0');
+    out.print( h);
+    out.print(':');
+    if (m < 10) out.print('0');
+    out.print( m);
+    out.print(':');
+    if (s < 10) out.print('0');
+    out.print( s);
+    
+    out.print( "  (");
+    out.print(td);
+    out.println(" ms)");
+  }
+  
+  @Override
+  public void searchFinished(Search search) {
+    report("------ Search statistics: ------");
+  }
+
+  void report (String header) {
+    time = System.currentTimeMillis();
+
+    out.println(header);
+
+    reportRuntime();
+        
+    out.println();
+    out.print("  search depth:      ");
+    out.print(searchLevel);
+    out.print(" (max: ");
+    out.print(maxSearchLevel);
+    out.println(")");
+    
+    out.print("  new states:        ");
+    out.println(newStates);
+    
+    out.print("  revisited states:  ");
+    out.println(revisitedStates);
+        
+    out.print("  end states:        ");
+    out.println(endStates);
+
+    out.print("  backtracks:        ");
+    out.println(backtracks);
+
+    out.print("  processed states:  ");
+    out.print( processedStates);
+    out.print(" (");
+    // a little ad-hoc rounding
+    double d = (double) backtracks / (double)processedStates;
+    int n = (int) d;
+    int m = (int) ((d - /*(double)*/ n) * 10.0);
+    out.print( n);
+    out.print('.');
+    out.print(m);
+    out.println( " bt/proc state)");
+    
+    out.print("  restored states:   ");
+    out.println(restoredStates);
+
+    if (isHeuristic) {
+       out.print("  queue size:        ");
+       out.println(queueSize);
+    }
+    
+    out.println();
+    out.print("  total memory [kB]: ");
+    out.print(totalMemory / 1024);
+    out.print(" (max: ");
+    out.print(maxMemory / 1024);
+    out.println(")");
+    
+    out.print("  free memory [kB]:  ");
+    out.println(freeMemory / 1024);
+    
+    out.print("  max heap objects:  ");
+    out.print(maxHeapCount);
+
+    out.println();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/SimpleDot.java b/src/main/gov/nasa/jpf/listener/SimpleDot.java
new file mode 100644 (file)
index 0000000..9d4c486
--- /dev/null
@@ -0,0 +1,507 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.Property;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.INVOKESTATIC;
+import gov.nasa.jpf.jvm.bytecode.JVMInstanceFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.InstanceInvocation;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.LockInstruction;
+import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
+import gov.nasa.jpf.jvm.bytecode.PUTSTATIC;
+import gov.nasa.jpf.jvm.bytecode.JVMStaticFieldInstruction;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ExceptionInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NoUncaughtExceptionsProperty;
+import gov.nasa.jpf.vm.NotDeadlockedProperty;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.HashSet;
+
+/**
+ * an alternative  Graphviz dot-file generator for simple,educational state graphs
+ * except of creating funny wallpapers, it doesn't help much in real life if the
+ * state count gets > 50, but for the small ones it's actually quite readable.
+ * Good for papers.
+ *
+ * normal states are labeled with their numeric ids, end states are double circled.
+ * start, end and error states are color filled
+ *
+ * edges have two labels: the choice value at the beginning and the CG cause
+ * at the end. Only the first incoming edge into a state shows the CG cause
+ *
+ * we only render one backtrack edge per from-state
+ *
+ * <2do> GraphViz doesn't seem to handle color or fontname for head/tail labels correctly
+ */
+public class SimpleDot extends ListenerAdapter {
+
+  static final String GRAPH_ATTRS = "pad=0.5";
+  static final String GENERIC_NODE_ATTRS = "shape=circle,style=filled,fillcolor=white";
+  static final String GENERIC_EDGE_ATTRS = "fontsize=10,fontname=Helvetica,fontcolor=blue,color=cadetblue,style=\"setlinewidth(0.5)\",arrowhead=empty,arrowsize=0.5";
+  static final String START_NODE_ATTRS = "fillcolor=green";
+  static final String END_NODE_ATTRS = "shape=doublecircle,fillcolor=cyan";
+  static final String ERROR_NODE_ATTRS = "color=red,fillcolor=lightcoral";
+  static final String BACKTRACK_EDGE_ATTRS = "arrowhead=onormal,color=gray52,style=\"dotted\"";
+  static final String RESTORED_EDGE_ATTRS = "arrowhead=onormal,color=red,style=\"dotted\"";
+  static final String NEW_EDGE_ATTRS = "arrowhead=normal";
+  static final String VISITED_EDGE_ATTRS = "arrowhead=vee";
+
+
+  //--- configurable Graphviz attributes
+  protected String graphAttrs;
+  protected String genericNodeAttrs;
+  protected String genericEdgeAttrs;
+  protected String startNodeAttrs;
+  protected String endNodeAttrs;
+  protected String errorNodeAttrs;
+  protected String newEdgeAttrs;
+  protected String visitedEdgeAttrs;
+  protected String backtrackEdgeAttrs;
+  protected String restoreEdgeAttrs;
+
+  protected boolean showTarget;
+  protected boolean printFile;
+
+  protected VM vm;
+  protected String app;
+  protected File file;
+  protected PrintWriter pw;
+
+  protected int lastId = -1;    // where we come from
+  protected String lastErrorId;
+  protected ElementInfo lastEi;
+  protected ThreadInfo lastTi;  // the last started thread
+
+  // helper because GraphViz cannot eliminate duplicate edges
+  HashSet<Long> seenEdges;
+
+  public SimpleDot( Config config, JPF jpf){
+
+    graphAttrs = config.getString("dot.graph_attr", GRAPH_ATTRS);
+    genericNodeAttrs = config.getString("dot.node_attr", GENERIC_NODE_ATTRS);
+    genericEdgeAttrs = config.getString("dot.edge_attr", GENERIC_EDGE_ATTRS);
+    newEdgeAttrs = config.getString("dot.new_edge_attr", NEW_EDGE_ATTRS);
+    visitedEdgeAttrs = config.getString("dot.visited_edge_attr", VISITED_EDGE_ATTRS);
+    startNodeAttrs = config.getString("dot.start_node_attr", START_NODE_ATTRS);
+    endNodeAttrs = config.getString("dot.end_node_attr", END_NODE_ATTRS);
+    errorNodeAttrs = config.getString("dot.error_node_attr", ERROR_NODE_ATTRS);
+    backtrackEdgeAttrs = config.getString("dot.bt_edge_attr", BACKTRACK_EDGE_ATTRS);
+    restoreEdgeAttrs = config.getString("dot.restore_edge_attr", RESTORED_EDGE_ATTRS);
+
+    printFile = config.getBoolean("dot.print_file", false);
+    showTarget = config.getBoolean("dot.show_target", false);
+
+    // app and filename are not known until the search is started
+    
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+
+  void initialize (VM vm){
+    Config config = vm.getConfig();
+    
+    app = vm.getSUTName();
+    app = app.replace("+", "__");
+    app = app.replace('.', '_');
+
+    String fname = config.getString("dot.file");
+    if (fname == null){
+      fname = app + ".dot";
+    }
+
+    try {
+      file = new File(fname);
+      FileWriter fw = new FileWriter(file);
+      pw = new PrintWriter(fw);
+    } catch (IOException iox){
+      throw new JPFConfigException("unable to open SimpleDot output file: " + fname);
+    }
+    
+    seenEdges = new HashSet<Long>();
+  }
+  
+  //--- the listener interface
+
+  @Override
+  public void searchStarted(Search search){
+    vm = search.getVM();
+    
+    initialize(vm);
+    
+    printHeader();
+    printStartState("S");
+  }
+
+  @Override
+  public void stateAdvanced(Search search){
+    int id = search.getStateId();
+    long edgeId = ((long)lastId << 32) | id;
+
+    if (id <0 || seenEdges.contains(edgeId)){
+      return; // skip the root state and property violations (reported separately)
+    }
+
+
+    if (search.isErrorState()) {
+      String eid = "e" + search.getNumberOfErrors();
+      printTransition(getStateId(lastId), eid, getLastChoice(), getError(search));
+      printErrorState(eid);
+      lastErrorId = eid;
+
+    } else if (search.isNewState()) {
+
+      if (search.isEndState()) {
+        printTransition(getStateId(lastId), getStateId(id), getLastChoice(), "return");
+        printEndState(getStateId(id));
+      } else {
+        printTransition(getStateId(lastId), getStateId(id), getLastChoice(), getNextCG());
+      }
+
+    } else { // already visited state
+      printTransition(getStateId(lastId), getStateId(id), getLastChoice(), null);
+    }
+
+    seenEdges.add(edgeId);
+    lastId = id;
+  }
+
+  @Override
+  public void stateBacktracked(Search search){
+    int id = search.getStateId();
+    long edgeId = ((long)lastId << 32) | id;
+
+    if (!seenEdges.contains(edgeId)) {
+      if(lastErrorId!=null) {
+        printBacktrack(lastErrorId, getStateId(id));
+        lastErrorId = null;
+      } else {
+        printBacktrack(getStateId(lastId), getStateId(id));
+      }
+      seenEdges.add(edgeId);
+    }
+    lastId = id;
+  }
+  
+  @Override
+  public void stateRestored(Search search) {
+    int id = search.getStateId();
+    long edgeId = ((long)lastId << 32) | id;
+
+    if (!seenEdges.contains(edgeId)) {
+      printRestored(getStateId(lastId), getStateId(id));
+      seenEdges.add(edgeId);
+    }
+    lastId = id;
+  }
+
+  @Override
+  public void searchFinished (Search search){
+    pw.println("}");
+    pw.close();
+  }
+
+  @Override
+  public void threadStarted (VM vm, ThreadInfo ti){
+    lastTi = ti;
+  }
+
+  @Override
+  public void objectWait (VM vm, ThreadInfo ti, ElementInfo ei){
+    lastEi = ei;
+    lastTi = ti;
+  }
+
+  @Override
+  public void publishFinished (Publisher publisher) {
+    PrintWriter ppw = publisher.getOut();
+    publisher.publishTopicStart("SimpleDot");
+
+    ppw.println("dot file generated: " + file.getPath());
+
+    if (printFile){
+      ppw.println();
+      FileUtils.printFile(ppw,file);
+    }
+  }
+
+
+  //--- data collection
+
+  protected String getStateId (int id){
+    return id < 0 ? "S" : Integer.toString(id);
+  }
+
+  protected String getLastChoice() {
+    ChoiceGenerator<?> cg = vm.getChoiceGenerator();
+    Object choice = cg.getNextChoice();
+
+    if (choice instanceof ThreadInfo){
+      int idx = ((ThreadInfo)choice).getId();
+      return "T"+idx;
+    } else {
+      return choice.toString(); // we probably want more here
+    }
+  }
+
+  // this is the only method that's more tricky - we have to find a balance
+  // between being conscious enough to not clutter the graph, and expressive
+  // enough to understand it.
+  // <2do> this doesn't deal well with custom or data CGs yet
+  protected String getNextCG(){
+    ChoiceGenerator<?> cg = vm.getNextChoiceGenerator(); // that's the next one
+    Instruction insn = cg.getInsn();
+
+    if (insn instanceof EXECUTENATIVE) {
+      return getNativeExecCG((EXECUTENATIVE)insn);
+
+    } else if (insn instanceof JVMFieldInstruction) { // shared object field access
+      return getFieldAccessCG((JVMFieldInstruction)insn);
+
+    } else if (insn instanceof LockInstruction){ // monitor_enter
+      return getLockCG((LockInstruction)insn);
+
+    } else if (insn instanceof JVMInvokeInstruction){ // sync method invoke
+      return getInvokeCG((JVMInvokeInstruction)insn);
+    }
+
+    return insn.getMnemonic(); // our generic fallback
+  }
+
+  protected String getNativeExecCG (EXECUTENATIVE insn){
+    MethodInfo mi = insn.getExecutedMethod();
+    String s = mi.getName();
+
+    if (s.equals("start")) {
+      s = "T" + lastTi.getId() + ".start";
+    } else if (s.equals("wait")) {
+      s = "T" + lastTi.getId() + ".wait";
+    }
+
+    return s;
+  }
+
+  protected String getFieldAccessCG (JVMFieldInstruction insn){
+    String s;
+
+    if (insn instanceof JVMInstanceFieldInstruction) {
+
+      if (insn instanceof PUTFIELD) {
+        s = "put";
+      } else /* if (insn instanceof GETFIELD) */ {
+        s = "get";
+      }
+
+      if (showTarget){
+        int ref = ((JVMInstanceFieldInstruction) insn).getLastThis();
+        s = getInstanceRef(ref) + '.' + s;
+      }
+
+    } else /* if (insn instanceof StaticFieldInstruction) */ {
+      if (insn instanceof PUTSTATIC) {
+        s = "put";
+      } else /* if (insn instanceof GETSTATIC) */ {
+        s = "get";
+      }
+
+      if (showTarget){
+        String clsName = ((JVMStaticFieldInstruction) insn).getLastClassName();
+        s = Misc.stripToLastDot(clsName) + '.' + s;
+      }
+    }
+
+    String varId = insn.getFieldName();
+    s = s + ' ' + varId;
+
+    return s;
+  }
+
+  protected String getLockCG(LockInstruction insn){
+    String s = "sync";
+
+    if (showTarget){
+      int ref = insn.getLastLockRef();
+      s = getInstanceRef(ref) + '.' + s;
+    }
+
+    return s;
+  }
+
+  protected String getInvokeCG (JVMInvokeInstruction insn){
+    MethodInfo mi = insn.getInvokedMethod();
+    String s = mi.getName() + "()";
+
+    if (showTarget){
+      if (insn instanceof InstanceInvocation) {
+        int ref = ((InstanceInvocation) insn).getLastObjRef();
+        s = getInstanceRef(ref) + '.' + s;
+
+      } else if (insn instanceof INVOKESTATIC) {
+        String clsName = ((INVOKESTATIC) insn).getInvokedClassName();
+        s = Misc.stripToLastDot(clsName) + '.' + s;
+      }
+    }
+
+    return s;
+  }
+
+  protected String getError (Search search){
+    String e;
+    Error error = search.getLastError();
+    Property prop = error.getProperty();
+
+    if (prop instanceof NoUncaughtExceptionsProperty){
+      ExceptionInfo xi = ((NoUncaughtExceptionsProperty)prop).getUncaughtExceptionInfo();
+      return Misc.stripToLastDot(xi.getExceptionClassname());
+
+    } else if (prop instanceof NotDeadlockedProperty){
+      return "deadlock";
+    }
+
+    // fallback
+    return Misc.stripToLastDot(prop.getClass().getName());
+  }
+
+  protected static String getInstanceRef (int ref){
+    return "@" + Integer.toHexString(ref).toUpperCase();
+  }
+
+  protected static String getClassObjectRef (int ref){
+    return "#" + Integer.toHexString(ref).toUpperCase();
+  }
+
+  //--- dot file stuff
+
+  protected void printHeader(){
+    pw.print("digraph ");
+    pw.print(app);
+    pw.println(" {");
+
+    pw.print("node [");
+    pw.print(genericNodeAttrs);
+    pw.println(']');
+
+    pw.print("edge [");
+    pw.print(genericEdgeAttrs);
+    pw.println(']');
+
+    pw.println(graphAttrs);
+
+    pw.println();
+    pw.print("label=\"");
+    pw.print(app);
+    pw.print("\"");
+    pw.println();
+  }
+
+  protected void printTransition(String fromState, String toState, String choiceVal, String cgCause){
+    pw.println();
+    pw.print(fromState);
+    pw.print(" -> ");
+    pw.print( toState);
+    pw.print(" [label=\"");
+    pw.print(choiceVal);
+    pw.print('"');
+    if (cgCause != null){
+      pw.print(NEW_EDGE_ATTRS);
+      pw.print(",headlabel=\"");
+      pw.print(cgCause);
+      pw.print('"');
+    } else {
+      pw.print(VISITED_EDGE_ATTRS);
+    }
+    pw.println(']');
+  }
+
+  protected void printBacktrack (String fromState, String toState){
+    pw.println();
+    pw.print(fromState);
+    pw.print(" -> ");
+    pw.print( toState);
+
+    pw.print(" [");
+    pw.print(backtrackEdgeAttrs);
+    pw.print(']');
+
+    pw.println("  // backtrack");
+  }
+
+  protected void printRestored (String fromState, String toState){
+    pw.println();
+    pw.print(fromState);
+    pw.print(" -> ");
+    pw.print( toState);
+
+    pw.print(" [");
+    pw.print(restoreEdgeAttrs);
+    pw.print(']');
+
+    pw.println("  // restored");
+  }
+  
+  protected void printStartState(String stateId){
+    pw.print(stateId);
+
+    pw.print(" [");
+    pw.print(startNodeAttrs);
+    pw.print(']');
+
+    pw.println("  // start state");
+  }
+
+  protected void printEndState(String stateId){
+    pw.print(stateId);
+
+    pw.print(" [");
+    pw.print(endNodeAttrs);
+    pw.print(']');
+
+    pw.println("  // end state");
+  }
+
+  protected void printErrorState(String error){
+    pw.print(error);
+
+    pw.print(" [");
+    pw.print(errorNodeAttrs);
+    pw.print(']');
+
+    pw.println("  // error state");
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/SimpleIdleFilter.java b/src/main/gov/nasa/jpf/listener/SimpleIdleFilter.java
new file mode 100644 (file)
index 0000000..3d448fd
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.ObjVector;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.logging.Logger;
+
+
+/**
+ * This is the simple version of IdleFilter. This one simply breaks all back-edges
+ * encountered to make sure JPF's partial-order reduction doesn't add meaningless
+ * transitions forever. This is our dual of the cycle-proviso in classic po-reduction theory.
+ *
+ *  One can set how many back-edges to consider before breaking, but by default it is 1
+ *
+ */
+public class SimpleIdleFilter extends ListenerAdapter {
+
+         static Logger log = JPF.getLogger("gov.nasa.jpf.listener.SimpleIdleFilter");
+
+         static class ThreadStat {
+           String tname;
+
+           int backJumps;
+
+           int loopStartPc;
+
+           int loopEndPc;
+
+           int loopStackDepth;
+
+           ThreadStat(String tname) {
+             this.tname = tname;
+           }
+         }
+
+         ObjVector<ThreadStat> threadStats = new ObjVector<ThreadStat>();
+
+         ThreadStat ts;
+
+         int maxBackJumps;
+
+         // ----------------------------------------------------- SearchListener
+         // interface
+
+         public SimpleIdleFilter(Config config) {
+           maxBackJumps = config.getInt("idle.max_backjumps", 1);
+         }
+
+         @Override
+         public void stateAdvanced(Search search) {
+           ts.backJumps = 0;
+           ts.loopStackDepth = 0;
+           ts.loopStartPc = ts.loopEndPc = 0;
+         }
+
+         @Override
+         public void stateBacktracked(Search search) {
+           ts.backJumps = 0;
+           ts.loopStackDepth = 0;
+           ts.loopStartPc = ts.loopEndPc = 0;
+         }
+
+         // ----------------------------------------------------- VMListener interface
+         @Override
+         public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+
+       if (!executedInsn.isBackJump()) {     // Put this test first for a performance optimization.
+         return;
+       }
+
+           int tid = ti.getId();
+           ts = threadStats.get(tid);
+           if (ts == null) {
+             ts = new ThreadStat(ti.getName());
+             threadStats.set(tid, ts);
+           }
+
+       ts.backJumps++;
+
+       int loopStackDepth = ti.getStackDepth();
+       int loopPc = nextInsn.getPosition();
+
+       if ((loopStackDepth != ts.loopStackDepth) || (loopPc != ts.loopStartPc)) {
+         // new loop, reset
+         ts.loopStackDepth = loopStackDepth;
+         ts.loopStartPc = loopPc;
+         ts.loopEndPc = executedInsn.getPosition();
+         ts.backJumps = 0;
+       } else {
+         if (ts.backJumps > maxBackJumps) {
+           ti.reschedule("idleFilter"); // this breaks the executePorStep loop
+         }
+       }
+         }
+}
diff --git a/src/main/gov/nasa/jpf/listener/StackDepthChecker.java b/src/main/gov/nasa/jpf/listener/StackDepthChecker.java
new file mode 100644 (file)
index 0000000..6c07905
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * listener that throws a java.lang.StackOverflowError in case a thread
+ * exceeds a configured max stack depth
+ * 
+ * <2do> - maybe we should only count visible stackframes, i.e. the ones for
+ * which we have invoke insns on the stack
+ */
+public class StackDepthChecker extends ListenerAdapter {
+  
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.listener.StackDepthChecker");
+
+  protected int maxDepth;
+  
+  public StackDepthChecker (Config config, JPF jpf){
+    maxDepth = config.getInt( "sdc.max_stack_depth", 42);
+  }
+  
+  @Override
+  public void methodEntered (VM vm, ThreadInfo thread, MethodInfo mi){
+    
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    int depth = ti.getStackDepth(); // note this is only an approximation since it also returns natives and overlays
+    
+    if (depth > maxDepth){
+      log.info("configured vm.max_stack_depth exceeded: ", depth);
+      
+      // NOTE - we get this notification from inside of the InvokeInstruction.enter(),
+      // i.e. before we get the instructionExecuted(). Throwing exceptions is
+      // therefore a bit harder since we have to set the next pc explicitly
+
+      Instruction nextPc = ti.createAndThrowException("java.lang.StackOverflowError");
+      StackFrame topFrame = ti.getModifiableTopFrame();
+      topFrame.setPC(nextPc);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/StackTracker.java b/src/main/gov/nasa/jpf/listener/StackTracker.java
new file mode 100644 (file)
index 0000000..1217440
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.INVOKESPECIAL;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.VirtualInvocation;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * simple tool to log stack invocations
+ *
+ * at this point, it doesn't do fancy things yet, but gives a more high
+ * level idea of what got executed by JPF than the ExecTracker
+ */
+public class StackTracker extends ListenerAdapter {
+
+  static final String INDENT = "  ";
+
+  MethodInfo lastMi;
+  PrintWriter out;
+  long nextLog;
+  int logPeriod;
+
+  public StackTracker (Config conf, JPF jpf) {
+    out = new PrintWriter(System.out, true);
+    logPeriod = conf.getInt("jpf.stack_tracker.log_period", 5000);
+  }
+
+  void logStack(ThreadInfo ti) {
+    long time = System.currentTimeMillis();
+
+    if (time < nextLog) {
+      return;
+    }
+
+    nextLog = time + logPeriod;
+
+    out.println();
+    out.print("Thread: ");
+    out.print(ti.getId());
+    out.println(":");
+
+    out.println(ti.getStackTrace());
+    out.println();
+  }
+
+  @Override
+  public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute) {
+    MethodInfo mi = insnToExecute.getMethodInfo();
+
+    if (mi != lastMi) {
+      logStack(ti);
+      lastMi = mi;
+
+    } else if (insnToExecute instanceof JVMInvokeInstruction) {
+      MethodInfo callee;
+
+      // that's the only little gist of it - if this is a VirtualInvocation,
+      // we have to dig the callee out by ourselves (it's not known
+      // before execution)
+
+      if (insnToExecute instanceof VirtualInvocation) {
+        VirtualInvocation callInsn = (VirtualInvocation)insnToExecute;
+        int objref = callInsn.getCalleeThis(ti);
+        callee = callInsn.getInvokedMethod(ti, objref);
+
+      } else if (insnToExecute instanceof INVOKESPECIAL) {
+        INVOKESPECIAL callInsn = (INVOKESPECIAL)insnToExecute;
+        callee = callInsn.getInvokedMethod(ti);
+
+      } else {
+        JVMInvokeInstruction callInsn = (JVMInvokeInstruction)insnToExecute;
+        callee = callInsn.getInvokedMethod(ti);
+      }
+
+      if (callee != null) {
+        if (callee.isMJI()) {
+          logStack(ti);
+        }
+      } else {
+        out.println("ERROR: unknown callee of: " + insnToExecute);
+      }
+    }
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    lastMi = null;
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    lastMi = null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/StateCountEstimator.java b/src/main/gov/nasa/jpf/listener/StateCountEstimator.java
new file mode 100644 (file)
index 0000000..f50da27
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.Transition;
+
+import java.io.PrintWriter;
+import java.util.Formatter;
+
+/**
+ * From already visited states, estimates the total number of states by the branching factor.
+ */
+
+public class StateCountEstimator extends ListenerAdapter
+{
+   private final PrintWriter   m_out;
+   private final StringBuilder m_buffer    = new StringBuilder();
+   private final Formatter     m_formatter = new Formatter(m_buffer);
+   private final int           m_logPeriod;
+   private       double        m_lastPercent;
+   private       long          m_nextLog;
+   private       long          m_startTime;
+
+   public StateCountEstimator(Config config)
+   {
+      m_out       = new PrintWriter(System.out, true);
+      m_logPeriod = config.getInt("jpf.state_count_estimator.log_period", 0);
+   }
+
+   @Override
+   public void searchStarted(Search search)
+   {
+      m_nextLog     = 0;
+      m_lastPercent = 0.0;
+      m_startTime   = System.currentTimeMillis();
+   }
+
+   @Override
+   public void searchFinished(Search search)
+   {
+      log(search);
+   }
+
+   @Override
+   public void stateProcessed(Search search)
+   {
+      if (m_nextLog > System.currentTimeMillis())
+         return;
+      
+      if (log(search))
+         m_nextLog = m_logPeriod + System.currentTimeMillis();
+   }
+
+   private boolean log(Search search)
+   {
+      VM vm;
+      Path path;
+      Transition trans;
+      ChoiceGenerator cg;
+      double percent, delta;
+      long currentState, expectedState, currentTime, expectedTime;
+      int i, size, processed;
+
+      vm       = search.getVM();
+      path      = vm.getPath();
+      size      = path.size();
+      percent   = 0.0;
+      delta     = 1.0;
+      processed = 0;
+      
+      for (i = 0; i < size; i++)
+      {
+         trans      = path.get(i);
+         cg         = trans.getChoiceGenerator();
+         delta     /= cg.getTotalNumberOfChoices();
+         processed  = cg.getProcessedNumberOfChoices() - 1;
+         percent   += delta * processed;
+      }
+      
+      if (size == 0)
+         percent = 1.0;
+      
+      if (m_lastPercent > percent)  // Sometimes a state is declared as processed but doesn't show up in the path so the percentage appears to go backwards.
+         return(false);
+      
+      m_lastPercent = percent;
+         
+      currentState  = vm.getStateCount();
+      expectedState = (long) (currentState / percent);
+      
+      currentTime   = System.currentTimeMillis() - m_startTime;
+      expectedTime  = (long) (currentTime / percent);
+      
+      m_formatter.format("State:  %,d / %,d (%g%%)    Time:  ", currentState, expectedState, 100.0 * percent);
+      
+      formatTime(expectedTime);
+      m_buffer.append(" - ");
+      formatTime(currentTime);
+      m_buffer.append(" = ");
+      formatTime(expectedTime - currentTime);
+
+      m_out.println(m_buffer.toString());
+      m_buffer.setLength(0);
+      
+      return(true);
+   }
+
+   private void formatTime(long time)
+   {
+      long days, hours, minutes, seconds;
+      boolean commit;
+
+      seconds = time / 1000;
+      minutes = seconds / 60;
+      hours   = minutes / 60;
+      days    = hours / 24;
+
+      seconds %= 60;
+      minutes %= 60;
+      hours   %= 24;
+
+      commit = false;
+
+      if ((commit) || (days != 0))
+      {
+         commit = true;
+         m_buffer.append(days);
+         m_buffer.append(' ');
+      }
+
+      if ((commit) || (hours != 0))
+      {
+         if ((commit) && (hours < 10))
+            m_buffer.append('0');
+
+         m_buffer.append(hours);
+         m_buffer.append(':');
+         commit = true;
+      }
+
+      if ((commit) || (minutes != 0))
+      {
+         if ((commit) && (minutes < 10))
+            m_buffer.append('0');
+
+         m_buffer.append(minutes);
+         m_buffer.append(':');
+         commit = true;
+      }
+
+      if ((commit) && (seconds < 10))
+         m_buffer.append('0');
+
+      m_buffer.append(seconds);
+   }
+}
diff --git a/src/main/gov/nasa/jpf/listener/StateSpaceAnalyzer.java b/src/main/gov/nasa/jpf/listener/StateSpaceAnalyzer.java
new file mode 100644 (file)
index 0000000..9b34d22
--- /dev/null
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.MONITORENTER;
+import gov.nasa.jpf.jvm.bytecode.MONITOREXIT;
+import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.report.PublisherExtension;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.BooleanChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.DoubleChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * a listener that collects information about ChoiceGenerators, choices and
+ * where they are used. The purpose is to find out what causes the state space
+ * size, and to give hints of how to reduce it.
+ * The interesting part is that this is a listener that doesn't work off traces,
+ * but needs to collect info up to a point where we want it to report. That's
+ * state space or resource related, i.e. a combination of
+ *
+ *  - number of transitions
+ *  - memory consumption
+ *  - elapsed time
+ *
+ * once the limit is reached, we stop the search and report.
+ *
+ * There are two parts we are interested in:
+ *
+ *  - what CGs do we have
+ *  - what creates those CGs (thread,insn,source) = last step insn
+ */
+public class StateSpaceAnalyzer extends ListenerAdapter implements PublisherExtension {
+  // Search termination conditions
+
+  private final long m_maxTime;
+  private final long m_maxMemory;
+  private final int m_maxStates;
+  private final int m_maxChoices;
+  private final ArrayList<CGGrouper> m_groupers = new ArrayList<CGGrouper>();
+  private final int m_maxOutputLines; // how many detail lines do we display in the report
+  private long m_terminateTime;
+  private int m_choiceCount;
+
+  public StateSpaceAnalyzer(Config config, JPF jpf) {
+    m_maxStates = config.getInt("ssa.max_states", -1);
+    m_maxTime = config.getDuration("ssa.max_time", -1);
+    m_maxMemory = config.getMemorySize("ssa.max_memory", -1);
+    m_maxChoices = config.getInt("ssa.max_choices", -1);
+    m_maxOutputLines = config.getInt("ssa.max_output_lines", 10);
+
+    initGroupers(config);
+
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+
+  private void initGroupers(Config config) {
+    HashMap<String, CGAccessor> accessors;
+    CGGrouper grouper;
+    int i;
+
+    if (config.getStringArray("ssa.sort_order") == null) {
+      config.setProperty("ssa.sort_order", "type");
+      config.setProperty("ssa.sort_order2", "package,class,method,instruction,type");
+    }
+
+    accessors = new HashMap<String, CGAccessor>(5);
+    accessors.put("package", new CGPackageAccessor());
+    accessors.put("class", new CGClassAccessor());
+    accessors.put("method", new CGMethodAccessor());
+    accessors.put("instruction", new CGInstructionAccessor());
+    accessors.put("type", new CGTypeAccessor());
+
+    m_groupers.add(initGrouper(config, "ssa.sort_order", accessors));
+
+    for (i = 2; true; i++) {
+      grouper = initGrouper(config, "ssa.sort_order" + i, accessors);
+      if (grouper == null) {
+        break;
+      }
+
+      m_groupers.add(grouper);
+    }
+  }
+
+  private CGGrouper initGrouper(Config config, String parameter, Map<String, CGAccessor> accessors) {
+    CGGrouper grouper;
+    CGAccessor list[];
+    StringBuilder name;
+    String key, sortOrder[];
+    int i;
+
+    sortOrder = config.getStringArray(parameter);
+    if ((sortOrder == null) || (sortOrder.length <= 0)) {
+      return (null);
+    }
+
+    name = new StringBuilder();
+    list = new CGAccessor[sortOrder.length];
+
+    for (i = 0; i < sortOrder.length; i++) {
+      key = sortOrder[i].toLowerCase();
+      name.append(key);
+      name.append(", ");
+
+      list[i] = accessors.get(key);
+
+      if (list[i] == null) {
+        config.exception("Unknown sort key: " + sortOrder[i] + ".  Found in parameter: " + parameter);
+      }
+    }
+
+    name.setLength(name.length() - 2);
+    grouper = new CGGrouper(list, name.toString());
+
+    return (grouper);
+  }
+
+  @Override
+  public void choiceGeneratorSet(VM vm, ChoiceGenerator<?> newCG) {
+    int i;
+
+    // NOTE: we get this from SystemState.nextSuccessor, i.e. when the CG
+    // is actually used (which doesn't necessarily mean it produces a new state,
+    // but it got created from a new state)
+    
+    // The original code stored each choice generator in an ArrayList.  For long 
+    // running tests, this would grow and cause an OutOfMemoryError.  Now, the 
+    // generators are dealt with as they are created.  This means a bit more 
+    // processing up front but huge memory savings in the long run.  If the 
+    // machine has multiple processors, a better solution would be to have a 
+    // background thread process the generators.
+
+    m_choiceCount += newCG.getTotalNumberOfChoices();
+
+    for (i = m_groupers.size(); --i >= 0; )
+      m_groupers.get(i).add(newCG);
+  }
+
+  @Override
+  public void searchStarted(Search search) {
+    int i;
+    
+    for (i = m_groupers.size(); --i >= 0; )
+      m_groupers.get(i).clear();
+    
+    m_choiceCount = 0;
+    m_terminateTime = m_maxTime + System.currentTimeMillis();
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    if (shouldTerminate(search)) {
+      search.terminate();
+    }
+  }
+
+  private boolean shouldTerminate(Search search) {
+    if ((m_maxStates >= 0) && (search.getVM().getStateCount() >= m_maxStates)) {
+      return (true);
+    }
+
+    if ((m_maxTime >= 0) && (System.currentTimeMillis() >= m_terminateTime)) {
+      return (true);
+    }
+
+    if ((m_maxMemory >= 0) && (Runtime.getRuntime().totalMemory() >= m_maxMemory)) {
+      return (true);
+    }
+
+    if ((m_maxChoices >= 0) && (m_choiceCount >= m_maxChoices)) {
+      return (true);
+    }
+
+    return (false);
+  }
+
+  @Override
+  public void publishFinished(Publisher publisher) {
+    CGGrouper groupers[];
+
+    groupers = new CGGrouper[m_groupers.size()];
+    m_groupers.toArray(groupers);
+
+    if (publisher instanceof ConsolePublisher) {
+      new PublishConsole((ConsolePublisher) publisher, groupers, m_maxOutputLines).publish();
+    }
+  }
+
+  private enum CGType {
+
+    FieldAccess,
+    ObjectWait,
+    ObjectNotify,
+    SyncEnter,
+    SyncExit,
+    ThreadStart,
+    ThreadTerminate,
+    ThreadSuspend,
+    ThreadResume,
+    SyncCall,
+    SyncReturn,
+    AtomicOp,
+    DataChoice
+  }
+
+  private interface CGAccessor {
+
+    public Object getValue(ChoiceGenerator generator);
+  }
+
+  private static class CGPackageAccessor implements CGAccessor {
+
+    @Override
+       public Object getValue(ChoiceGenerator generator) {
+      ClassInfo ci;
+      MethodInfo mi;
+      Instruction instruction;
+
+      instruction = generator.getInsn();
+      if (instruction == null) {
+        return (null);
+      }
+
+      mi = instruction.getMethodInfo();
+      if (mi == null) {
+        return (null);
+      }
+
+      ci = mi.getClassInfo();
+      if (ci == null) {
+        return (null);
+      }
+
+      return (ci.getPackageName());
+    }
+  }
+
+  private static class CGClassAccessor implements CGAccessor {
+
+    @Override
+       public Object getValue(ChoiceGenerator generator) {
+      ClassInfo ci;
+      MethodInfo mi;
+      Instruction instruction;
+
+      instruction = generator.getInsn();
+      if (instruction == null) {
+        return (null);
+      }
+
+      mi = instruction.getMethodInfo();
+      if (mi == null) {
+        return (null);
+      }
+
+      ci = mi.getClassInfo();
+      if (ci == null) {
+        return (null);
+      }
+
+      return (ci.getName());
+    }
+  }
+
+  private static class CGMethodAccessor implements CGAccessor {
+
+    @Override
+       public Object getValue(ChoiceGenerator generator) {
+      MethodInfo mi;
+      Instruction instruction;
+
+      instruction = generator.getInsn();
+      if (instruction == null) {
+        return (null);
+      }
+
+      mi = instruction.getMethodInfo();
+      if (mi == null) {
+        return (null);
+      }
+
+      return (mi.getFullName());
+    }
+  }
+
+  private static class CGInstructionAccessor implements CGAccessor {
+
+    @Override
+       public Object getValue(ChoiceGenerator generator) {
+      return (generator.getInsn());
+    }
+  }
+
+  private static class CGTypeAccessor implements CGAccessor {
+
+    private static final String OBJECT_CLASS_NAME = Object.class.getName();
+    private static final String THREAD_CLASS_NAME = Thread.class.getName();
+
+    @Override
+       public Object getValue(ChoiceGenerator generator) {
+      if (generator instanceof ThreadChoiceGenerator) {
+        return (getType((ThreadChoiceGenerator) generator));
+      }
+
+      if (generator instanceof BooleanChoiceGenerator) {
+        return (CGType.DataChoice);
+      }
+
+      if (generator instanceof DoubleChoiceGenerator) {
+        return (CGType.DataChoice);
+      }
+
+      if (generator instanceof IntChoiceGenerator) {
+        return (CGType.DataChoice);
+      }
+
+      if (generator instanceof BooleanChoiceGenerator) {
+        return (CGType.DataChoice);
+      }
+
+      return (null);
+    }
+
+    private static CGType getType(ThreadChoiceGenerator generator) {
+      Instruction instruction;
+
+      instruction = generator.getInsn();
+      if (instruction == null) {
+        return (null);
+      }
+
+      if (instruction instanceof JVMFieldInstruction) {
+        return (CGType.FieldAccess);
+      }
+
+      if (instruction instanceof JVMInvokeInstruction) {
+        return (getType((JVMInvokeInstruction) instruction));
+      }
+
+      if (instruction instanceof JVMReturnInstruction) {
+        return (getType(generator, (JVMReturnInstruction) instruction));
+      }
+
+      if (instruction instanceof MONITORENTER) {
+        return (CGType.SyncEnter);
+      }
+
+      if (instruction instanceof MONITOREXIT) {
+        return (CGType.SyncExit);
+      }
+
+      return (null);
+    }
+
+    private static CGType getType(JVMInvokeInstruction instruction) {
+      MethodInfo mi;
+
+      if (is(instruction, OBJECT_CLASS_NAME, "wait")) {
+        return (CGType.ObjectWait);
+      }
+
+      if (is(instruction, OBJECT_CLASS_NAME, "notify")) {
+        return (CGType.ObjectNotify);
+      }
+
+      if (is(instruction, OBJECT_CLASS_NAME, "notifyAll")) {
+        return (CGType.ObjectNotify);
+      }
+
+      if (is(instruction, THREAD_CLASS_NAME, "start")) {
+        return (CGType.ThreadStart);
+      }
+
+      if (is(instruction, THREAD_CLASS_NAME, "suspend")) {
+        return (CGType.ThreadSuspend);
+      }
+
+      if (is(instruction, THREAD_CLASS_NAME, "resume")) {
+        return (CGType.ThreadResume);
+      }
+
+      mi = instruction.getInvokedMethod();
+      if (mi.getClassName().startsWith("java.util.concurrent.atomic.")) {
+        return (CGType.AtomicOp);
+      }
+
+      if (mi.isSynchronized()) {
+        return (CGType.SyncCall);
+      }
+
+      return (null);
+    }
+
+    private static boolean is(JVMInvokeInstruction instruction, String className, String methodName) {
+      MethodInfo mi;
+      ClassInfo ci;
+
+      mi = instruction.getInvokedMethod();
+      if (!methodName.equals(mi.getName())) {
+        return (false);
+      }
+
+      ci = mi.getClassInfo();
+
+      if (!className.equals(ci.getName())) {
+        return (false);
+      }
+
+      return (true);
+    }
+
+    private static CGType getType(ThreadChoiceGenerator generator, JVMReturnInstruction instruction) {
+      MethodInfo mi;
+
+      if (generator.getThreadInfo().getStackDepth() <= 1) // The main thread has 0 frames.  Other threads have 1 frame.
+      {
+        return (CGType.ThreadTerminate);
+      }
+
+      mi = instruction.getMethodInfo();
+      if (mi.isSynchronized()) {
+        return (CGType.SyncReturn);
+      }
+
+      return (null);
+    }
+  }
+
+  private static class TreeNode {
+
+    private final HashMap<Object, TreeNode> m_childNodes;
+    private final ArrayList<Object> m_sortedValues;
+    private final CGAccessor m_accessors[];
+    private final Object m_value;
+    private final int m_level;
+    private String m_sampleGeneratorClassName;
+    private Instruction m_sampleGeneratorInstruction;
+    private int m_choiceCount;
+    private int m_generatorCount;
+
+    TreeNode(CGAccessor accessors[], int level, Object value) {
+      m_accessors = accessors;
+      m_level = level;
+      m_value = value;
+
+      if (level >= accessors.length) {
+        m_childNodes = null;
+        m_sortedValues = null;
+      } else {
+        m_sortedValues = new ArrayList<Object>();
+        m_childNodes = new HashMap<Object, TreeNode>();
+      }
+    }
+
+    public void add(ChoiceGenerator generator) {
+      TreeNode child;
+      Object value;
+
+      m_generatorCount++;
+      m_choiceCount += generator.getTotalNumberOfChoices();
+
+      if (isLeaf()) {
+        if (m_sampleGeneratorClassName == null) {
+          m_sampleGeneratorClassName = generator.getClass().getName();
+          m_sampleGeneratorInstruction = generator.getInsn();
+        }
+
+        return;
+      }
+
+      value = m_accessors[m_level].getValue(generator);
+      child = m_childNodes.get(value);
+      if (child == null) {
+        child = new TreeNode(m_accessors, m_level + 1, value);
+        m_childNodes.put(value, child);
+      }
+
+      child.add(generator);
+    }
+
+    public int getLevel() {
+      return (m_level);
+    }
+
+    public Object getValue() {
+      return (m_value);
+    }
+
+    public int getChoiceCount() {
+      return (m_choiceCount);
+    }
+
+    public int getGeneratorCount() {
+      return (m_generatorCount);
+    }
+
+    public String getSampleGeneratorClassName() {
+      return (m_sampleGeneratorClassName);
+    }
+    
+    public Instruction getSampleGeneratorInstruction() {
+      return (m_sampleGeneratorInstruction);
+    }
+
+    public boolean isLeaf() {
+      return (m_childNodes == null);
+    }
+
+    public void sort() {
+      Comparator<Object> comparator;
+
+      if (isLeaf()) {
+        return;
+      }
+
+      m_sortedValues.clear();
+      m_sortedValues.addAll(m_childNodes.keySet());
+
+      comparator = new Comparator<Object>() {
+
+        @Override
+               public int compare(Object value1, Object value2) {
+          TreeNode node1, node2;
+
+          node1 = m_childNodes.get(value1);
+          node2 = m_childNodes.get(value2);
+
+          return (node2.getChoiceCount() - node1.getChoiceCount());  // Sort descending
+        }
+      };
+
+      Collections.sort(m_sortedValues, comparator);
+
+      for (TreeNode child : m_childNodes.values()) {
+        child.sort();
+      }
+    }
+
+    public List<TreeNode> tour() {
+      List<TreeNode> result;
+
+      result = new ArrayList<TreeNode>();
+      tour(result);
+
+      return (result);
+    }
+
+    public void tour(List<TreeNode> list) {
+      TreeNode child;
+      Object value;
+      int i;
+
+      list.add(this);
+
+      if (isLeaf()) {
+        return;
+      }
+
+      for (i = 0; i < m_sortedValues.size(); i++) {
+        value = m_sortedValues.get(i);
+        child = m_childNodes.get(value);
+        child.tour(list);
+      }
+    }
+
+    @Override
+       public String toString() {
+      StringBuilder result;
+
+      result = new StringBuilder();
+
+      if (m_value == null) {
+        result.append("(null)");
+      } else {
+        result.append(m_value);
+      }
+
+      result.append(" - L");
+      result.append(m_level);
+      result.append(" / C");
+      result.append(m_choiceCount);
+      result.append(" / G");
+      result.append(m_generatorCount);
+      result.append(" / N");
+      result.append(m_childNodes.size());
+
+      return (result.toString());
+    }
+  }
+
+  private static class CGGrouper {
+
+    private final CGAccessor m_accessors[];
+    private final String m_name;
+    private       TreeNode m_root;
+    private       boolean m_sorted;
+
+    CGGrouper(CGAccessor accessors[], String name) {
+      if (accessors.length <= 0) {
+        throw new IllegalArgumentException("accessors.length <= 0");
+      }
+
+      if (name == null) {
+        throw new NullPointerException("name == null");
+      }
+
+      m_accessors = accessors;
+      m_name = name;
+      
+      clear();
+    }
+    
+    public void clear() {
+      m_sorted = false;
+      m_root = new TreeNode(m_accessors, 0, "All");
+    }
+
+    public String getName() {
+      return(m_name);
+    }
+
+    public int getLevelCount() {
+      return(m_accessors.length);
+    }
+
+    public TreeNode getTree() {
+      if (!m_sorted) {
+        m_sorted = true;
+        m_root.sort();
+      }
+      
+      return(m_root);
+    }
+    
+    public void add(ChoiceGenerator generator) {
+      m_sorted = false;
+      m_root.add(generator); 
+    }
+  }
+
+  private static abstract class Publish {
+
+    protected final Publisher m_publisher;
+    protected final CGGrouper m_groupers[];
+    protected final int m_maxOutputLines;
+    protected PrintWriter m_output;
+
+    Publish(Publisher publisher, CGGrouper groupers[], int maxOutputLines) {
+      m_publisher = publisher;
+      m_groupers = groupers;
+      m_maxOutputLines = maxOutputLines;
+    }
+
+    public abstract void publish();
+  }
+
+  private static class PublishConsole extends Publish {
+
+    PublishConsole(ConsolePublisher publisher, CGGrouper[] groupers, int maxOutputLines) {
+      super(publisher, groupers, maxOutputLines);
+      m_output = publisher.getOut();
+    }
+
+    @Override
+       public void publish() {
+      int i;
+
+      for (i = 0; i < m_groupers.length; i++) {
+        publishSortedData(m_groupers[i]);
+      }
+    }
+
+    private void publishSortedData(CGGrouper grouper) {
+      List<TreeNode> tour;
+      TreeNode node;
+      int i, lines, levelCount;
+
+      lines = 0;
+      levelCount = grouper.getLevelCount();
+      node = grouper.getTree();
+      tour = node.tour();
+
+      m_publisher.publishTopicStart("Grouped By: " + grouper.getName());
+
+      for (i = 0; (i < tour.size()) && (lines < m_maxOutputLines); i++) {
+        node = tour.get(i);
+
+        publishTreeNode(node);
+
+        if (node.isLeaf()) {
+          publishDetails(node, levelCount + 1);
+          lines++;
+        }
+      }
+
+      if (lines >= m_maxOutputLines) {
+        m_output.println("...");
+      }
+    }
+
+    private void publishTreeNode(TreeNode node) {
+      Object value;
+
+      // Tree
+      publishPadding(node.getLevel());
+
+      value = node.getValue();
+      if (value == null) {
+        m_output.print("(null)");
+      } else {
+        m_output.print(value);
+      }
+
+      // Choices
+      m_output.print("  (choices: ");
+      m_output.print(node.getChoiceCount());
+
+      // Generators
+      m_output.print(", generators: ");
+      m_output.print(node.getGeneratorCount());
+
+      m_output.println(')');
+    }
+
+    private void publishDetails(TreeNode node, int levelCount) {
+      ChoiceGenerator generator;
+      Instruction instruction;
+
+      instruction = node.getSampleGeneratorInstruction();
+
+      // Location
+      publishPadding(levelCount);
+      m_output.print("Location:  ");
+      m_output.println(instruction.getFileLocation());
+
+      // Code
+      publishPadding(levelCount);
+      m_output.print("Code:  ");
+      m_output.println(instruction.getSourceOrLocation().trim());
+
+      // Instruction
+      publishPadding(levelCount);
+      m_output.print("Instruction:  ");
+      m_output.println(instruction.getMnemonic());
+
+      // Position
+      publishPadding(levelCount);
+      m_output.print("Position:  ");
+      m_output.println(instruction.getPosition());
+
+      // Generator Class
+      publishPadding(levelCount);
+      m_output.print("Generator Class:  ");
+      m_output.println(node.getSampleGeneratorClassName());
+    }
+
+    private void publishPadding(int levelCount) {
+      int i;
+
+      for (i = levelCount; i > 0; i--) {
+        m_output.print("   ");
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/StateSpaceDot.java b/src/main/gov/nasa/jpf/listener/StateSpaceDot.java
new file mode 100644 (file)
index 0000000..59e4df2
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Step;
+import gov.nasa.jpf.vm.Transition;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/*
+ * Add a state space observer to JPF and build a graph of the state space
+ * that is explored by JPF. The graph can be generated in different formats.
+ * The current formats that are supported are DOT (visualized by a tool
+ * like GraphViz from ATT - http://www.graphviz.org/) and gdf (used by GUESS
+ * from HP - http://www.hpl.hp.com/research/idl/projects/graphs/).
+ * The graph is stored in a file called "jpf-state-space.<extension>" where
+ * extension is ".dot" or ".gdf". By default it generates a DOT graph.
+ *
+ * Options:
+ *   -gdf:                Generate the graph in GDF format. The default is DOT.
+ *   -transition-numbers: Include transition numbers in transition labels.
+ *   -show-source:        Include source lines in transition labels.
+ *   -labelvisible:       Indicates if the label on the transitions is visible (used only with the -gdf option)
+ *   -help:               Prints this help information and stops.
+ *
+ * @date 9/12/03
+ * @author Owen O'Malley (Initial version generating the DOT graph)
+ *
+ * @date 7/17/05
+ * @author Masoud Mansouri-Samani (Extended to also generate the gdf graph)
+ */
+public class StateSpaceDot extends ListenerAdapter {
+  // NODE styles constants
+  static final int RECTANGLE = 1;
+  static final int ELLIPSE   = 2;
+  static final int ROUND_RECTANGLE = 3;
+  static final int RECTANGLE_WITH_TEXT = 4;
+  static final int ELLIPSE_WITH_TEXT = 5;
+  static final int ROUND_RECTANGLE_WITH_TEXT = 6;
+
+  private static final String DOT_EXT = "dot";
+  private static final String GDF_EXT = "gdf";
+  private static final String OUT_FILENAME_NO_EXT = "jpf-state-space";
+
+  // State and transition node styles used
+  private static final int state_node_style = ELLIPSE_WITH_TEXT;
+  private static final int transition_node_style = RECTANGLE_WITH_TEXT;
+
+  // File formats supported
+  private static final int DOT_FORMAT=0;
+  private static final int GDF_FORMAT=1;
+
+  private BufferedWriter graph = null;
+  private int edge_id = 0;
+  private static boolean transition_numbers=false;
+  private static boolean show_source=false;
+  private static int format=DOT_FORMAT;
+  private String out_filename = OUT_FILENAME_NO_EXT+"."+DOT_EXT;
+  private static boolean labelvisible=false;
+  private static boolean helpRequested=false;
+
+
+  /* In gdf format all the edges must come after all the nodes of the graph
+   * are generated. So we first output the nodes as we come across them but
+   * we store the strings for edges in the gdfEdges list and output them when
+   * the search ends.
+   */
+  ArrayList<String> gdfEdges=new ArrayList<String>();
+
+  private StateInformation prev_state = null;
+
+  public StateSpaceDot(Config conf, JPF jpf) {
+
+    VM vm = jpf.getVM();
+    vm.recordSteps(true);
+  }
+
+  @Override
+  public void searchStarted(Search search) {
+    try {
+      beginGraph();
+    } catch (IOException e) {}
+  }
+
+  @Override
+  public void searchFinished(Search search) {
+    try {
+      endGraph();
+    } catch (IOException e) {}
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    int id = search.getStateId();
+    boolean has_next =search.hasNextState();
+    boolean is_new = search.isNewState();
+    try {
+      if (format==DOT_FORMAT) {
+        graph.write("/* searchAdvanced(" + id + ", " + makeDotLabel(search, id) +
+                    ", " + has_next + ") */");
+        graph.newLine();
+      }
+      if (prev_state != null) {
+        addEdge(prev_state.id, id, search);
+      } else {
+        prev_state = new StateInformation();
+      }
+      addNode(prev_state);
+      prev_state.reset(id, has_next, is_new);
+    } catch (IOException e) {}
+  }
+
+  @Override
+  public void stateRestored (Search search) {
+    prev_state.reset(search.getStateId(), false, false);
+  }
+
+  @Override
+  public void stateProcessed (Search search) {
+   // nothing to do
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    try {
+      addNode(prev_state);
+      prev_state.reset(search.getStateId(), false, false);
+      if (format==DOT_FORMAT) {
+        graph.write("/* searchBacktracked(" + prev_state + ") */");
+        graph.newLine();
+      }
+    } catch (IOException e) {}
+  }
+
+  @Override
+  public void searchConstraintHit(Search search) {
+    try {
+      if (format==DOT_FORMAT) {
+        graph.write("/* searchConstraintHit(" + search.getStateId() + ") */");
+        graph.newLine();
+      }
+    } catch (IOException e) {}
+  }
+
+  private String getErrorMsg(Search search) {
+    List<Error> errs = search.getErrors();
+    if (errs.isEmpty()) {
+      return null;
+    } else {
+      return errs.get(0).getDescription();
+    }
+  }
+
+  @Override
+  public void propertyViolated(Search search) {
+       try {
+         prev_state.error = getErrorMsg(search);
+         if (format==DOT_FORMAT) {
+           graph.write("/* propertyViolated(" + search.getStateId() + ") */");
+           graph.newLine();
+         }
+       } catch (IOException e) {}
+  }
+
+  /**
+   * Put the header for the graph into the file.
+   */
+  private void beginGraph() throws IOException {
+       graph = new BufferedWriter(new FileWriter(out_filename));
+       if (format==GDF_FORMAT) {
+         graph.write("nodedef>name,label,style,color");
+       } else { // dot
+         graph.write("digraph jpf_state_space {");
+       }
+       graph.newLine();
+  }
+
+  /**
+   * In the case of the DOT graph it is just adding the final "}" at the end.
+   * In the case of GPF format we must output edge definition and all the
+   * edges that we have found.
+   */
+  private void endGraph() throws IOException {
+    if(prev_state != null)
+      addNode(prev_state);
+    if (format==GDF_FORMAT) {
+      graph.write("edgedef>node1,node2,label,labelvisible,directed,thread INT");
+      graph.newLine();
+
+      // Output all the edges that we have accumulated so far
+      int size=gdfEdges.size();
+         for (int i=0; i<size; i++) {
+               graph.write(gdfEdges.get(i));
+           graph.newLine();
+         }
+    } else {
+      graph.write("}");
+      graph.newLine();
+    }
+    graph.close();
+  }
+
+  /**
+   * Return the string that will be used to label this state for the user.
+   */
+  private String makeDotLabel(Search state, int my_id) {
+    Transition trans = state.getTransition();
+    if (trans == null) {
+      return "-init-";
+    }
+    Step last_trans_step = trans.getLastStep();
+    if (last_trans_step == null) {
+      return "?";
+    }
+
+    StringBuilder result = new StringBuilder();
+
+    if (transition_numbers) {
+      result.append(my_id);
+      result.append("\\n");
+    }
+
+    int thread = trans.getThreadIndex();
+
+    result.append("Thd");
+    result.append(thread);
+    result.append(':');
+    result.append(last_trans_step.toString());
+
+    if (show_source) {
+      String source_line=last_trans_step.getLineString();
+      if ((source_line != null) && !source_line.equals("")) {
+        result.append("\\n");
+
+        StringBuilder sb=new StringBuilder(source_line);
+
+        // We need to precede the dot-specific special characters which appear
+        // in the Java source line, such as ']' and '"', with the '\' escape
+        // characters and also to remove any new lines.
+
+        replaceString(sb, "\n", "");
+        replaceString(sb, "]", "\\]");
+        replaceString(sb, "\"", "\\\"");
+        result.append(sb.toString());
+      }
+    }
+
+    return result.toString();
+  }
+
+  /**
+   * Return the string that will be used to label this state in the GDF graph.
+   */
+  private String makeGdfLabel(Search state, int my_id) {
+    Transition trans = state.getTransition();
+    if (trans == null) {
+         return "-init-";
+       }
+
+       StringBuilder result = new StringBuilder();
+
+       if (transition_numbers) {
+         result.append(my_id);
+         result.append(':');
+       }
+
+       Step last_trans_step = trans.getLastStep();
+       result.append(last_trans_step.toString());
+
+       if (show_source) {
+         String source_line=last_trans_step.getLineString();
+         if ((source_line != null) && !source_line.equals("")) {
+
+           // We need to deal with gdf-specific special characters which couls appear
+           // in the Java source line, such as '"'.
+           result.append(source_line);
+               convertGdfSpecial(result);
+         }
+    }
+       return result.toString();
+  }
+
+  /**
+   * Locates and replaces all occurrences of a given string with another given
+   * string in an original string buffer. There seems to be a bug in Java
+   * String's replaceAll() method which does not allow us to use it to replace
+   * some special chars so here we use StringBuilder's replace method to do
+   * this.
+   * @param original The original string builder.
+   * @param from The replaced string.
+   * @param to The replacing string.
+   * @return The original string builder with the substring replaced
+   *         throughout.
+   */
+  private StringBuilder replaceString(StringBuilder original,
+                                      String from,
+                                      String to) {
+    int indexOfReplaced=0, lastIndexOfReplaced=0;
+    while (indexOfReplaced!=-1) {
+      indexOfReplaced=original.indexOf(from,lastIndexOfReplaced);
+      if (indexOfReplaced!=-1) {
+       original.replace(indexOfReplaced, indexOfReplaced+1, to);
+        lastIndexOfReplaced=indexOfReplaced+to.length();
+      }
+    }
+    return original;
+  }
+
+  /**
+   * Locates and replaces all occurrences of a given string with another given
+   * string in an original string buffer.
+   * @param original The original string buffer.
+   * @param from The replaced string.
+   * @param to The replacing string.
+   * @return The original string buffer with the sub-string replaced
+   *         throughout.
+   */
+  private String replaceString(String original, String from, String to) {
+       if ((original!=null) && (from!=null) && (to!=null)) {
+      return replaceString(new StringBuilder(original), from, to).toString();
+       } else {
+      return original;
+       }
+  }
+
+  /**
+   * Add a new node to the graph with the relevant properties.
+   */
+  private void addNode(StateInformation state) throws IOException {
+    if (state.is_new) {
+      if (format==GDF_FORMAT) {
+        graph.write("st" + state.id + ",\"" + state.id);
+        if (state.error != null) {
+          graph.write(":" + state.error);
+        }
+        graph.write("\","+state_node_style);
+        if (state.error != null) {
+            graph.write(",red");
+          } else if (state.has_next) {
+            graph.write(",black");
+          } else {
+            graph.write(",green");
+          }
+      } else { // The dot version
+        graph.write("  st" + state.id + " [label=\"" + state.id);
+        if (state.error != null) {
+          graph.write(":" + state.error);
+        }
+        graph.write("\",shape=");
+        if (state.error != null) {
+          graph.write("diamond,color=red");
+        } else if (state.has_next) {
+          graph.write("circle,color=black");
+        } else {
+          graph.write("egg,color=green");
+        }
+        graph.write("];");
+      }
+      graph.newLine();
+    }
+  }
+
+  private static class StateInformation {
+    public StateInformation() {}
+    public void reset(int id, boolean has_next, boolean is_new) {
+      this.id = id;
+      this.has_next = has_next;
+      this.error = null;
+      this.is_new = is_new;
+    }
+    int id = -1;
+    boolean has_next = true;
+    String error = null;
+    boolean is_new = false;
+  }
+
+  /**
+   * Creates an GDF edge string.
+   */
+  private String makeGdfEdgeString(String from_id,
+                                          String to_id,
+                                                                  String label,
+                                                                  int thread) {
+       StringBuilder sb=new StringBuilder(from_id);
+       sb.append(',').append(to_id).append(',').append('\"');
+       if ((label!=null) && (!"".equals(label))) {
+               sb.append(label);
+       } else {
+               sb.append('-');
+       }
+       sb.append('\"').append(',').append(labelvisible).append(',').append(true).
+       append(',').append(thread);
+       replaceString(sb, "\n", "");
+       return sb.toString();
+  }
+
+  /**
+   * GUESS cannot deal with '\n' chars, so remove them. Also convert all '"'
+   * characters to "''".
+   * @param str The string to perform the conversion on.
+   * @return The converted string.
+   */
+  private String convertGdfSpecial(String str) {
+       if ((str==null) || "".equals(str)) return "";
+
+       StringBuilder sb=new StringBuilder(str);
+       convertGdfSpecial(sb);
+       return sb.toString();
+  }
+
+  /**
+   * GUESS cannot deal with '\n' chars, so replace them with a space. Also
+   * convert all '"' characters to "''".
+   * @param sb The string buffer to perform the conversion on.
+   */
+  private void convertGdfSpecial(StringBuilder sb) {
+       replaceString(sb, "\"", "\'\'");
+       replaceString(sb, "\n", " ");
+  }
+
+  /**
+   * Create an edge in the graph file from old_id to new_id.
+   */
+  private void addEdge(int old_id, int new_id, Search state) throws IOException {
+    int my_id = edge_id++;
+    if (format==GDF_FORMAT) {
+      Transition trans=state.getTransition();
+      int thread = trans.getThreadIndex();
+
+      // edgedef>node1,node2,label,labelvisible,directed,thread INT
+
+      // Old State -> Transition - labeled with the source info if any.
+      gdfEdges.add(
+               makeGdfEdgeString("st"+old_id, "tr"+my_id,
+                                         makeGdfLabel(state, my_id),
+                                         thread));
+
+      // Transition node: name,label,style,color
+      graph.write("tr" + my_id + ",\"" +my_id+"\","+transition_node_style);
+
+      graph.newLine();
+      // Transition -> New State - labeled with the last output if any.
+
+      String lastOutputLabel=
+       replaceString(convertGdfSpecial(trans.getOutput()), "\"", "\'\'");
+      gdfEdges.add(
+       makeGdfEdgeString("tr"+my_id, "st"+new_id, lastOutputLabel, thread));
+    } else { // DOT
+      graph.write("  st" + old_id + " -> tr" + my_id + ";");
+      graph.newLine();
+      graph.write("  tr" + my_id + " [label=\"" + makeDotLabel(state, my_id) +
+                  "\",shape=box]");
+      graph.newLine();
+      graph.write("  tr" + my_id + " -> st" + new_id + ";");
+    }
+  }
+
+  /**
+   * Show the usage message.
+   */
+  static void showUsage() {
+    System.out
+        .println("Usage: \"java [<vm-options>] gov.nasa.jpf.tools.StateSpaceDot [<graph-options>] [<jpf-options-and-args>]");
+    System.out.println("  <graph-options> : ");
+    System.out.println("    -gdf:                Generate the graph in GDF format. The default is DOT.");
+    System.out.println("    -transition-numbers: Include transition numbers in transition labels.");
+    System.out.println("    -show-source:        Include source lines in transition labels.");
+    System.out.println("    -labelvisible:       Indicates if the label on the transitions is visible (used only with the -gdf option)");
+    System.out.println("    -help:               Prints this help information and stops.");
+    System.out.println("  <jpf-options-and-args>:");
+    System.out.println("    Options and command line arguments passed directly to JPF.");
+    System.out.println("  Note: With -gdf option transition edges could also include program output ");
+    System.out.println("  but in order to enable this JPF's vm.path_output option must be set to ");
+    System.out.println("  true.");
+  }
+
+  void filterArgs (String[] args) {
+    for (int i=0; i<args.length; i++) {
+      if (args[i] != null){
+        String arg = args[i];
+        if ("-transition-numbers".equals(arg)) {
+          transition_numbers = true;
+          args[i] = null;
+        } else if ("-show-source".equals(arg)) {
+          show_source = true;
+          args[i] = null;
+        } else if ("-gdf".equals(arg)) {
+          format=GDF_FORMAT;
+          out_filename=OUT_FILENAME_NO_EXT+"."+GDF_EXT;
+          args[i] = null;
+        } else if ("-labelvisible".equals(arg)) {
+          labelvisible=true;
+          args[i] = null;
+        } else if  ("-help".equals(args[i])) {
+          showUsage();
+          helpRequested=true;
+        }
+      }
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/StateTracker.java b/src/main/gov/nasa/jpf/listener/StateTracker.java
new file mode 100644 (file)
index 0000000..181c456
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+
+import java.io.PrintWriter;
+
+/**
+ * simple tool to log state changes
+ */
+public class StateTracker extends ListenerAdapter {
+
+  private final PrintWriter out;
+  private final int logPeriod;
+  volatile private String operation;
+  volatile private String detail;
+  volatile private int depth;
+  volatile private int id;
+
+  public StateTracker (Config conf, JPF jpf) {
+    out = new PrintWriter(System.out, true);
+    logPeriod = conf.getInt("jpf.state_tracker.log_period", 0);
+    Runnable task = new Runnable() {@Override
+       public void run() {logger();}};
+    Thread thread = new Thread(task);
+    thread.setDaemon(true);
+    thread.setName("StateTracker Logger");
+    thread.start();
+  }
+
+  private void logger() {
+    StringBuilder buffer = new StringBuilder();
+
+    buffer.append("----------------------------------- [");
+    int len = buffer.length();
+
+    while (true) {
+      try {
+        Thread.sleep(logPeriod);
+      } catch (InterruptedException e) {
+        e.printStackTrace();
+      }
+
+      buffer.append(depth);
+      buffer.append(']');
+      buffer.append(operation);
+      buffer.append(": ");
+      buffer.append(id);
+
+      if (detail != null) {
+        buffer.append(' ');
+        buffer.append(detail);
+      }
+
+      out.println(buffer.toString());
+
+      buffer.setLength(len);
+    }
+  }
+
+  @Override
+  public void stateRestored(Search search) {
+    id = search.getStateId();
+    depth = search.getDepth();
+    operation = "restored";
+    detail = null;
+  }
+
+  //--- the ones we are interested in
+  @Override
+  public void searchStarted(Search search) {
+    out.println("----------------------------------- search started");
+  }
+
+  @Override
+  public void stateAdvanced(Search search) {
+    id = search.getStateId();
+    depth = search.getDepth();
+    operation = "forward";
+    if (search.isNewState()) {
+      detail = "new";
+    } else {
+      detail = "visited";
+    }
+
+    if (search.isEndState()) {
+      detail += " end";
+    }
+  }
+
+  @Override
+  public void stateBacktracked(Search search) {
+    id = search.getStateId();
+    depth = search.getDepth();
+    operation = "backtrack";
+    detail = null;
+  }
+
+  @Override
+  public void searchFinished(Search search) {
+    out.println("----------------------------------- search finished");
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/listener/StopWatchFuzzer.java b/src/main/gov/nasa/jpf/listener/StopWatchFuzzer.java
new file mode 100644 (file)
index 0000000..b9c9feb
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.LCMP;
+import gov.nasa.jpf.jvm.bytecode.LSUB;
+import gov.nasa.jpf.jvm.bytecode.NATIVERETURN;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+
+
+/**
+ * a listener that is used to explore all paths from a time-value comparison.
+ * 
+ * This works by creating a CG on LCMP instructions that involve 
+ * System.currentTimeMillis() obtained values, which are attribute tagged
+ * upon return from the native method, propagated on LSUB (duration computation),
+ * and finally used for LCMP interception if the tag attributes are present
+ * 
+ *   long t1 = System.currentTimeMillis();
+ *   doSomeComputation();
+ *   long t2 = System.currentTimeMillis();
+ *   if (t2 - t1 <= MAX_DURATION){
+ *     // all fine branch
+ *   } else {
+ *     // catastrophic failure branch
+ *   }
+ * 
+ * which boils down to a bytecode pattern like
+ * 
+ *   invokestatic #2; // System.currentTimeMillis()   <<< tag result with TimeVal attr
+ *   ..
+ *   lsub  <<< propagate if any of the operands has a TimeVal attr
+ *   ..
+ *   lcmp <<< register CG and skip if any of the operands has TimeVal attr
+ * 
+ */
+public class StopWatchFuzzer extends ListenerAdapter {
+  
+  MethodInfo miCurrentTimeMillis;
+  
+  static class TimeVal {
+    // attribute for values obtained from System.currentTimeMillis()
+  }
+  static TimeVal timeValAttr = new TimeVal(); // we don't need separate instances
+  
+  static String CG_ID = "LCMP_fuzzer";
+
+  @Override
+  public void classLoaded(VM vm, ClassInfo ci){
+    if (miCurrentTimeMillis == null){
+      if (ci.getName().equals("java.lang.System")) {
+        miCurrentTimeMillis = ci.getMethod("currentTimeMillis()J", false); // its got to be there
+      }
+    }
+  }
+  
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+
+    if (executedInsn instanceof NATIVERETURN){
+      if (executedInsn.isCompleted(ti)){
+        if (((NATIVERETURN)executedInsn).getMethodInfo() == miCurrentTimeMillis){
+          // the two top operand slots hold the 'long' time value
+          StackFrame frame = ti.getModifiableTopFrame();
+          frame.addLongOperandAttr( timeValAttr);
+        }
+      }
+    }
+  }
+  
+  @Override
+  public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute){
+
+    if (insnToExecute instanceof LSUB){  // propagate TimeVal attrs
+      StackFrame frame = ti.getTopFrame();
+      // check if any of the operands have TimeVal attributes
+      // attributes are stored on the first slot of a long val
+      if (frame.hasOperandAttr(1, TimeVal.class) || frame.hasOperandAttr(3, TimeVal.class)){      
+        // enter insn (this pops the 4 top operand slots and pushes the long result
+        ti.skipInstruction(insnToExecute.execute(ti));
+      
+        // propagate TimeVal attr, now we need a modifiable frame
+        frame = ti.getModifiableTopFrame();
+        frame.addLongOperandAttr(timeValAttr);
+      }
+       
+    } else if (insnToExecute instanceof LCMP){ // create and set CG if operand has TimeVal attr
+      
+      if (!ti.isFirstStepInsn()){ // this is the first time we see this insn
+        StackFrame frame = ti.getTopFrame();
+        
+        if (frame.hasOperandAttr(1, TimeVal.class) || frame.hasOperandAttr(3, TimeVal.class)){
+          IntChoiceFromSet cg = new IntChoiceFromSet( CG_ID, -1, 0, 1);
+          if (vm.setNextChoiceGenerator(cg)){
+            ti.skipInstruction(insnToExecute); // reexecute after breaking the transition
+          }
+        }
+        
+      } else { // it is the beginning of a transition, push the choice and proceed
+        IntChoiceFromSet cg = vm.getCurrentChoiceGenerator(CG_ID, IntChoiceFromSet.class);
+        if (cg != null){
+          int choice = cg.getNextChoice();
+          StackFrame frame = ti.getModifiableTopFrame();
+          
+          // pop the operands 
+          frame.popLong();
+          frame.popLong();
+          
+          frame.push(choice);
+          
+          ti.skipInstruction(insnToExecute.getNext());
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/TraceStorer.java b/src/main/gov/nasa/jpf/listener/TraceStorer.java
new file mode 100644 (file)
index 0000000..69df64d
--- /dev/null
@@ -0,0 +1,164 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * tool to save traces upon various conditions like
+ *  - property violation
+ *  - call of a certain method
+ *  - reaching a certain search depth
+ *  - creating a certain thread
+ */
+public class TraceStorer extends ListenerAdapter {
+
+  int nTrace = 1; 
+
+  String traceFileName;
+  
+  // do we store to the same file? (i.e. overwrite previously stored traces)
+  // if set to 'true', store all traces (in <traceFileName>.n)
+  boolean storeMultiple;
+  
+  // do we want to terminate after first store, even if it's triggered by a
+  // property violation?
+  boolean terminateOnStore;
+  
+  boolean storeOnConstraintHit;
+  
+  // search depth at what we store the tace 
+  int storeDepth;
+  
+  // calls that should trigger a store
+  StringSetMatcher storeCalls;
+  
+  // thread starts that should trigger a store
+  StringSetMatcher storeThreads;
+  
+  // do we want verbose output
+  boolean verbose;
+  
+  Search search;
+  VM vm;
+  
+  public TraceStorer (Config config, JPF jpf){
+    
+    traceFileName = config.getString("trace.file", "trace");
+    storeMultiple = config.getBoolean("trace.multiple", false);    
+    storeDepth = config.getInt("trace.depth", Integer.MAX_VALUE);
+    verbose = config.getBoolean("trace.verbose", false);
+    
+    terminateOnStore = config.getBoolean("trace.terminate", false);
+    storeOnConstraintHit = config.getBoolean("trace.store_constraint", false);
+    
+    storeCalls = StringSetMatcher.getNonEmpty(config.getStringArray("trace.store_calls"));
+    storeThreads = StringSetMatcher.getNonEmpty(config.getStringArray("trace.store_threads"));
+    
+    vm = jpf.getVM();
+    search = jpf.getSearch();
+  }
+  
+  void storeTrace(String reason) {
+    String fname = traceFileName;
+    
+    if (storeMultiple){
+      fname = fname  + '.' + nTrace++;
+    }
+    
+    vm.storeTrace(fname, reason, verbose); // <2do> maybe some comment would be in order
+  }
+  
+  @Override
+  public void propertyViolated (Search search){
+    // Ok, this is unconditional
+    storeTrace("violated property: " + search.getLastError().getDetails());
+    
+    // no need to terminate (and we don't want to interfere with search.multiple_errors)
+  }
+  @Override
+  public void stateAdvanced (Search search){
+    if (search.getDepth() == storeDepth){
+      storeTrace("search depth reached: " + storeDepth);
+      checkSearchTermination();
+    }
+  }
+  
+  @Override
+  public void searchConstraintHit (Search search){
+    if (storeOnConstraintHit){
+      storeTrace("search constraint hit: " + search.getLastSearchConstraint());      
+    }
+  }
+  
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+    if (storeCalls != null){
+      if (executedInsn instanceof JVMInvokeInstruction) {
+        JVMInvokeInstruction iinsn = (JVMInvokeInstruction)executedInsn;
+        String clsName = iinsn.getInvokedMethodClassName();
+        String mthName = iinsn.getInvokedMethodName();
+        String mn = clsName + '.' + mthName;
+        
+        if (storeCalls.matchesAny(mn)){
+          storeTrace("call: " + mn);
+          checkVMTermination(ti);
+        }
+      }
+    }
+  }
+  
+  @Override
+  public void threadStarted(VM vm, ThreadInfo ti) {
+    if (storeThreads != null){
+      String tname = ti.getName();
+      if (storeThreads.matchesAny( tname)){
+        storeTrace("thread started: " + tname);
+        checkVMTermination(ti);
+      }
+    } 
+  }
+
+  boolean checkVMTermination(ThreadInfo ti) {
+    if (terminateOnStore){
+      ti.breakTransition("storeTraceTermination");
+      search.terminate();
+      return true;
+    }
+    
+    return false;
+  }
+  
+  boolean checkSearchTermination() {
+    if (terminateOnStore){
+      search.terminate();
+      return true;
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/VarRecorder.java b/src/main/gov/nasa/jpf/listener/VarRecorder.java
new file mode 100644 (file)
index 0000000..8727943
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMArrayElementInstruction;
+import gov.nasa.jpf.jvm.bytecode.ArrayLoadInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMLocalVariableInstruction;
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.Step;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+import java.util.HashMap;
+
+/**
+ * Simple listener tool to record the values of variables as they are accessed.
+ * Records the information in Step.setComment() so that when the trace is
+ * written the values can be written too.
+ */
+public class VarRecorder extends ListenerAdapter {
+
+  private final HashMap<ThreadInfo, String> pendingComment = new HashMap<ThreadInfo, String>();
+
+  private final StringSetMatcher includes;
+  private final StringSetMatcher excludes;
+  private final boolean recordFields;
+  private final boolean recordLocals;
+  private final boolean recordArrays;
+
+  private ClassInfo lastClass;
+  private boolean recordClass;
+
+  public VarRecorder(Config config) {
+    includes = StringSetMatcher.getNonEmpty(config.getStringArray("var_recorder.include"));
+    excludes = StringSetMatcher.getNonEmpty(config.getStringArray("var_recorder.exclude"));
+    recordFields = config.getBoolean("var_recorder.fields", true);
+    recordLocals = config.getBoolean("var_recorder.locals", true);
+    recordArrays = config.getBoolean("var_recorder.arrays", true);
+  }
+
+  @Override
+  public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute) {
+    String name, value;
+    byte type;
+
+    if (!canRecord(vm, insnToExecute))
+      return;
+
+    if (!(insnToExecute instanceof StoreInstruction))
+      if (!(insnToExecute instanceof ArrayLoadInstruction))
+        return;
+
+    type = getType(ti, insnToExecute);
+    name = getName(ti, insnToExecute, type);
+
+    if (insnToExecute instanceof ArrayLoadInstruction) {
+      setComment(vm, ti, name, "", "", true);
+      saveVariableType(ti, type);
+    } else {
+      value = getValue(ti, insnToExecute, type);
+      setComment(vm, ti, name, " <- ", value, true);
+    }
+  }
+
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    String name, value;
+    byte type;
+
+    if (!canRecord(vm, executedInsn))
+      return;
+
+    if (executedInsn instanceof StoreInstruction) {
+      name = pendingComment.remove(ti);
+      setComment(vm, ti, name, "", "", false);
+      return;
+    }
+
+    type  = getType(ti, executedInsn);
+    value = getValue(ti, executedInsn, type);
+
+    if (executedInsn instanceof ArrayLoadInstruction)
+      name = pendingComment.remove(ti);
+    else
+      name = getName(ti, executedInsn, type);
+
+    if (isArrayReference(vm, ti))
+      saveVariableName(ti, name);
+    else
+      saveVariableType(ti, type);
+
+    setComment(vm, ti, name, " -> ", value, false);
+  }
+
+  private final void setComment(VM vm, ThreadInfo ti, String name, String operator, String value, boolean pending) {
+    Step step;
+    String comment;
+
+    if (name == null)
+      return;
+
+    if (value == null)
+      return;
+
+    comment = name + operator + value;
+
+    if (pending) {
+      pendingComment.put(ti, comment);
+    } else {
+      step = vm.getLastStep();
+      step.setComment(name + operator + value);
+    }
+  }
+
+  private final boolean canRecord(VM vm, Instruction inst) {
+    ClassInfo ci;
+    MethodInfo mi;
+
+    if (vm.getLastStep() == null)
+      return(false);
+
+    if (!(inst instanceof LocalVariableInstruction))
+      if (!(inst instanceof JVMArrayElementInstruction))
+        return(false);
+
+    mi   = inst.getMethodInfo();
+    if (mi == null)
+      return(false);
+
+    ci = mi.getClassInfo();
+    if (ci == null)
+      return(false);
+
+    if (lastClass != ci) {
+      lastClass   = ci;
+      recordClass = StringSetMatcher.isMatch(ci.getName(), includes, excludes);
+    }
+
+    return(recordClass);
+  }
+
+  // <2do> general purpose listeners should not use anonymous attribute types such as String
+  
+  private final void saveVariableName(ThreadInfo ti, String name) {
+    StackFrame frame = ti.getModifiableTopFrame();
+    frame.addOperandAttr(name);
+  }
+
+  private final void saveVariableType(ThreadInfo ti, byte type) {
+    StackFrame frame;
+    String str;
+
+    frame = ti.getModifiableTopFrame();
+    if (frame.getTopPos() < 0) {
+      return;
+    }
+
+    str = encodeType(type);
+    frame.addOperandAttr(str);
+  }
+
+  private final boolean isArrayReference(VM vm, ThreadInfo ti) {
+    StackFrame frame = ti.getTopFrame();
+
+    if (frame.getTopPos() < 0) {
+      return(false);
+    }
+
+    if (!frame.isOperandRef()) {
+      return(false);
+    }
+
+    int objRef = frame.peek();
+    if (objRef == MJIEnv.NULL) {
+      return(false);
+    }
+
+    ElementInfo ei = ti.getElementInfo(objRef);
+    if (ei == null) {
+      return(false);
+    }
+
+    return(ei.isArray());
+  }
+
+  private byte getType(ThreadInfo ti, Instruction inst) {
+    StackFrame frame;
+    FieldInfo fi;
+    String type;
+
+    frame = ti.getTopFrame();
+    if ((frame.getTopPos() >= 0) && (frame.isOperandRef())) {
+      return (Types.T_REFERENCE);
+    }
+
+    type = null;
+
+    if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction))
+            || ((recordFields) && (inst instanceof JVMFieldInstruction))) {
+      if (inst instanceof JVMLocalVariableInstruction) {
+        type = ((JVMLocalVariableInstruction) inst).getLocalVariableType();
+      } else {
+        fi = ((JVMFieldInstruction) inst).getFieldInfo();
+        type = fi.getType();
+      }
+    }
+
+    if ((recordArrays) && (inst instanceof JVMArrayElementInstruction)) {
+      return (getTypeFromInstruction(inst));
+    }
+
+    if (type == null) {
+      return (Types.T_VOID);
+    }
+
+    return (decodeType(type));
+  }
+
+  private final static byte getTypeFromInstruction(Instruction inst) {
+    if (inst instanceof JVMArrayElementInstruction)
+      return(getTypeFromInstruction((JVMArrayElementInstruction) inst));
+
+    return(Types.T_VOID);
+  }
+
+  private final static byte getTypeFromInstruction(JVMArrayElementInstruction inst) {
+    String name;
+
+    name = inst.getClass().getName();
+    name = name.substring(name.lastIndexOf('.') + 1);
+
+    switch (name.charAt(0)) {
+      case 'A': return(Types.T_REFERENCE);
+      case 'B': return(Types.T_BYTE);      // Could be a boolean but it is better to assume a byte.
+      case 'C': return(Types.T_CHAR);
+      case 'F': return(Types.T_FLOAT);
+      case 'I': return(Types.T_INT);
+      case 'S': return(Types.T_SHORT);
+      case 'D': return(Types.T_DOUBLE);
+      case 'L': return(Types.T_LONG);
+    }
+
+    return(Types.T_VOID);
+  }
+
+  private final static String encodeType(byte type) {
+    switch (type) {
+      case Types.T_BYTE:    return("B");
+      case Types.T_CHAR:    return("C");
+      case Types.T_DOUBLE:  return("D");
+      case Types.T_FLOAT:   return("F");
+      case Types.T_INT:     return("I");
+      case Types.T_LONG:    return("J");
+      case Types.T_REFERENCE:  return("L");
+      case Types.T_SHORT:   return("S");
+      case Types.T_VOID:    return("V");
+      case Types.T_BOOLEAN: return("Z");
+      case Types.T_ARRAY:   return("[");
+    }
+
+    return("?");
+  }
+
+  private final static byte decodeType(String type) {
+    if (type.charAt(0) == '?'){
+      return(Types.T_REFERENCE);
+    } else {
+      return Types.getBuiltinType(type);
+    }
+  }
+
+  private String getName(ThreadInfo ti, Instruction inst, byte type) {
+    String name;
+    int index;
+    boolean store;
+
+    if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction)) ||
+        ((recordFields) && (inst instanceof JVMFieldInstruction))) {
+      name = ((LocalVariableInstruction) inst).getVariableId();
+      name = name.substring(name.lastIndexOf('.') + 1);
+
+      return(name);
+    }
+
+    if ((recordArrays) && (inst instanceof JVMArrayElementInstruction)) {
+      store  = inst instanceof StoreInstruction;
+      name   = getArrayName(ti, type, store);
+      index  = getArrayIndex(ti, type, store);
+      return(name + '[' + index + ']');
+    }
+
+    return(null);
+  }
+
+  private String getValue(ThreadInfo ti, Instruction inst, byte type) {
+    StackFrame frame;
+    int lo, hi;
+
+    frame = ti.getTopFrame();
+
+    if (((recordLocals) && (inst instanceof JVMLocalVariableInstruction)) ||
+        ((recordFields) && (inst instanceof JVMFieldInstruction)))
+    {
+       if (frame.getTopPos() < 0)
+         return(null);
+
+       lo = frame.peek();
+       hi = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
+
+       return(decodeValue(type, lo, hi));
+    }
+
+    if ((recordArrays) && (inst instanceof JVMArrayElementInstruction))
+      return(getArrayValue(ti, type));
+
+    return(null);
+  }
+
+  private String getArrayName(ThreadInfo ti, byte type, boolean store) {
+    String attr;
+    int offset;
+
+    offset = calcOffset(type, store) + 1;
+    // <2do> String is really not a good attribute type to retrieve!
+    StackFrame frame = ti.getTopFrame();
+    attr   = frame.getOperandAttr( offset, String.class); 
+
+    if (attr != null) {
+      return(attr);
+    }
+
+    return("?");
+  }
+
+  private int getArrayIndex(ThreadInfo ti, byte type, boolean store) {
+    int offset;
+
+    offset = calcOffset(type, store);
+
+    return(ti.getTopFrame().peek(offset));
+  }
+
+  private final static int calcOffset(byte type, boolean store) {
+    if (!store)
+      return(0);
+
+    return(Types.getTypeSize(type));
+  }
+
+  private String getArrayValue(ThreadInfo ti, byte type) {
+    StackFrame frame;
+    int lo, hi;
+
+    frame = ti.getTopFrame();
+    lo    = frame.peek();
+    hi    = frame.getTopPos() >= 1 ? frame.peek(1) : 0;
+
+    return(decodeValue(type, lo, hi));
+  }
+
+  private final static String decodeValue(byte type, int lo, int hi) {
+    switch (type) {
+      case Types.T_ARRAY:   return(null);
+      case Types.T_VOID:    return(null);
+
+      case Types.T_BOOLEAN: return(String.valueOf(Types.intToBoolean(lo)));
+      case Types.T_BYTE:    return(String.valueOf(lo));
+      case Types.T_CHAR:    return(String.valueOf((char) lo));
+      case Types.T_DOUBLE:  return(String.valueOf(Types.intsToDouble(lo, hi)));
+      case Types.T_FLOAT:   return(String.valueOf(Types.intToFloat(lo)));
+      case Types.T_INT:     return(String.valueOf(lo));
+      case Types.T_LONG:    return(String.valueOf(Types.intsToLong(lo, hi)));
+      case Types.T_SHORT:   return(String.valueOf(lo));
+
+      case Types.T_REFERENCE:
+        ElementInfo ei = VM.getVM().getHeap().get(lo);
+        if (ei == null)
+          return(null);
+
+        ClassInfo ci = ei.getClassInfo();
+        if (ci == null)
+          return(null);
+
+        if (ci.getName().equals("java.lang.String"))
+          return('"' + ei.asString() + '"');
+
+        return(ei.toString());
+
+      default:
+        System.err.println("Unknown type: " + type);
+        return(null);
+     }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/listener/VarTracker.java b/src/main/gov/nasa/jpf/listener/VarTracker.java
new file mode 100644 (file)
index 0000000..9615e5a
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.listener;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.ALOAD;
+import gov.nasa.jpf.jvm.bytecode.ArrayStoreInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.GETFIELD;
+import gov.nasa.jpf.jvm.bytecode.GETSTATIC;
+import gov.nasa.jpf.vm.bytecode.ReadInstruction;
+import gov.nasa.jpf.vm.bytecode.StoreInstruction;
+import gov.nasa.jpf.vm.bytecode.LocalVariableInstruction;
+import gov.nasa.jpf.report.ConsolePublisher;
+import gov.nasa.jpf.report.Publisher;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.MethodSpec;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.bytecode.WriteInstruction;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * simple listener tool to find out which variables (locals and fields) are
+ * changed how often and from where. This should give a good idea if a state
+ * space blows up because of some counter/timer vars, and where to apply the
+ * necessary abstractions to close/shrink it
+ *
+ */
+public class VarTracker extends ListenerAdapter {
+
+  // our matchers to determine which variables we have to report
+  StringSetMatcher includeVars = null; //  means all
+  StringSetMatcher excludeVars = null; //  means none
+
+  // filter methods from which access happens
+  MethodSpec methodSpec;
+
+  int maxVars; // maximum number of variables shown
+  
+  ArrayList<VarChange> queue = new ArrayList<VarChange>();
+  ThreadInfo lastThread;
+  HashMap<String, VarStat> stat = new HashMap<String, VarStat>();
+  int nStates = 0;
+  int maxDepth;
+
+
+  public VarTracker (Config config, JPF jpf){
+
+    includeVars = StringSetMatcher.getNonEmpty(config.getStringArray("vt.include"));
+    excludeVars = StringSetMatcher.getNonEmpty(config.getStringArray("vt.exclude",
+            new String[] {"java.*", "javax.*"} ));
+
+    maxVars = config.getInt("vt.max_vars", 25);
+
+    methodSpec = MethodSpec.createMethodSpec(config.getString("vt.methods", "!java.*.*"));
+
+    jpf.addPublisherExtension(ConsolePublisher.class, this);
+  }
+
+  @Override
+  public void publishPropertyViolation (Publisher publisher) {
+    PrintWriter pw = publisher.getOut();
+    publisher.publishTopicStart("field access ");
+
+    report(pw);
+  }
+
+  void print (PrintWriter pw, int n, int length) {
+    String s = Integer.toString(n);
+    int l = length - s.length();
+    
+    for (int i=0; i<l; i++) {
+      pw.print(' ');
+    }
+    
+    pw.print(s);
+  }
+  
+  void report (PrintWriter pw) {
+    pw.println();
+    pw.println("      change    variable");
+    pw.println("---------------------------------------");
+    
+    Collection<VarStat> values = stat.values();
+    List<VarStat> valueList = new ArrayList<VarStat>();
+    valueList.addAll(values);
+    Collections.sort(valueList);
+
+    int n = 0;
+    for (VarStat s : valueList) {
+      
+      if (n++ > maxVars) {
+        break;
+      }
+      
+      print(pw, s.nChanges, 12);
+      pw.print("    ");
+      pw.println(s.id);
+    }
+  }
+  
+  @Override
+  public void stateAdvanced(Search search) {
+    
+    if (search.isNewState()) { // don't count twice
+      int stateId = search.getStateId();
+      nStates++;
+      int depth = search.getDepth();
+      if (depth > maxDepth) maxDepth = depth;
+      
+      if (!queue.isEmpty()) {
+        for (Iterator<VarChange> it = queue.iterator(); it.hasNext(); ){
+          VarChange change = it.next();
+            String id = change.getVariableId();
+            VarStat s = stat.get(id);
+            if (s == null) {
+              s = new VarStat(id, stateId);
+              stat.put(id, s);
+            } else {
+              // no good - we should filter during reg (think of large vectors or loop indices)
+              if (s.lastState != stateId) { // count only once per new state
+                s.nChanges++;
+                s.lastState = stateId;
+              }
+            }
+        }
+      }
+    }
+
+    queue.clear();
+  }
+
+  // <2do> - general purpose listeners should not use types such as String for storing
+  // attributes, there is no good way to make sure you retrieve your own attributes
+  @Override
+  public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+    String varId;
+
+    if (executedInsn instanceof ALOAD) {
+      // a little extra work - we need to keep track of variable names, because
+      // we cannot easily retrieve them in a subsequent xASTORE, which follows
+      // a pattern like:  ..GETFIELD.. some-stack-operations .. xASTORE
+      StackFrame frame = ti.getTopFrame();
+      int objRef = frame.peek();
+      if (objRef != MJIEnv.NULL) {
+        ElementInfo ei = ti.getElementInfo(objRef);
+        if (ei.isArray()) {
+          varId = ((LocalVariableInstruction) executedInsn).getVariableId();
+
+          // <2do> unfortunately, we can't filter here because we don't know yet
+          // how the array ref will be used (we would only need the attr for
+          // subsequent xASTOREs)
+          frame = ti.getModifiableTopFrame();
+          frame.addOperandAttr(varId);
+        }
+      }
+
+    } else if ((executedInsn instanceof ReadInstruction) && ((JVMFieldInstruction)executedInsn).isReferenceField()){
+      varId = ((JVMFieldInstruction)executedInsn).getFieldName();
+
+      StackFrame frame = ti.getModifiableTopFrame();
+      frame.addOperandAttr(varId);
+
+
+    // here come the changes - note that we can't update the stats right away,
+    // because we don't know yet if the state this leads into has already been
+    // visited, and we want to detect only var changes that lead to *new* states
+    // (objective is to find out why we have new states). Note that variable
+    // changes do not necessarily contribute to the state hash (@FilterField)
+  } else if (executedInsn instanceof StoreInstruction) {
+      if (executedInsn instanceof ArrayStoreInstruction) {
+        // did we have a name for the array?
+        // stack is ".. ref idx [l]value => .."
+        // <2do> String is not a good attribute type to retrieve
+        Object attr = ((ArrayStoreInstruction)executedInsn).getArrayOperandAttr(ti);
+        if (attr != null) {
+          varId = attr + "[]";
+        } else {
+          varId = "?[]";
+        }
+      } else {
+        varId = ((LocalVariableInstruction)executedInsn).getVariableId();
+      }
+      queueIfRelevant(ti, executedInsn, varId);
+
+    } else if (executedInsn instanceof WriteInstruction){
+      varId = ((WriteInstruction) executedInsn).getFieldInfo().getFullName();
+      queueIfRelevant(ti, executedInsn, varId);
+    }
+  }
+
+  void queueIfRelevant(ThreadInfo ti, Instruction insn, String varId){
+    if (isMethodRelevant(insn.getMethodInfo()) && isVarRelevant(varId)) {
+      queue.add(new VarChange(varId));
+      lastThread = ti;
+    }
+  }
+
+  boolean isMethodRelevant (MethodInfo mi){
+    return methodSpec.matches(mi);
+  }
+
+  boolean isVarRelevant (String varId) {
+    if (!StringSetMatcher.isMatch(varId, includeVars, excludeVars)){
+      return false;
+    }
+    
+    // filter subsequent changes in the same transition (to avoid gazillions of
+    // VarChanges for loop variables etc.)
+    // <2do> this is very inefficient - improve
+    for (int i=0; i<queue.size(); i++) {
+      VarChange change = queue.get(i);
+      if (change.getVariableId().equals(varId)) {
+        return false;
+      }
+    }
+    
+    return true;
+  }
+}
+
+// <2do> expand into types to record value ranges
+class VarStat implements Comparable<VarStat> {
+  String id;               // class[@objRef].field || class[@objref].method.local
+  int nChanges;
+  
+  int lastState;           // this was changed in (<2do> don't think we need this)
+  
+  // might have more info in the future, e.g. total number of changes vs.
+  // number of states incl. this var change, source locations, threads etc.
+  
+  VarStat (String varId, int stateId) {
+    id = varId;
+    nChanges = 1;
+    
+    lastState = stateId;
+  }
+  
+  int getChangeCount () {
+    return nChanges;
+  }
+  
+  @Override
+  public int compareTo (VarStat other) {
+    if (other.nChanges > nChanges) {
+      return 1;
+    } else if (other.nChanges == nChanges) {
+      return 0;
+    } else {
+      return -1;
+    }
+  }
+}
+
+// <2do> expand into types to record values
+class VarChange {
+  String id;
+  
+  VarChange (String varId) {
+    id = varId;
+  }
+  
+  String getVariableId () {
+    return id;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/perturb/GenericDataAbstractor.java b/src/main/gov/nasa/jpf/perturb/GenericDataAbstractor.java
new file mode 100644 (file)
index 0000000..58636c0
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.perturb;
+
+import java.util.Random;
+import java.util.Vector;
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.Types;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+
+/**
+ * This file implements a generic data abstraction module that can be used
+ * with the Perturbator to execute a method with a choice of values for the
+ * method parameters.
+ * 
+ * This module handles all basic types and selects values for them as follows:
+ * int, short: random negative integer | 0 | random positive integer
+ * float: random negative floating point | 0 | random positive floating point
+ * char: two random values in [0, 255] and an explicit 0
+ * boolean: true | false
+ * 
+ * An instance of this class is expected to be specialized for each method that we
+ * want perturbed. The cstor creates a vector of valuations for the parameters
+ * using all choices for each basic type such that each vector can be written 
+ * directly into the method stack frame for parameters
+ * 
+ */
+
+public class GenericDataAbstractor implements OperandPerturbator {
+
+  // A valuations helper class
+  public class Valuation {
+    protected int valuation[] = null;
+
+    public Valuation(int size) {
+      valuation = new int[size];
+    }
+    // create an object from an existing valuation
+    public Valuation(Valuation seedValuation) {
+      valuation = seedValuation.valuation.clone();
+    }
+    public Valuation(Valuation val, int size) {
+      valuation = new int[size];
+      int[] valuationArray = val.getValuation();
+      for (int i = 0; i < valuationArray.length; i++)
+        valuation[i] = valuationArray[i];
+    }
+    public int[] getValuation() {
+      return valuation;
+    }
+    public void add(int index, int element) {
+      valuation[index] = element;
+    }
+  }
+
+  static long seed = 5;
+
+  protected MethodInfo mi;
+  protected StackFrame stackFrame;
+  protected int nParams;
+  protected byte[] paramTypes = null;
+  protected String[] paramTypeNames = null;
+  protected String[] paramNames = null;
+  protected Vector<Valuation> valuations = new Vector<Valuation>();
+  protected int choices;
+  protected int operandSize;
+  protected Valuation valuation = null;
+  protected boolean isStatic;
+  protected Random randomizer = new Random(seed);
+
+  public GenericDataAbstractor (Config conf, String keyPrefix){
+    // this will expand to read parameters from the configuration
+    // to specialize the behavior of the abstractor
+    mi = null;
+    choices = 0;
+  }
+
+  // At the time of instance creation we don't have the MethodInfo object
+  // for the method that needs its parameters perturbed. Therefore, we 
+  // should set MethodInfo using a call to the following method before
+  // we can use this instance to perturb a method invocation
+  public void setMethodInfo(MethodInfo m, StackFrame frame) {
+    if (mi != null) 
+      return;
+
+    mi = m;
+    stackFrame = frame;
+    // Need to figure out the right number of parameters. 
+    paramTypes = mi.getArgumentTypes();
+    paramTypeNames = mi.getArgumentTypeNames();
+    nParams = paramTypes.length;
+    isStatic = mi.isStatic();
+
+    // now compute the operand size in 32-bit words
+    operandSize = 0;
+    for (byte type : paramTypes) {
+      if (type == Types.T_LONG || type == Types.T_DOUBLE)
+        operandSize += 2;
+      else
+        operandSize++;
+    }
+
+    // useful when we call the various populate methods that populate
+    // choice vectors. The parameter name can be used to specialize
+    // population to parameter names
+    paramNames = new String[nParams];
+    if (nParams != 0) {
+       String[] localVars = mi.getLocalVariableNames();
+       for (int i = 0; i < nParams; i++) {
+               paramNames[i] = isStatic? localVars[i] : localVars[i + 1];
+       }
+    }
+
+    // We build an array of choices, with each choice being an index into
+    // an array of integers representing the operand stack values.
+    // We then use an IntChoiceGenerator to give us an index that is then
+    // used to access the values we want to replace for the operands on the
+    // stack
+
+    valuation = new Valuation(operandSize);
+    valuations.add(valuation);
+    populateValuations(frame, 0, 0);
+
+    // we now know how many choices there are and hence set choices
+    choices = valuations.size() - 1;
+  }
+
+  // Method to populate boolean values
+  public int[] populateBoolean(MethodInfo mi, String name) {
+    int[] bVec = {0 /* false */, 1 /* true */};
+
+    return bVec;
+  }
+
+  // Method to populate character values
+  public int[] populateChar(MethodInfo mi, String name) {
+    int[] iVec = {Math.abs(randomizer.nextInt() % 255), 0, Math.abs(randomizer.nextInt() % 255)};
+
+    return iVec;
+  }
+
+  // Method to populate byte values
+  public int[] populateByte(MethodInfo mi, String name) {
+    int[] iVec = {Math.abs(randomizer.nextInt() % 128), 0, -1 * Math.abs(randomizer.nextInt() % 127)};
+
+    return iVec;
+  }
+
+  // Method to populate integer values
+  public int[] populateInt(MethodInfo mi, String name) {
+    int[] iVec = {Math.abs(randomizer.nextInt() % 100), 0, -1 * Math.abs(randomizer.nextInt() % 100)};
+
+    return iVec;
+  }
+
+  // Method to populate integer values
+  public int[] populateShort(MethodInfo mi, String name) {
+    return populateInt(mi, name);
+  }
+
+  // Method to populate long values
+  public int[] populateLong(MethodInfo mi, String name) {
+    long[] lVec = {Math.abs(randomizer.nextLong() % 100), 0, -1 * Math.abs(randomizer.nextLong() % 100)};
+    int[] iVec = new int[lVec.length * 2];
+
+    int i = 0;
+    for (long l : lVec) {
+      iVec[i++] = Types.hiLong(l);
+      iVec[i++] = Types.loLong(l);
+    }
+    return iVec;
+  }
+
+  // Method to populate integer values
+  public int[] populateFloat(MethodInfo mi, String name) {
+    int[] fVec = {Float.floatToIntBits(randomizer.nextFloat()), 0, 
+        -1 * Float.floatToIntBits(randomizer.nextFloat())};
+
+    return fVec;
+  }
+
+  // Method to populate long values
+  public int[] populateDouble(MethodInfo mi, String name) {
+    double[] dVec = {-1.414, 0.0, 3.141};
+    int[] iVec = new int[dVec.length * 2];
+
+    int i = 0;
+    for (double d : dVec) {
+      iVec[i++] = Types.hiDouble(d);
+      iVec[i++] = Types.loDouble(d);
+    }
+    return iVec;
+  }
+
+  public void populateValuations(StackFrame frame, int paramIndex, int dataIndex) {
+    if (paramIndex == nParams) {               
+      // copy the contents of the previous vector as a
+      // suffix of it will be over-written, retaining 
+      // the valuations for all parameters ahead of the
+      // suffix
+      valuation = new Valuation(valuation);
+      valuations.add(valuation);
+    } else {
+      switch (paramTypes[paramIndex]) {
+        case Types.T_ARRAY:
+          populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          break;
+        case Types.T_BOOLEAN:
+          int[] bVec = populateBoolean(mi, paramNames[paramIndex]);
+          for (int i = 0; i < bVec.length; i++) {
+            valuation.add(dataIndex, bVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_FLOAT:
+          int[] fVec = populateFloat(mi, paramNames[paramIndex]);
+          for (int i = 0; i < fVec.length; i++) {
+            valuation.add(dataIndex, fVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_CHAR:
+          int[] iVec = populateChar(mi, paramNames[paramIndex]);
+          for (int i = 0; i < iVec.length; i++) {
+            valuation.add(dataIndex, iVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_BYTE:
+          iVec = populateByte(mi, paramNames[paramIndex]);
+          for (int i = 0; i < iVec.length; i++) {
+            valuation.add(dataIndex, iVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_INT:
+          iVec = populateInt(mi, paramNames[paramIndex]);
+          for (int i = 0; i < iVec.length; i++) {
+            valuation.add(dataIndex, iVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_SHORT:
+          iVec = populateShort(mi, paramNames[paramIndex]);
+          for (int i = 0; i < iVec.length; i++) {
+            valuation.add(dataIndex, iVec[i]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 1);
+          }
+          break;
+        case Types.T_LONG:
+          int[] lVec = populateLong(mi, paramNames[paramIndex]);
+          int i = 0;
+          while (i < lVec.length) {
+            valuation.add(dataIndex, lVec[i++]);
+            valuation.add(dataIndex + 1, lVec[i++]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 2);
+          }
+          break;
+        case Types.T_DOUBLE:
+          int[] dVec = populateDouble(mi, paramNames[paramIndex]);
+          i = 0;
+          while (i < dVec.length) {
+            valuation.add(dataIndex, dVec[i++]);
+            valuation.add(dataIndex + 1, dVec[i++]);
+            populateValuations(frame, paramIndex + 1, dataIndex + 2);
+          }
+          break;
+      }
+    }
+  }
+
+  @Override
+  public ChoiceGenerator<?> createChoiceGenerator (String id, StackFrame frame, Object refObject) {
+    // We expect that the refObject in this case will be a MethodInfo object
+    // Set it so that we can create valuation vectors
+    assert refObject instanceof MethodInfo : "wrong refObject type for GenericDataAbstractor: " + 
+    refObject.getClass().getName();
+
+  setMethodInfo((MethodInfo)refObject, frame);
+
+  if (choices > 0) {
+    // now create a choices vector which will be used to iterate over the number of
+    // parameter valuations we want to use. We set each element of the vector simply
+    // to an index into the valuations vector
+    int[] indices = new int[choices];
+    for (int i = 0; i < choices; i++) {
+      indices[i] = i;
+    }
+    return new IntChoiceFromSet(id, indices);
+  } else
+    return null;
+  }
+
+  @Override
+  public boolean perturb(ChoiceGenerator<?>cg, StackFrame frame) {
+    assert cg instanceof IntChoiceGenerator : "wrong choice generator type for GenericDataAbstractor: " + cg.getClass().getName();
+
+  int choice = ((IntChoiceGenerator)cg).getNextChoice();
+  Valuation valuation = valuations.get(choice);
+
+  // iterate over the number of operands and set the operand array to the values
+  // we have in the valuation vector
+  int val = 0;
+
+  int top = frame.getTopPos();
+  int stackIdx = frame.getLocalVariableCount() + ((isStatic)? 0 : 1);
+  int argSize = paramTypes.length;
+
+  for (int j = 0; j < argSize; j++) { // j ranges over actual arguments
+    if (!frame.isOperandRef(top - stackIdx)) {
+      frame.setOperand(top - stackIdx++, valuation.getValuation()[val++], false);
+      if (paramTypes[j] == Types.T_LONG || paramTypes[j] == Types.T_DOUBLE) {
+        frame.setOperand(top - stackIdx++, valuation.getValuation()[val++], false);
+      }
+    }
+  }
+
+  return cg.hasMoreChoices();
+  }
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType(){
+    return IntChoiceFromSet.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/perturb/IntOverUnder.java b/src/main/gov/nasa/jpf/perturb/IntOverUnder.java
new file mode 100644 (file)
index 0000000..87693c5
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.perturb;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+
+/**
+ * simple +/- delta perturbation of integer operand values
+ */
+public class IntOverUnder implements OperandPerturbator {
+
+  protected int delta;
+  protected int offset;
+
+  public IntOverUnder (Config conf, String keyPrefix) {
+    delta = conf.getInt(keyPrefix + ".delta", 0);
+    offset = 0;
+  }
+
+  public IntOverUnder (int delta){
+    this.delta = delta;
+    offset = 0;
+  }
+  
+  @Override
+  public ChoiceGenerator<?> createChoiceGenerator (String id, StackFrame frame, Object refObject){
+    int val = frame.peek(offset);
+
+    int[] values = new int[3];
+
+    values[0] = val + delta;
+    values[1] = val;
+    values[2] = val - delta;
+    
+    // set offset from refObject
+    offset = (Integer)refObject;
+
+    return new IntChoiceFromSet(id, values);
+  }
+
+  @Override
+  public boolean perturb(ChoiceGenerator<?>cg, StackFrame frame) {
+       assert cg instanceof IntChoiceGenerator : "wrong choice generator type for IntOverUnder: " + cg.getClass().getName();
+
+    int val = ((IntChoiceGenerator)cg).getNextChoice();
+       frame.setOperand(offset, val, false);
+       return cg.hasMoreChoices();
+  }
+  
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType(){
+    return IntChoiceFromSet.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/perturb/OperandPerturbator.java b/src/main/gov/nasa/jpf/perturb/OperandPerturbator.java
new file mode 100644 (file)
index 0000000..289e240
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.perturb;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * basic type for policy objects that perturb operand stacks by means
+ * of choice generators
+ */
+public interface OperandPerturbator {
+
+  Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType();
+
+  ChoiceGenerator<?> createChoiceGenerator (String id, StackFrame frame, Object refObject);
+    
+  boolean perturb (ChoiceGenerator<?> cg, StackFrame frame);
+}
diff --git a/src/main/gov/nasa/jpf/report/ConsolePublisher.java b/src/main/gov/nasa/jpf/report/ConsolePublisher.java
new file mode 100644 (file)
index 0000000..b8db8ae
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.util.Left;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.Step;
+import gov.nasa.jpf.vm.Transition;
+
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class ConsolePublisher extends Publisher {
+
+  // output destinations
+  String fileName;
+  FileOutputStream fos;
+
+  String port;
+
+  // the various degrees of information for program traces
+  protected boolean showCG;
+  protected boolean showSteps;
+  protected boolean showLocation;
+  protected boolean showSource;
+  protected boolean showMethod;
+  protected boolean showCode;
+
+  public ConsolePublisher(Config conf, Reporter reporter) {
+    super(conf, reporter);
+
+    // options controlling the output destination
+    fileName = conf.getString("report.console.file");
+    port = conf.getString("report.console.port");
+
+    // options controlling what info should be included in a trace
+    showCG = conf.getBoolean("report.console.show_cg", true);
+    showSteps = conf.getBoolean("report.console.show_steps", true);
+    showLocation = conf.getBoolean("report.console.show_location", true);
+    showSource = conf.getBoolean("report.console.show_source", true);
+    showMethod = conf.getBoolean("report.console.show_method", false);
+    showCode = conf.getBoolean("report.console.show_code", false);
+  }
+
+  @Override
+  public String getName() {
+    return "console";
+  }
+
+  @Override
+  protected void openChannel(){
+
+    if (fileName != null) {
+      try {
+        fos = new FileOutputStream(fileName);
+        out = new PrintWriter( fos);
+      } catch (FileNotFoundException x) {
+        // fall back to System.out
+      }
+    } else if (port != null) {
+      // <2do>
+    }
+
+    if (out == null){
+      out = new PrintWriter(System.out, true);
+    }
+  }
+
+  @Override
+  protected void closeChannel() {
+    if (fos != null){
+      out.close();
+    }
+  }
+
+  @Override
+  public void publishTopicStart (String topic) {
+    out.println();
+    out.print("====================================================== ");
+    out.println(topic);
+  }
+
+  @Override
+  public void publishTopicEnd (String topic) {
+    // nothing here
+  }
+
+  @Override
+  public void publishStart() {
+    super.publishStart();
+
+    if (startItems.length > 0){ // only report if we have output for this phase
+      publishTopicStart("search started: " + formatDTG(reporter.getStartDate()));
+    }
+  }
+
+  @Override
+  public void publishFinished() {
+    super.publishFinished();
+
+    if (finishedItems.length > 0){ // only report if we have output for this phase
+      publishTopicStart("search finished: " + formatDTG(reporter.getFinishedDate()));
+    }
+  }
+
+  @Override
+  protected void publishJPF() {
+    out.println(reporter.getJPFBanner());
+    out.println();
+  }
+
+  @Override
+  protected void publishDTG() {
+    out.println("started: " + reporter.getStartDate());
+  }
+
+  @Override
+  protected void publishUser() {
+    out.println("user: " + reporter.getUser());
+  }
+
+  @Override
+  protected void publishJPFConfig() {
+    publishTopicStart("JPF configuration");
+
+    TreeMap<Object,Object> map = conf.asOrderedMap();
+    Set<Map.Entry<Object,Object>> eSet = map.entrySet();
+
+    for (Object src : conf.getSources()){
+      out.print("property source: ");
+      out.println(conf.getSourceName(src));
+    }    
+    
+    out.println("properties:");
+    for (Map.Entry<Object,Object> e : eSet) {
+      out.println("  " + e.getKey() + "=" + e.getValue());
+    }
+  }
+
+  @Override
+  protected void publishPlatform() {
+    publishTopicStart("platform");
+    out.println("hostname: " + reporter.getHostName());
+    out.println("arch: " + reporter.getArch());
+    out.println("os: " + reporter.getOS());
+    out.println("java: " + reporter.getJava());
+  }
+
+
+  @Override
+  protected void publishSuT() {
+    publishTopicStart("system under test");
+    out.println( reporter.getSuT());
+  }
+
+  @Override
+  protected void publishError() {
+    Error e = reporter.getCurrentError();
+
+    publishTopicStart("error " + e.getId());
+    out.println(e.getDescription());
+
+    String s = e.getDetails();
+    if (s != null) {
+      out.println(s);
+    }
+
+  }
+
+  @Override
+  protected void publishConstraint() {
+    String constraint = reporter.getLastSearchConstraint();
+    publishTopicStart("search constraint");
+    out.println(constraint);  // not much info here yet
+  }
+
+  @Override
+  protected void publishResult() {
+    List<Error> errors = reporter.getErrors();
+
+    publishTopicStart("results");
+
+    if (errors.isEmpty()) {
+      out.println("no errors detected");
+    } else {
+      for (Error e : errors) {
+        out.print("error #");
+        out.print(e.getId());
+        out.print(": ");
+        out.print(e.getDescription());
+
+        String s = e.getDetails();
+        if (s != null) {
+          s = s.replace('\n', ' ');
+          s = s.replace('\t', ' ');
+          s = s.replace('\r', ' ');
+          out.print(" \"");
+          if (s.length() > 50){
+            out.print(s.substring(0,50));
+            out.print("...");
+          } else {
+            out.print(s);
+          }
+          out.print('"');
+        }
+
+        out.println();
+      }
+    }
+  }
+
+  /**
+   * this is done as part of the property violation reporting, i.e.
+   * we have an error
+   */
+  @Override
+  protected void publishTrace() {
+    Path path = reporter.getPath();
+    int i=0;
+
+    if (path.size() == 0) {
+      return; // nothing to publish
+    }
+
+    publishTopicStart("trace " + reporter.getCurrentErrorId());
+
+    for (Transition t : path) {
+      out.print("------------------------------------------------------ ");
+      out.println("transition #" + i++ + " thread: " + t.getThreadIndex());
+
+      if (showCG){
+        out.println(t.getChoiceGenerator());
+      }
+
+      if (showSteps) {
+        String lastLine = null;
+        MethodInfo lastMi = null;
+        int nNoSrc=0;
+
+        for (Step s : t) {
+          if (showSource) {
+            String line = s.getLineString();
+            if (line != null) {
+              if (!line.equals(lastLine)) {
+                if (nNoSrc > 0){
+                  out.println("      [" + nNoSrc + " insn w/o sources]");
+                }
+
+                out.print("  ");
+                if (showLocation) {
+                  out.print(Left.format(s.getLocationString(),30));
+                  out.print(" : ");
+                }
+                out.println(line.trim());
+                nNoSrc = 0;
+
+              }
+            } else { // no source
+              nNoSrc++;
+            }
+            
+            lastLine = line;
+          }
+
+          if (showCode) {
+            Instruction insn = s.getInstruction();
+            if (showMethod){
+              MethodInfo mi = insn.getMethodInfo();
+              if (mi != lastMi) {
+                ClassInfo mci = mi.getClassInfo();
+                out.print("    ");
+                if (mci != null) {
+                  out.print(mci.getName());
+                  out.print(".");
+                }
+                out.println(mi.getUniqueName());
+                lastMi = mi;
+              }
+            }
+            out.print("      ");
+            out.println(insn);
+          }
+        }
+
+        if (showSource && !showCode && (nNoSrc > 0)) {
+          out.println("      [" + nNoSrc + " insn w/o sources]");
+        }
+      }
+    }
+  }
+
+  @Override
+  protected void publishOutput() {
+    Path path = reporter.getPath();
+
+    if (path.size() == 0) {
+      return; // nothing to publish
+    }
+
+    publishTopicStart("output " + reporter.getCurrentErrorId());
+
+    if (path.hasOutput()) {
+      for (Transition t : path) {
+        String s = t.getOutput();
+        if (s != null){
+          out.print(s);
+        }
+      }
+    } else {
+      out.println("no output");
+    }
+  }
+
+  @Override
+  protected void publishSnapshot() {
+    VM vm = reporter.getVM();
+
+    // not so nice - we have to delegate this since it's using a lot of internals, and is also
+    // used in debugging
+    publishTopicStart("snapshot " + reporter.getCurrentErrorId());
+
+    if (vm.getPathLength() > 0) {
+      vm.printLiveThreadStatus(out);
+    } else {
+      out.println("initial program state");
+    }
+  }
+
+  public static final String STATISTICS_TOPIC = "statistics";
+  
+  // this is useful if somebody wants to monitor progress from a specialized ConsolePublisher
+  public synchronized void printStatistics (PrintWriter pw){
+    publishTopicStart( STATISTICS_TOPIC);
+    printStatistics( pw, reporter);
+  }
+  
+  // this can be used outside a publisher, to show the same info
+  public static void printStatistics (PrintWriter pw, Reporter reporter){
+    Statistics stat = reporter.getStatistics();
+
+    pw.println("elapsed time:       " + formatHMS(reporter.getElapsedTime()));
+    pw.println("states:             new=" + stat.newStates + ",visited=" + stat.visitedStates
+            + ",backtracked=" + stat.backtracked + ",end=" + stat.endStates);
+    pw.println("search:             maxDepth=" + stat.maxDepth + ",constraints=" + stat.constraints);
+    pw.println("choice generators:  thread=" + stat.threadCGs
+            + " (signal=" + stat.signalCGs + ",lock=" + stat.monitorCGs + ",sharedRef=" + stat.sharedAccessCGs
+            + ",threadApi=" + stat.threadApiCGs + ",reschedule=" + stat.breakTransitionCGs
+            + "), data=" + stat.dataCGs);
+    pw.println("heap:               " + "new=" + stat.nNewObjects
+            + ",released=" + stat.nReleasedObjects
+            + ",maxLive=" + stat.maxLiveObjects
+            + ",gcCycles=" + stat.gcCycles);
+    pw.println("instructions:       " + stat.insns);
+    pw.println("max memory:         " + (stat.maxUsed >> 20) + "MB");
+
+    pw.println("loaded code:        classes=" + ClassLoaderInfo.getNumberOfLoadedClasses() + ",methods="
+            + MethodInfo.getNumberOfLoadedMethods());
+  }
+  
+  @Override
+  public void publishStatistics() {
+    printStatistics(out);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/report/Publisher.java b/src/main/gov/nasa/jpf/report/Publisher.java
new file mode 100644 (file)
index 0000000..3935bfc
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+import gov.nasa.jpf.Config;
+
+import java.io.PrintWriter;
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * abstract base for all format specific publishers. Note that this interface
+ * also has to work for non-stream based reporting, i.e. within Eclipse
+ * (we don't want to re-parse from text reports there)
+ */
+public abstract class Publisher {
+
+  // output phases
+  public static final int START = 1;
+  public static final int TRANSITION = 2;
+  public static final int PROBE = 3;
+  public static final int CONSTRAINT = 4;
+  public static final int PROPERTY_VIOLATION = 5;
+  public static final int FINISHED = 6;
+
+  protected Config conf;
+  protected Reporter reporter; // our master
+
+  protected String[] startItems = {};
+  protected String[] transitionItems = {};
+  protected String[] propertyViolationItems = {};
+  protected String[] constraintItems = {};
+  protected String[] finishedItems = {};
+  protected String[] probeItems = {};
+
+  DateFormat dtgFormatter = DateFormat.getDateTimeInstance(DateFormat.SHORT,
+      DateFormat.SHORT);
+
+  ArrayList<PublisherExtension> extensions;
+
+  /**
+   * to be initialized in openChannel
+   * NOTE - not all publishers need to have one
+   */
+  protected PrintWriter out;
+
+  public PrintWriter getOut () {
+    return out;
+  }
+
+  protected Publisher (Config conf, Reporter reporter){
+    this.conf = conf;
+    this.reporter = reporter;
+
+    setTopicItems();
+  }
+
+  public void setItems (int category, String[] newTopics){
+    switch (category){
+    case START:
+      startItems = newTopics; break;
+    case PROBE:
+      probeItems = newTopics; break;
+    case TRANSITION:
+      transitionItems = newTopics; break;
+    case CONSTRAINT:
+      constraintItems = newTopics; break;
+    case PROPERTY_VIOLATION:
+      propertyViolationItems = newTopics; break;
+    case FINISHED:
+      finishedItems = newTopics; break;
+    default:
+      Reporter.log.warning("unknown publisher topic: " + category);
+    }
+  }
+
+  public abstract String getName();
+
+  protected void setTopicItems () {
+    setTopicItems(getName());
+  }
+  
+  protected void setTopicItems (String name) {
+    String prefix = "report." + name;
+    startItems = conf.getStringArray(prefix + ".start", startItems);
+    transitionItems = conf.getStringArray(prefix + ".transition", transitionItems);
+    probeItems = conf.getStringArray(prefix + ".probe", transitionItems);
+    propertyViolationItems = conf.getStringArray(prefix + ".property_violation", propertyViolationItems);
+    constraintItems = conf.getStringArray(prefix + ".constraint", constraintItems);
+    finishedItems = conf.getStringArray(prefix + ".finished", finishedItems);    
+  }
+  
+  public void addExtension (PublisherExtension ext) {
+    if (extensions == null) {
+      extensions = new ArrayList<PublisherExtension>();
+    }
+    extensions.add(ext);
+  }
+
+  // <2do> should not be a list we can add to
+  private static final List<PublisherExtension> EMPTY_LIST = new ArrayList<PublisherExtension>(0);
+  
+  public List<PublisherExtension> getExtensions(){
+    if (extensions != null){
+      return extensions;
+    } else {
+      return EMPTY_LIST; // make life easier for callers
+    }
+  }
+  
+  public String getLastErrorId() {
+    return reporter.getCurrentErrorId();
+  }
+
+  public boolean hasTopic (String topic) {
+    for (String s : startItems) {
+      if (s.equalsIgnoreCase(topic)){
+        return true;
+      }
+    }
+    for (String s : transitionItems) {
+      if (s.equalsIgnoreCase(topic)){
+        return true;
+      }
+    }
+    for (String s : constraintItems) {
+      if (s.equalsIgnoreCase(topic)){
+        return true;
+      }
+    }
+    for (String s : propertyViolationItems) {
+      if (s.equalsIgnoreCase(topic)){
+        return true;
+      }
+    }
+    for (String s : finishedItems) {
+      if (s.equalsIgnoreCase(topic)){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public String formatDTG (Date date) {
+    return dtgFormatter.format(date);
+  }
+
+  /**
+  static public String _formatHMS (long t) {
+    t /= 1000; // convert to sec
+
+    long s = t % 60;
+    long h = t / 3600;
+    long m = (t % 3600) / 60;
+
+    StringBuilder sb = new StringBuilder();
+    sb.append(h);
+    sb.append(':');
+    if (m < 10){
+      sb.append('0');
+    }
+    sb.append(m);
+    sb.append(':');
+    if (s < 10){
+      sb.append('0');
+    }
+    sb.append(s);
+
+    return sb.toString();
+  }
+  **/
+
+  static char[] tBuf = { '0', '0', ':', '0', '0', ':', '0', '0' };
+  
+  static synchronized public String formatHMS (long t) {
+    int h = (int) (t / 3600000);
+    int m = (int) ((t / 60000) % 60);
+    int s = (int) ((t / 1000) % 60);
+    
+    tBuf[0] = (char) ('0' + (h / 10));
+    tBuf[1] = (char) ('0' + (h % 10));
+    
+    tBuf[3] = (char) ('0' + (m / 10));
+    tBuf[4] = (char) ('0' + (m % 10));
+    
+    tBuf[6] = (char) ('0' + (s / 10));
+    tBuf[7] = (char) ('0' + (s % 10));
+    
+    return new String(tBuf);
+  }
+  
+  public String getReportFileName (String key) {
+    String fname = conf.getString(key);
+    if (fname == null){
+      fname = conf.getString("report.file");
+      if (fname == null) {
+        fname = "report";
+      }
+    }
+
+    return fname;
+  }
+
+  public void publishTopicStart (String topic) {
+    // to be done by subclasses
+  }
+
+  public void publishTopicEnd (String topic) {
+    // to be done by subclasses
+  }
+
+  public boolean hasToReportStatistics() {
+    for (String s : finishedItems) {
+      if ("statistics".equalsIgnoreCase(s)){
+        return true;
+      }
+    }
+    return false;
+  }
+
+  //--- open/close streams etc
+  protected void openChannel(){}
+  protected void closeChannel(){}
+
+  //--- if you have different preferences about when to report things, override those
+  public void publishStart() {
+    for (String item : startItems) {
+      if ("jpf".equalsIgnoreCase(item)){
+        publishJPF();
+      } else if ("platform".equalsIgnoreCase(item)){
+        publishPlatform();
+      } else if ("user".equalsIgnoreCase(item)) {
+      } else if ("dtg".equalsIgnoreCase(item)) {
+        publishDTG();
+      } else if ("config".equalsIgnoreCase(item)){
+        publishJPFConfig();
+      } else if ("sut".equalsIgnoreCase(item)){
+        publishSuT();
+      }
+    }
+
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishStart(this);
+      }
+    }
+  }
+
+  public void publishTransition() {
+    for (String topic : transitionItems) {
+      if ("statistics".equalsIgnoreCase(topic)){
+        publishStatistics();
+      }
+    }
+    
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishTransition(this);
+      }
+    }
+  }
+
+  public void publishConstraintHit() {
+    for (String item : constraintItems) {
+      if ("constraint".equalsIgnoreCase(item)) {
+        publishConstraint();
+      } else if ("trace".equalsIgnoreCase(item)){
+        publishTrace();
+      } else if ("snapshot".equalsIgnoreCase(item)){
+        publishSnapshot();
+      } else if ("output".equalsIgnoreCase(item)){
+        publishOutput();
+      } else if ("statistics".equalsIgnoreCase(item)){
+        publishStatistics(); // not sure if that is good for anything
+      }
+    }
+
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishConstraintHit(this);
+      }
+    }
+  }
+  
+  public void publishProbe(){
+    for (String topic : probeItems) {
+      if ("statistics".equalsIgnoreCase(topic)){
+        publishStatistics();
+      }
+    }    
+    
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishProbe(this);
+      }
+    }
+  }
+
+  public void publishPropertyViolation() {
+
+    for (String topic : propertyViolationItems) {
+      if ("error".equalsIgnoreCase(topic)) {
+        publishError();
+      } else if ("trace".equalsIgnoreCase(topic)){
+        publishTrace();
+      } else if ("snapshot".equalsIgnoreCase(topic)){
+        publishSnapshot();
+      } else if ("output".equalsIgnoreCase(topic)){
+        publishOutput();
+      } else if ("statistics".equalsIgnoreCase(topic)){
+        publishStatistics(); // not sure if that is good for anything
+      }
+    }
+
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishPropertyViolation(this);
+      }
+    }
+
+  }
+
+  public void publishFinished() {
+    if (extensions != null) {
+      for (PublisherExtension e : extensions) {
+        e.publishFinished(this);
+      }
+    }
+
+    for (String topic : finishedItems) {
+      if ("result".equalsIgnoreCase(topic)){
+        publishResult();
+      } else if ("statistics".equalsIgnoreCase(topic)){
+        publishStatistics();
+      }
+    }
+  }
+
+  protected void publishProlog() {} // XML headers etc
+  protected void publishEpilog() {} // likewise at the end
+
+  //--- our standard topics (only placeholders here)
+  protected void publishJPF() {}
+  protected void publishJPFConfig() {}
+  protected void publishPlatform() {}
+  protected void publishUser() {}
+  protected void publishDTG() {}
+  protected void publishJava() {}
+  protected void publishSuT() {}
+  protected void publishResult() {}
+  protected void publishError() {}
+  protected void publishConstraint() {}
+  protected void publishTrace() {}
+  protected void publishOutput() {}
+  protected void publishSnapshot() {}
+  protected void publishStatistics() {}
+
+  //--- internal helpers
+}
diff --git a/src/main/gov/nasa/jpf/report/PublisherExtension.java b/src/main/gov/nasa/jpf/report/PublisherExtension.java
new file mode 100644 (file)
index 0000000..41dc7bb
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+/**
+ * used to add sections to publishers
+ */
+public interface PublisherExtension {
+
+  void publishStart (Publisher publisher);
+  void publishTransition (Publisher publisher);
+  void publishPropertyViolation (Publisher publisher);
+  void publishConstraintHit (Publisher publisher);
+  void publishFinished (Publisher publisher);
+  void publishProbe (Publisher publisher);
+}
diff --git a/src/main/gov/nasa/jpf/report/PublisherExtensionAdapter.java b/src/main/gov/nasa/jpf/report/PublisherExtensionAdapter.java
new file mode 100644 (file)
index 0000000..3950e92
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+/**
+ * this is just a little helper class to ease standalone PublisherExtension implementations
+ */
+public class PublisherExtensionAdapter implements PublisherExtension {
+
+  @Override
+  public void publishConstraintHit(Publisher publisher) {}
+
+  @Override
+  public void publishFinished(Publisher publisher) {}
+
+  @Override
+  public void publishPropertyViolation(Publisher publisher) {}
+
+  @Override
+  public void publishStart(Publisher publisher) {}
+
+  @Override
+  public void publishTransition(Publisher publisher) {}
+
+  @Override
+  public void publishProbe(Publisher publisher) {}
+
+}
diff --git a/src/main/gov/nasa/jpf/report/Reporter.java b/src/main/gov/nasa/jpf/report/Reporter.java
new file mode 100644 (file)
index 0000000..209cbd9
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFListener;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.search.SearchListenerAdapter;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+import java.util.logging.Logger;
+
+/**
+ * this is our default report generator, which is heavily configurable
+ * via our standard properties. Note this gets instantiated and
+ * registered automatically via JPF.addListeners(), so you don't
+ * have to add it explicitly
+ */
+
+public class Reporter extends SearchListenerAdapter {
+
+  public static Logger log = JPF.getLogger("report");
+
+  protected Config conf;
+  protected JPF jpf;
+  protected Search search;
+  protected VM vm;
+
+  protected Date started, finished;
+  protected Statistics stat; // the object that collects statistics
+  protected List<Publisher> publishers = new ArrayList<Publisher>();
+  
+  protected Thread probeTimer;
+  
+  public Reporter (Config conf, JPF jpf) {
+    this.conf = conf;
+    this.jpf = jpf;
+    search = jpf.getSearch();
+    vm = jpf.getVM();
+    int probeInterval = conf.getInt("report.probe_interval");
+    boolean reportStats = conf.getBoolean("report.statistics", false) || (probeInterval > 0);
+
+    started = new Date();
+
+    addConfiguredPublishers(conf);
+
+    for (Publisher publisher : publishers) {
+      if (reportStats || publisher.hasToReportStatistics()) {
+        reportStats = true;
+      }
+
+      if (publisher instanceof JPFListener) {
+        jpf.addListener((JPFListener)publisher);
+      }
+    }
+
+    if (reportStats){
+      getRegisteredStatistics();
+    }
+    
+    if (probeInterval > 0){
+      probeTimer = createProbeIntervalTimer(probeInterval);
+    }
+  }
+
+  protected Thread createProbeIntervalTimer (final int probeInterval){
+    Thread timer = new Thread( new Runnable(){
+        @Override
+               public void run(){
+          log.info("probe timer running");
+          while (!search.isDone()){
+            try {
+              Thread.sleep( probeInterval * 1000);
+              search.probeSearch(); // this is only a request
+            } catch (InterruptedException ix) {
+              // nothing
+            }
+          }
+          log.info("probe timer terminating");
+        }
+     }, "probe-timer");
+    timer.setDaemon(true);
+    
+    // we don't start before the Search is started
+    
+    return timer;
+  }
+  
+  /**
+   * called after the JPF run is finished. Shouldn't be public, but is called by JPF
+   */
+  public void cleanUp(){
+    // nothing yet
+  }
+  
+  public Statistics getRegisteredStatistics(){
+    
+    if (stat == null){ // none yet, initialize
+      // first, check if somebody registered one explicitly
+      stat = vm.getNextListenerOfType(Statistics.class, null);
+      if (stat == null){
+        stat = conf.getInstance("report.statistics.class@stat", Statistics.class);
+        if (stat == null) {
+          stat = new Statistics();
+        }
+        jpf.addListener(stat);
+      }
+    }
+    
+    return stat;
+  }
+  
+  
+  void addConfiguredPublishers (Config conf) {
+    String[] def = { "console" };
+
+    Class<?>[] argTypes = { Config.class, Reporter.class };
+    Object[] args = { conf, this };
+
+    for (String id : conf.getStringArray("report.publisher", def)){
+      Publisher p = conf.getInstance("report." + id + ".class",
+                                     Publisher.class, argTypes, args);
+      if (p != null){
+        publishers.add(p);
+      } else {
+        log.warning("could not instantiate publisher class: " + id);
+      }
+    }
+  }
+
+  public void addPublisher( Publisher newPublisher){
+    publishers.add(newPublisher);
+  }
+  
+  public List<Publisher> getPublishers() {
+    return publishers;
+  }
+
+  public boolean hasToReportTrace() {
+    for (Publisher p : publishers) {
+      if (p.hasTopic("trace")) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public boolean hasToReportOutput() {
+    for (Publisher p : publishers) {
+      if (p.hasTopic("output")) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  public <T extends Publisher> boolean addPublisherExtension (Class<T> publisherCls, PublisherExtension e) {
+    boolean added = false;
+    for (Publisher p : publishers) {
+      Class<?> pCls = p.getClass();
+      if (publisherCls.isAssignableFrom(pCls)) {
+        p.addExtension(e);
+        added = true;
+      }
+    }
+
+    return added;
+  }
+
+  public <T extends Publisher> void setPublisherItems (Class<T> publisherCls,
+                                                        int category, String[] topics){
+    for (Publisher p : publishers) {
+      if (publisherCls.isInstance(p)) {
+        p.setItems(category,topics);
+        return;
+      }
+    }
+  }
+
+  boolean contains (String key, String[] list) {
+    for (String s : list) {
+      if (s.equalsIgnoreCase(key)){
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  //--- the publishing phases
+  
+  protected void publishStart() {
+    for (Publisher publisher : publishers) {
+      publisher.openChannel();
+      publisher.publishProlog();
+      publisher.publishStart();
+    }
+  }
+
+  protected void publishTransition() {
+    for (Publisher publisher : publishers) {
+      publisher.publishTransition();
+    }
+  }
+
+  protected void publishPropertyViolation() {
+    for (Publisher publisher : publishers) {
+      publisher.publishPropertyViolation();
+    }
+  }
+
+  protected void publishConstraintHit() {
+    for (Publisher publisher : publishers) {
+      publisher.publishConstraintHit();
+    }
+  }
+
+  protected void publishFinished() {
+    for (Publisher publisher : publishers) {
+      publisher.publishFinished();
+      publisher.publishEpilog();
+      publisher.closeChannel();
+    }
+  }
+
+  protected void publishProbe(){
+    for (Publisher publisher : publishers) {
+      publisher.publishProbe();
+    }    
+  }
+  
+  //--- the listener interface that drives report generation
+
+  @Override
+  public void searchStarted (Search search){
+    publishStart();
+    
+    if (probeTimer != null){
+      probeTimer.start();
+    }
+  }
+
+  @Override
+  public void stateAdvanced (Search search) {
+    publishTransition();
+  }
+
+  @Override
+  public void searchConstraintHit(Search search) {
+    publishConstraintHit();
+  }
+
+  @Override
+  public void searchProbed (Search search){
+    publishProbe();
+  }
+
+  @Override
+  public void propertyViolated (Search search) {
+    publishPropertyViolation();
+  }
+
+  @Override
+  public void searchFinished (Search search){
+    finished = new Date();
+
+    publishFinished();
+    
+    if (probeTimer != null){
+      // we could interrupt, but it's a daemon anyways
+      probeTimer = null;
+    }
+  }
+
+
+  //--- various getters
+  
+  public Date getStartDate() {
+    return started;
+  }
+
+  public Date getFinishedDate () {
+    return finished;
+  }
+    
+  public VM getVM() {
+    return vm;
+  }
+
+  public Search getSearch() {
+    return search;
+  }
+
+  public List<Error> getErrors () {
+    return search.getErrors();
+  }
+
+  public Error getCurrentError () {
+    return search.getCurrentError();
+  }
+
+  public String getLastSearchConstraint () {
+    return search.getLastSearchConstraint();
+  }
+
+  public String getCurrentErrorId () {
+    Error e = getCurrentError();
+    if (e != null) {
+      return "#" + e.getId();
+    } else {
+      return "";
+    }
+  }
+
+  public int getNumberOfErrors() {
+    return search.getErrors().size();
+  }
+
+  public Statistics getStatistics() {
+    return stat;
+  }
+
+  public Statistics getStatisticsSnapshot () {
+    return stat.clone();
+  }
+  
+  /**
+   * in ms
+   */
+  public long getElapsedTime () {
+    Date d = (finished != null) ? finished : new Date();
+    long t = d.getTime() - started.getTime();
+    return t;
+  }
+
+  public Path getPath (){
+    return vm.getClonedPath();
+  }
+
+  public String getJPFBanner () {
+    StringBuilder sb = new StringBuilder();
+    
+    sb.append("JavaPathfinder core system v");
+    sb.append(JPF.VERSION);
+    
+    String rev = getRevision();
+    if (rev != null){
+      sb.append(" (rev ");
+      sb.append(rev);
+      sb.append(')');
+    }
+    
+    sb.append(" - (C) 2005-2014 United States Government. All rights reserved.");
+    
+    if (conf.getBoolean("report.show_repository", false)) {
+      String repInfo =  getRepositoryInfo();
+      if (repInfo != null) {
+        sb.append( repInfo);
+      }
+    }
+    
+    return sb.toString();
+  }
+
+
+  protected String getRevision() {
+    try {
+      InputStream is = JPF.class.getResourceAsStream(".version");
+      if (is != null){
+        int len = is.available();
+        byte[] data = new byte[len];
+        is.read(data);
+        is.close();
+        return new String(data).trim();
+        
+      } else {
+        return null;
+      }
+      
+    } catch (Throwable t){
+      return null;
+    }
+  }
+  
+  protected String getRepositoryInfo() {
+    try {
+      InputStream is = JPF.class.getResourceAsStream("build.properties");
+      if (is != null){
+        Properties revInfo = new Properties();
+        revInfo.load(is);
+
+        StringBuffer sb = new StringBuffer();
+        String date = revInfo.getProperty("date");
+        String author = revInfo.getProperty("author");
+        String rev = revInfo.getProperty("rev");
+        String machine = revInfo.getProperty("hostname");
+        String loc = revInfo.getProperty("location");
+        String upstream = revInfo.getProperty("upstream");
+
+        return String.format("%s %s %s %s %s", date,author,rev,machine,loc);
+      }
+    } catch (IOException iox) {
+      return null;
+    }
+
+    return null;
+  }
+
+  
+  public String getHostName () {
+    try {
+      InetAddress in = InetAddress.getLocalHost();
+      String hostName = in.getHostName();
+      return hostName;
+    } catch (Throwable t) {
+      return "localhost";
+    }
+  }
+
+  public String getUser() {
+    return System.getProperty("user.name");
+  }
+
+  public String getSuT() {
+    return vm.getSUTDescription();
+  }
+  
+  public String getJava (){
+    String vendor = System.getProperty("java.vendor");
+    String version = System.getProperty("java.version");
+    return vendor + "/" + version;
+  }
+
+  public String getArch () {
+    String arch = System.getProperty("os.arch");
+    Runtime rt = Runtime.getRuntime();
+    String type = arch + "/" + rt.availableProcessors();
+
+    return type;
+  }
+
+  public String getOS () {
+    String name = System.getProperty("os.name");
+    String version = System.getProperty("os.version");
+    return name + "/" + version;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/report/Statistics.java b/src/main/gov/nasa/jpf/report/Statistics.java
new file mode 100644 (file)
index 0000000..152189c
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.jvm.bytecode.JVMFieldInstruction;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.LockInstruction;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+
+/**
+ * simple structure to hold statistics info created by Reporters/Publishers
+ * this is kind of a second tier SearchListener, which does not
+ * explicitly have to be registered
+ * 
+ * <2do> this should get generic and accessible enough to replace all the
+ * other statistics collectors, otherwise there is too much redundancy.
+ * If users have special requirements, they should subclass Statistics
+ * and set jpf.report.statistics.class accordingly
+ * 
+ * Note that Statistics might be accessed by a background thread
+ * reporting JPF progress, hence we have to synchronize
+ */
+public class Statistics extends ListenerAdapter implements Cloneable {
+    
+  // we make these public since we don't want to add a gazillion of
+  // getters for these purely informal numbers
+  
+  public long maxUsed = 0;
+  public long newStates = 0;
+  public long backtracked = 0;
+  public long restored = 0;
+  public int processed = 0;
+  public int constraints = 0;
+  public long visitedStates = 0;
+  public long endStates = 0;
+  public int maxDepth = 0;
+  
+  public int gcCycles = 0;
+  public long insns = 0;
+  public int threadCGs = 0;
+  public int sharedAccessCGs = 0;
+  public int monitorCGs = 0;
+  public int signalCGs = 0;
+  public int threadApiCGs = 0;
+  public int breakTransitionCGs = 0;
+  public int dataCGs = 0;
+  public long nNewObjects = 0;
+  public long nReleasedObjects = 0;
+  public int maxLiveObjects = 0;
+
+  @Override
+  public Statistics clone() {
+    try {
+      return (Statistics)super.clone();
+    } catch (CloneNotSupportedException e) {
+      return null; // can't happen
+    }
+  }
+  
+  @Override
+  public void gcBegin (VM vm) {
+    int heapSize = vm.getHeap().size();
+    if (heapSize > maxLiveObjects){
+      maxLiveObjects = heapSize;
+    }
+
+    gcCycles++;
+  }
+  
+  @Override
+  public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+    insns++;
+  }
+
+  @Override
+  public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG){
+    ChoiceGenerator<?> cg = VM.getVM().getChoiceGenerator();
+    if (cg instanceof ThreadChoiceGenerator){
+      threadCGs++;
+
+      Instruction insn = cg.getInsn();
+      if (insn instanceof JVMFieldInstruction) {
+        sharedAccessCGs++;
+      } else if (insn instanceof LockInstruction || insn instanceof JVMInvokeInstruction) {
+        monitorCGs++;
+      } else if (insn instanceof EXECUTENATIVE) {
+        MethodInfo mi = insn.getMethodInfo();
+        if (mi != null) {
+          ClassInfo ci = mi.getClassInfo();
+          if (ci != null) {
+            if (ci.isObjectClassInfo()) {
+              // its got to be either a wait or a notify since we know the java.lang.Object methods
+              signalCGs++;
+            } else if (ci.isThreadClassInfo()) {
+              threadApiCGs++;
+            }
+          } else {
+            // Hmm - a CG from a synthetic method?
+          }
+        } else {
+          // even more Hmmm - a GC from a synthesized instruction 
+        }
+      } else {
+        breakTransitionCGs++; // e.g. max_transition_length or idleLoop breakers
+      }
+    } else {
+      dataCGs++;
+    }
+  }
+  
+  @Override
+  public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei){
+    nNewObjects++;
+  }
+  
+  @Override
+  public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei){
+    nReleasedObjects++;
+  }
+  
+  @Override
+  public void stateAdvanced (Search search){
+    long m = Runtime.getRuntime().totalMemory();
+    if (m > maxUsed) {
+      maxUsed = m;
+    }
+
+    if (search.isNewState()){
+      newStates++;
+      int depth = search.getDepth();
+      if (depth > maxDepth){
+        maxDepth = depth;
+      }
+    } else {
+      visitedStates++;
+    }
+    if (search.isEndState()){
+      endStates++;
+    }
+  }
+  
+  @Override
+  public void stateBacktracked (Search search){
+    backtracked++;
+  }
+  
+  @Override
+  public void stateProcessed (Search search){
+    processed++;
+  }
+
+  @Override
+  public void stateRestored (Search search){
+    restored++;
+  }
+  
+  @Override
+  public void searchConstraintHit (Search search){
+    constraints++;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/report/XMLPublisher.java b/src/main/gov/nasa/jpf/report/XMLPublisher.java
new file mode 100644 (file)
index 0000000..ce7999f
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.report;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.RepositoryEntry;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.Step;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Transition;
+
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+public class XMLPublisher extends Publisher {
+
+  public XMLPublisher(Config conf, Reporter reporter) {
+    super(conf, reporter);
+  }
+
+  @Override
+  public String getName() {
+    return "xml";
+  }
+  
+  @Override
+  protected void openChannel(){
+    if (out == null) {
+      String fname = getReportFileName("report.xml.file") + ".xml";
+      try {
+        out = new PrintWriter(fname);
+      } catch (FileNotFoundException fnfx) {
+        // log here
+      }
+    }
+  }
+
+  @Override
+  protected void closeChannel() {
+    if (out != null){
+      out.close();
+      out = null;
+    }
+  }
+  
+  
+  @Override
+  protected void publishProlog() {
+    out.println("<?xml version=\"1.0\" ?>");
+    out.println("<jpfreport>");
+  }
+  
+  @Override
+  public void publishTopicStart(String topic) {
+    out.println("  <" + topic + ">");
+  }
+  
+  @Override
+  public void publishTopicEnd(String topic) {
+    out.println("  </" + topic + ">");
+  }  
+  
+  @Override
+  protected void publishEpilog() {
+    out.println("</jpfreport>");
+  }
+  
+  @Override
+  protected void publishJPF() {
+    out.println("  <jpf-version>" + JPF.VERSION + "</jpf-version>");
+  }
+
+  @Override
+  protected void publishJPFConfig() {
+    TreeMap<Object,Object> map = conf.asOrderedMap();
+    Set<Map.Entry<Object,Object>> eSet = map.entrySet();
+
+    out.println("  <jpf-properties>");
+
+    for (Object src : conf.getSources()){
+      out.println("    <source value=\"" + conf.getSourceName(src) + "\"/>");
+    }    
+    
+    for (Map.Entry<Object,Object> e : eSet) {
+      out.println("    <entry key=\"" + e.getKey() + "\" value=\"" + e.getValue() + "\"/>");
+    }      
+    out.println("  </jpf-properties>");
+    
+  }
+
+  @Override
+  protected void publishPlatform() {
+    out.println("  <platform>");
+    out.println("    <hostname>" + reporter.getHostName() + "</hostname>");
+    out.println("    <arch>" + reporter.getArch() + "</arch>");
+    out.println("    <os>" + reporter.getOS() + "</os>");
+    out.println("    <java>" + reporter.getJava() + "</java>");    
+    out.println("  </platform>");
+  }
+
+  @Override
+  protected void publishUser() {
+    out.println("  <user>" + reporter.getUser() + "</user>");
+  }
+
+  @Override
+  protected void publishDTG() {
+    out.println("  <started>" + reporter.getStartDate() + "</started>");
+  }
+  
+  @Override
+  protected void publishSuT() {
+    out.println("  <sut>");
+    String mainCls = reporter.getSuT();
+    if (mainCls != null) {
+      String mainPath = reporter.getSuT();
+      if (mainPath != null) {
+        out.println("    <source>" + mainPath + "</source>");
+
+        RepositoryEntry rep = RepositoryEntry.getRepositoryEntry(mainPath);
+        if (rep != null) {
+          out.println("    <repository>" + rep.getRepository() + "</repository>");
+          out.println("    <revision>" + rep.getRevision() + "</revision>");
+        }
+      } else {
+        out.println("    <binary>" + mainCls + ".class" + "</binary>");
+      }
+    } else {
+      // no app specified
+    }
+    out.println("  </sut>");
+  }
+
+  @Override
+  protected void publishResult() {
+    List<Error> errors = reporter.getErrors();
+    
+    out.print("  <result findings=\"");
+    if (errors.isEmpty()){
+      out.println("none\"/>");
+    } else {
+      out.println("errors\">");
+      int i=0;
+      for (Error e : errors) {
+        out.print("    <error id=\"");
+        out.print(i++);
+        out.println("\">");
+        out.print("      <property>");
+        out.print(e.getProperty().getClass().getName());
+        out.println("</property>");
+        out.print("      <details>");
+        out.print(e.getDetails());
+        out.println("      </details>");
+        out.println("    </error>");
+      }
+      out.println("  </result>");
+    }
+  }
+
+  // not sure how much effort we want to put into readability here
+  @Override
+  protected void publishTrace() {
+    Path path = reporter.getPath();
+    int i=0;
+
+    if (path.size() == 0) {
+      return; // nothing to publish
+    }
+    
+    out.println("  <trace>");
+    for (Transition t : path) {
+      ChoiceGenerator<?> cg = t.getChoiceGenerator();
+      out.println("    <transition id=\"" + i++ + "\" thread=\"" + t.getThreadIndex() + "\">");
+      out.println("      <cg class=\""+cg.getClass().getName() + "\" choice=\"" +
+                  cg.getProcessedNumberOfChoices() + "\"/>");
+      for (Step s : t) {
+        out.print("      <insn src=\"" + s.getLocationString() + "\">");
+        String insn = s.getInstruction().toString();
+        if (insn.indexOf('<') >= 0) { // <init> and <clinit> clash with XML
+          insn = insn.replaceAll("<", "&lt;");
+          insn = insn.replaceAll(">", "&gt;");
+        }
+        out.print(insn);
+        out.println("</insn>");
+      }
+      out.println("    </transition>");      
+    }
+    out.println("  </trace>");
+  }
+
+  @Override
+  protected void publishOutput() {
+    Path path = reporter.getPath();
+
+    if (path.size() == 0) {
+      return; // nothing to publish
+    }
+        
+    if (path.hasOutput()) {
+      out.println("  <output>");
+      for (Transition t : path) {
+        String s = t.getOutput();
+        if (s != null){
+          out.print(s);
+        }
+      }
+      out.println("  </output>");
+    }
+  }
+  
+  @Override
+  protected void publishSnapshot() {
+    VM vm = reporter.getVM();
+    
+    out.println("  <live-threads>");
+    for (ThreadInfo ti : vm.getLiveThreads()) {
+      out.println("    <thread id=\"" + ti.getId() + "\" name=\"" + ti.getName()
+                  + "\" status=\"" + ti.getStateName() + "\">");
+      // owned locks
+      for (ElementInfo e : ti.getLockedObjects()) {
+        out.println("      <lock-owned object=\"" + e + "\"/>");
+      }
+      // requested locks
+      ElementInfo ei = ti.getLockObject();
+      if (ei != null) {
+        out.println("      <lock-request object=\"" + ei + "\"/>");
+      }
+      // stack frames
+      for (StackFrame frame : ti){
+        if (!frame.isDirectCallFrame()){
+          out.println("      <frame>" + frame.getStackTraceInfo() + "</frame>");
+        }
+      }
+      out.println("    </thread>");
+    }
+    out.println("  </live-threads>");
+  }
+
+  @Override
+  protected void publishStatistics() {
+    Statistics stat = reporter.getStatistics();
+    out.println("  <statistics>");
+    out.println("    <elapsed-time>" + formatHMS(reporter.getElapsedTime()) + "</elapsed-time>");
+    out.println("    <new-states>" + stat.newStates + "</new-states>");
+    out.println("    <visited-states>" + stat.visitedStates + "</visited-states>");
+    out.println("    <backtracked-states>" + stat.backtracked + "</backtracked-states>");
+    out.println("    <end-states>" + stat.endStates + "</end-states>");
+    out.println("    <max-memory unit=\"MB\">" + (stat.maxUsed >>20) + "</max-memory>");
+    out.println("  </statistics>");
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/search/DFSearch.java b/src/main/gov/nasa/jpf/search/DFSearch.java
new file mode 100644 (file)
index 0000000..8e6658f
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * standard depth first model checking (but can be bounded by search depth
+ * and/or explicit Verify.ignoreIf)
+ */
+public class DFSearch extends Search {
+
+  public DFSearch (Config config, VM vm) {
+       super(config,vm);
+  }
+
+  @Override
+  public boolean requestBacktrack () {
+    doBacktrack = true;
+
+    return true;
+  }
+
+  /**
+   * state model of the search
+   *    next new  -> action
+   *     T    T      forward
+   *     T    F      backtrack, forward
+   *     F    T      backtrack, forward
+   *     F    F      backtrack, forward
+   *
+   * end condition
+   *    backtrack failed (no saved states)
+   *  | property violation (currently only checked in new states)
+   *  | search constraint (depth or memory or time)
+   *
+   * <2do> we could split the properties into forward and backtrack properties,
+   * the latter ones being usable for liveness properties that are basically
+   * condition accumulators for sub-paths of the state space, to be checked when
+   * we backtrack to the state where they were introduced.
+   */
+  @Override
+  public void search () {
+    boolean depthLimitReached = false;
+
+    depth = 0;
+
+    notifySearchStarted();
+
+    while (!done) {
+      if (checkAndResetBacktrackRequest() || !isNewState() || isEndState() || isIgnoredState() || depthLimitReached ) {
+        if (!backtrack()) { // backtrack not possible, done
+          break;
+        }
+
+        depthLimitReached = false;
+        depth--;
+        notifyStateBacktracked();
+      }
+
+      if (forward()) {
+        depth++;
+        notifyStateAdvanced();
+
+        if (currentError != null){
+          notifyPropertyViolated();
+
+          if (hasPropertyTermination()) {
+            break;
+          }
+          // for search.multiple_errors we go on and treat this as a new state
+          // but hasPropertyTermination() will issue a backtrack request
+        }
+
+        if (depth >= depthLimit) {
+          depthLimitReached = true;
+          notifySearchConstraintHit("depth limit reached: " + depthLimit);
+          continue;
+        }
+
+        if (!checkStateSpaceLimit()) {
+          notifySearchConstraintHit("memory limit reached: " + minFreeMemory);
+          // can't go on, we exhausted our memory
+          break;
+        }
+
+      } else { // forward did not execute any instructions
+        notifyStateProcessed();
+      }
+    }
+
+    notifySearchFinished();
+  }
+
+
+  @Override
+  public boolean supportsBacktrack () {
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/PathSearch.java b/src/main/gov/nasa/jpf/search/PathSearch.java
new file mode 100644 (file)
index 0000000..8437582
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * PathSearch is not really a Search object, just a simple 'forward'
+ * driver for the VM that loops until there is no next instruction or
+ * a property doesn't hold
+ * 
+ */
+public class PathSearch extends Search {
+       
+  public PathSearch (Config config, VM vm) {
+    super(config,vm);    
+  }
+  
+  @Override
+  public boolean requestBacktrack () {
+    doBacktrack = true;
+
+    return true;
+  }
+
+  @Override
+  public void search () {
+    depth++;
+
+    if (hasPropertyTermination()) {
+      return;
+    }
+
+    notifySearchStarted();
+
+    while (true) {
+      while (doBacktrack) { // might be set by StateListeners
+
+        if (depth > 0) {
+          vm.backtrack();
+          depth--;
+
+          notifyStateBacktracked();
+        }
+
+        doBacktrack = false;
+      }
+
+      forward();
+      // isVisitedState is never true, because we don't really search, just replay
+      notifyStateAdvanced();
+
+      if (currentError != null){
+        notifyPropertyViolated();
+
+        if (hasPropertyTermination()) {
+          break;
+        }
+      }
+
+      if (isEndState()) {
+        break;
+      }
+
+      depth++;
+    }
+
+    notifySearchFinished();
+  }
+
+  @Override
+  public boolean supportsBacktrack () {
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/RandomSearch.java b/src/main/gov/nasa/jpf/search/RandomSearch.java
new file mode 100644 (file)
index 0000000..e9e79e8
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.RestorableVMState;
+
+
+/**
+ * this is a straight execution pseudo-search - it doesn't search at
+ * all (i.e. it doesn't backtrack), but just behaves like a 'normal' VM,
+ * going forward() until there is no next state then it restarts the search 
+ * until it hits a certain number of paths executed
+ *
+ * <2do> this needs to be updated & tested
+ */
+public class RandomSearch extends Search {
+  int path_limit = 0;
+  
+  public RandomSearch (Config config, VM vm) {
+    super(config, vm);
+    
+    path_limit = config.getInt("search.RandomSearch.path_limit", 0);
+  }
+  
+  @Override
+  public void search () {
+    int    depth = 0;
+    int paths = 0;
+    depth++;
+    
+    if (hasPropertyTermination()) {
+      return;
+    }
+    
+    //vm.forward();
+    RestorableVMState init_state = vm.getRestorableState();
+    
+    notifySearchStarted();
+    while (!done) {
+      if ((depth < depthLimit) && forward()) {
+        notifyStateAdvanced();
+
+        if (currentError != null){
+          notifyPropertyViolated();
+
+          if (hasPropertyTermination()) {
+            return;
+          }
+        }
+
+        if (isEndState()){
+          return;
+        }
+
+        depth++;
+
+      } else { // no next state or reached depth limit
+        // <2do> we could check for more things here. If the last insn wasn't
+        // the main return, or a System.exit() call, we could flag a JPFException
+        if (depth >= depthLimit) {
+          notifySearchConstraintHit("depth limit reached: " + depthLimit);
+        }
+        checkPropertyViolation();
+        done = (paths >= path_limit);
+        paths++;
+        System.out.println("paths = " + paths);
+        depth = 1;
+        vm.restoreState(init_state);
+        vm.resetNextCG();
+      }
+    }
+    notifySearchFinished();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/Search.java b/src/main/gov/nasa/jpf/search/Search.java
new file mode 100644 (file)
index 0000000..e447741
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ConfigChangeListener;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.JPFListenerException;
+import gov.nasa.jpf.Property;
+import gov.nasa.jpf.State;
+import gov.nasa.jpf.report.Reporter;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+import gov.nasa.jpf.vm.ThreadList;
+import gov.nasa.jpf.vm.Transition;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+/**
+ * the mother of all search classes. Mostly takes care of listeners, keeping
+ * track of state attributes and errors. This class mainly keeps the
+ * general search info like depth, configured properties etc.
+ */
+public abstract class Search {
+  
+  protected static JPFLogger log = JPF.getLogger("gov.nasa.jpf.search");
+  
+  /** error encountered during last transition, null otherwise */
+  protected Error currentError = null;
+  protected ArrayList<Error> errors = new ArrayList<Error>();
+
+  protected int       depth = 0;
+  protected VM       vm;
+
+  protected ArrayList<Property> properties;
+
+  protected boolean matchDepth;
+  protected long    minFreeMemory;
+  protected int     depthLimit;
+  protected boolean getAllErrors;
+
+  // message explaining the last search constraint hit
+  protected String lastSearchConstraint;
+
+  // these states control the search loop
+  protected boolean done = false;
+  protected boolean doBacktrack = false;
+
+  // do we have a probe request
+  protected AtomicBoolean notifyProbeListeners = new AtomicBoolean(false);
+
+  /** search listeners. We keep them in a simple array to avoid
+   creating objects on each notification */
+  protected SearchListener[] listeners = new SearchListener[0];
+
+  /** this is a special SearchListener that is always notified last, so that
+   * PublisherExtensions can be sure the notification has been processed by all listeners */
+  protected Reporter reporter;
+
+  protected final Config config; // to later-on access settings that are only used once (not ideal)
+
+  // don't forget to unregister or we have a HUGE memory leak if the same Config object is
+  // reused for several JPF runs
+  class ConfigListener implements ConfigChangeListener {
+
+    @Override
+    public void propertyChanged(Config config, String key, String oldValue, String newValue) {
+      // Different Config instance
+      if (!config.equals(Search.this.config)) {
+        return;
+      }
+
+      // Check if Search configuration changed
+      if (key.startsWith("search.")){
+        String k = key.substring(7);
+        if ("match_depth".equals(k) ||
+            "min_free".equals(k) ||
+            "multiple_errors".equals(k)){
+          initialize(config);
+        }
+      }
+    }
+    
+    @Override
+    public void jpfRunTerminated (Config config){
+      config.removeChangeListener(this);
+    }
+  }
+  
+  /** storage to keep track of state depths */
+  protected final IntVector stateDepth = new IntVector();
+
+  protected Search (Config config, VM vm) {
+    this.vm = vm;
+    this.config = config;
+
+    initialize( config);
+
+    properties = getProperties(config);
+    if (properties.isEmpty()) {
+      log.severe("no property");
+    }
+    
+    config.addChangeListener( new ConfigListener());
+  }
+
+  protected void initialize( Config conf){
+    depthLimit = conf.getInt("search.depth_limit", Integer.MAX_VALUE);
+    matchDepth = conf.getBoolean("search.match_depth");
+    minFreeMemory = conf.getMemorySize("search.min_free", 1024<<10);    
+    getAllErrors = conf.getBoolean("search.multiple_errors");
+  }
+  
+  /**
+   * called after the JPF run is finished. Shouldn't be public, but is called by JPF
+   */
+  public void cleanUp(){
+    // nothing here, the ConfigListener removes itself
+  }
+  
+  public Config getConfig() {
+    return config;
+  }
+  
+  public abstract void search ();
+
+  public void setReporter(Reporter reporter){
+    this.reporter = reporter;
+  }
+
+  public void addListener (SearchListener newListener) {
+    log.info("SearchListener added: ", newListener);
+    listeners = Misc.appendElement(listeners, newListener);
+  }
+
+  public boolean hasListenerOfType (Class<?> listenerCls) {
+    return Misc.hasElementOfType(listeners, listenerCls);
+  }
+  
+  public <T> T getNextListenerOfType(Class<T> type, T prev){
+    return Misc.getNextElementOfType(listeners, type, prev);
+  }
+
+
+  public void removeListener (SearchListener removeListener) {
+    listeners = Misc.removeElement(listeners, removeListener);
+  }
+
+
+  public void addProperty (Property newProperty) {
+    properties.add(newProperty);
+  }
+
+  public void removeProperty (Property oldProperty) {
+     properties.remove(oldProperty);
+  }
+
+  /**
+   * return set of configured properties
+   * note there is a name clash here - JPF 'properties' have nothing to do with
+   * Java properties (java.util.Properties)
+   */
+  protected ArrayList<Property> getProperties (Config config) {
+    Class<?>[] argTypes = { Config.class, Search.class };
+    Object[] args = { config, this };
+
+    ArrayList<Property> list = config.getInstances("search.properties", Property.class,
+                                                   argTypes, args);
+
+    return list;
+  }
+
+  // check for property violation, return true if not done
+  protected boolean hasPropertyTermination () {
+    if (currentError != null){
+      if (done){
+        return true;
+      } else { // we search for multiple errors, so we ignore and go on
+        doBacktrack = true;
+      }
+    }
+
+    return false;
+  }
+
+  // this should only be called once per transition, otherwise it keeps adding the same error
+  protected boolean checkPropertyViolation () {
+    for (Property p : properties) {
+      if (!p.check(this, vm)) {
+        error(p, vm.getClonedPath(), vm.getThreadList());
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public List<Error> getErrors () {
+    return errors;
+  }
+
+  public int getNumberOfErrors(){
+    return errors.size();
+  }
+
+  public String getLastSearchConstraint() {
+    return lastSearchConstraint;
+  }
+
+  /**
+   * request a probe
+   * 
+   * This does not do the actual listener notification, it only stores
+   * the request, which is then processed from within JPFs inner execution loop.
+   * As a consequence, probeSearch() can be called async, and searchProbed() listeners
+   * don't have to bother with synchronization or inconsistent JPF states (notification 
+   * happens from within JPFs main thread after a completed Instruction execution)
+   */
+  public void probeSearch(){
+    notifyProbeListeners.set(true);
+  }
+  
+  /**
+   * this does the actual notification and resets the request, hence this call
+   * should only happen from within JPFs main thread
+   */
+  public void checkAndResetProbeRequest(){
+    if (notifyProbeListeners.compareAndSet(true, false)){
+      notifySearchProbed();
+    }
+  }
+  
+  /**
+   * @return error encountered during *last* transition (null otherwise)
+   */
+  public Error getCurrentError(){
+    return currentError;
+  }
+
+  public Error getLastError() {
+    int i=errors.size()-1;
+    if (i >=0) {
+      return errors.get(i);
+    } else {
+      return null;
+    }
+  }
+
+  public boolean hasErrors(){
+    return !errors.isEmpty();
+  }
+
+  public VM getVM() {
+    return vm;
+  }
+
+  public boolean isEndState () {
+    return vm.isEndState();
+  }
+
+  public boolean isErrorState(){
+    return (currentError != null);
+  }
+
+  public boolean hasNextState () {
+    return !isEndState();
+  }
+
+  public boolean transitionOccurred(){
+    return vm.transitionOccurred();
+  }
+
+  public boolean isNewState () {
+    boolean isNew = vm.isNewState();
+
+    if (matchDepth) {
+      int id = vm.getStateId();
+
+      if (isNew) {
+        setStateDepth(id, depth);
+      } else {
+        return depth < getStateDepth(id);
+      }
+    }
+
+    return isNew;
+  }
+
+  public boolean isVisitedState () {
+    return !isNewState();
+  }
+
+  public boolean isIgnoredState(){
+    return vm.isIgnoredState();
+  }
+
+  public boolean isProcessedState(){
+    return vm.getChoiceGenerator().isProcessed();
+  }
+
+  public boolean isDone(){
+    return done;
+  }
+
+  public int getDepth () {
+    return depth;
+  }
+
+  public String getSearchConstraint () {
+    return lastSearchConstraint;
+  }
+
+  public Transition getTransition () {
+    return vm.getLastTransition();
+  }
+
+  public int getStateId () {
+    return vm.getStateId();
+  }
+
+  public int getPurgedStateId () {
+    return -1; // a lot of Searches don't purge any states
+  }
+
+
+  /**
+   * this is somewhat redundant to SystemState.setIgnored(), but we don't
+   * want to mix the case of overriding state matching with backtracking when
+   * searching for multiple errors
+   */
+  public boolean requestBacktrack () {
+    return doBacktrack = true;
+  }
+
+
+  protected boolean checkAndResetBacktrackRequest() {
+    if (doBacktrack){
+      doBacktrack = false;
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  public boolean supportsBacktrack () {
+    return true;
+  }
+
+  public boolean supportsRestoreState () {
+    // not supported by default
+    return false;
+  }
+
+  public int getDepthLimit () {
+    return depthLimit;
+  }
+  
+  public void setDepthLimit(int limit){
+    depthLimit = limit;
+  }
+
+  protected SearchState getSearchState () {
+    return new SearchState(this);
+  }
+
+  // can be used by SearchListeners to create path-less errors (liveness)
+  public void error (Property property) {
+    error(property, null, null);
+  }
+
+  public void error (Property property, Path path, ThreadList threadList) {
+
+    if (getAllErrors) {
+       // otherwise we are going to overwrite it if we go on
+      try {
+        property = property.clone();
+        path = path.clone();
+        threadList = (ThreadList) threadList.clone(); // this makes it a snapshot (deep) clone
+      } catch (CloneNotSupportedException cnsx){
+        throw new JPFException("failed to clone error information: " + cnsx);
+      }
+      done = false;
+      
+    } else {
+      done = true;
+    }
+
+    currentError = new Error(errors.size()+1, property, path, threadList);
+
+    errors.add(currentError);
+
+    // we should not reset the property until listeners have been notified
+    // (the listener might be the property itself, in which case it could get
+    // confused if propertyViolated() notifications happen after a reset)
+  }
+
+  public void resetProperties(){
+    for (Property p : properties) {
+      p.reset();
+    }
+  }
+
+  protected void notifyStateAdvanced () {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].stateAdvanced(this);
+      }
+      if (reporter != null){
+        // reporter always comes last to ensure all listeners have been notified
+        reporter.stateAdvanced(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during stateAdvanced() notification", t);
+    }
+  }
+
+  protected void notifyStateProcessed() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].stateProcessed(this);
+      }
+      if (reporter != null){
+        reporter.stateProcessed(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during stateProcessed() notification", t);
+    }
+  }
+
+  protected void notifyStateStored() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].stateStored(this);
+      }
+      if (reporter != null){
+        reporter.stateStored(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during stateStored() notification", t);
+    }
+  }
+
+  protected void notifyStateRestored() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].stateRestored(this);
+      }
+      if (reporter != null){
+        reporter.stateRestored(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during stateRestored() notification", t);
+    }
+  }
+
+  protected void notifyStateBacktracked() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].stateBacktracked(this);
+      }
+      if (reporter != null){
+        reporter.stateBacktracked(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during stateBacktracked() notification", t);
+    }
+  }
+
+  protected void notifyStatePurged() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].statePurged(this);
+      }
+      if (reporter != null){
+        reporter.statePurged(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during statePurged() notification", t);
+    }
+  }
+
+  public void notifySearchProbed() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].searchProbed(this);
+      }
+      if (reporter != null){
+        reporter.searchProbed(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during searchProbed() notification", t);
+    }
+  }
+
+  
+  protected void notifyPropertyViolated() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].propertyViolated(this);
+      }
+      if (reporter != null){
+        reporter.propertyViolated(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during propertyViolated() notification", t);
+    }
+
+    // reset properties if getAllErrors is set
+    if (getAllErrors){
+      resetProperties();
+    }
+  }
+
+  protected void notifySearchStarted() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].searchStarted(this);
+      }
+      if (reporter != null){
+        reporter.searchStarted(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during searchStarted() notification", t);
+    }
+  }
+
+  public void notifySearchConstraintHit(String details) {
+    try {
+      lastSearchConstraint = details;
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].searchConstraintHit(this);
+      }
+      if (reporter != null){
+        reporter.searchConstraintHit(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during searchConstraintHit() notification", t);
+    }
+  }
+
+  protected void notifySearchFinished() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].searchFinished(this);
+      }
+      if (reporter != null){
+        reporter.searchFinished(this);
+      }
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during searchFinished() notification", t);
+    }
+  }
+
+  protected boolean forward () {
+    currentError = null;
+
+    boolean ret = vm.forward();
+
+    checkPropertyViolation();
+    return ret;
+  }
+
+  protected boolean backtrack () {
+    return vm.backtrack();
+  }
+
+  public void setIgnoredState (boolean cond) {
+    vm.ignoreState(cond);
+  }
+
+  protected void restoreState (State state) {
+    // not supported by default
+  }
+
+  /** this can be used by listeners to terminate the search */
+  public void terminate () {
+    done = true;
+  }
+
+  protected void setStateDepth (int stateId, int depth) {
+    stateDepth.set(stateId, depth + 1);
+  }
+
+  public int getStateDepth (int stateId) {
+    int depthPlusOne = stateDepth.get(stateId);
+    if (depthPlusOne <= 0) {
+      throw new JPFException("Asked for depth of unvisited state");
+    } else {
+      return depthPlusOne - 1;
+    }
+  }
+
+  /**
+   * check if we have a minimum amount of free memory left. If not, we rather want to stop in time
+   * (with a threshold amount left) so that we can report something useful, and not just die silently
+   * with a OutOfMemoryError (which isn't handled too gracefully by most VMs)
+   */
+  public boolean checkStateSpaceLimit () {
+    Runtime rt = Runtime.getRuntime();
+
+    long avail = rt.freeMemory();
+
+    // we could also just check for a max number of states, but what really
+    // limits us is the memory required to store states
+
+    if (avail < minFreeMemory) {
+      // try to collect first
+      rt.gc();
+      avail = rt.freeMemory();
+
+      if (avail < minFreeMemory) {
+        // Ok, we give up, threshold reached
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/search/SearchListener.java b/src/main/gov/nasa/jpf/search/SearchListener.java
new file mode 100644 (file)
index 0000000..c137ecc
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+import gov.nasa.jpf.JPFListener;
+
+/**
+ * interface to register for notification by the Search object.
+ * Observer role in same-name pattern
+ */
+public interface SearchListener extends JPFListener {
+  
+  /**
+   * got the next state
+   * Note - this will be notified before any potential propertyViolated, in which
+   * case the currentError will be already set
+   */
+  void stateAdvanced (Search search);
+  
+  /**
+   * state is fully explored
+   */
+  void stateProcessed (Search search);
+  
+  /**
+   * state was backtracked one step
+   */
+  void stateBacktracked (Search search);
+
+  /**
+   * some state is not going to appear in any path anymore
+   */
+  void statePurged (Search search);
+
+  /**
+   * somebody stored the state
+   */
+  void stateStored (Search search);
+  
+  /**
+   * a previously generated state was restored
+   * (can be on a completely different path)
+   */
+  void stateRestored (Search search);
+  
+  /**
+   * there was a probe request, e.g. from a periodical timer
+   * note this is called synchronously from within the JPF execution loop
+   * (after instruction execution)
+   */
+  void searchProbed (Search search);
+  
+  /**
+   * JPF encountered a property violation.
+   * Note - this is always preceeded by a stateAdvanced
+   */
+  void propertyViolated (Search search);
+  
+  /**
+   * we get this after we enter the search loop, but BEFORE the first forward
+   */
+  void searchStarted (Search search);
+  
+  /**
+   * there was some contraint hit in the search, we back out
+   * could have been turned into a property, but usually is an attribute of
+   * the search, not the application
+   */
+  void searchConstraintHit (Search search);
+  
+  /**
+   * we're done, either with or without a preceeding error
+   */
+  void searchFinished (Search search);
+}
+
diff --git a/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java b/src/main/gov/nasa/jpf/search/SearchListenerAdapter.java
new file mode 100644 (file)
index 0000000..1493336
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+/**
+ * a no-action SearchListener which we can use to override only the
+ * notifications we are interested in
+ */
+public class SearchListenerAdapter implements SearchListener {
+
+  @Override
+  public void stateAdvanced(Search search) {}
+
+  @Override
+  public void stateProcessed(Search search) {}
+
+  @Override
+  public void stateBacktracked(Search search) {}
+
+  @Override
+  public void statePurged(Search search) {}
+
+  @Override
+  public void stateStored(Search search) {}
+
+  @Override
+  public void stateRestored(Search search) {}
+
+  @Override
+  public void searchProbed(Search search) {}
+  
+  @Override
+  public void propertyViolated(Search search) {}
+
+  @Override
+  public void searchStarted(Search search) {}
+
+  @Override
+  public void searchConstraintHit(Search search) {}
+
+  @Override
+  public void searchFinished(Search search) {}
+
+}
diff --git a/src/main/gov/nasa/jpf/search/SearchState.java b/src/main/gov/nasa/jpf/search/SearchState.java
new file mode 100644 (file)
index 0000000..14eab8c
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+public class SearchState {
+  
+  Search search;
+  int depth;
+
+  SearchState (Search search) {
+    this.search = search;
+    depth = search.depth;
+  }
+
+  public int getSearchDepth () {
+    return depth;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/Simulation.java b/src/main/gov/nasa/jpf/search/Simulation.java
new file mode 100644 (file)
index 0000000..4f0f99f
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * this is a straight execution pseudo-search - it doesn't search at
+ * all (i.e. it doesn't backtrack), but just behaves like a 'normal' VM,
+ * going forward() until there is no next state
+ *
+ * <2do> of course it doesn't quite behave like a normal VM, since it
+ * doesn't honor thread priorities yet (needs a special scheduler)
+ *
+ * <2do> it's not really clear to me how this differs from a 'PathSearch'
+ * other than using a different scheduler. Looks like there should be just one
+ *
+ * <2do> this needs to be updated & tested
+ *
+ */
+public class Simulation extends Search {
+  
+  public Simulation (Config config, VM vm) {
+    super(config, vm);
+  }
+
+  @Override
+  public void search () {
+    int    depth = 0;
+
+    depth++;
+
+    if (hasPropertyTermination()) {
+      return;
+    }
+
+    notifySearchStarted();
+    
+    while (!done) {
+      if (forward()) {
+
+        if (currentError != null){
+          notifyPropertyViolated();
+
+          if (hasPropertyTermination()) {
+            return;
+          }
+        }
+
+        depth++;
+
+      } else { // no next state
+
+        // <2do> we could check for more things here. If the last insn wasn't
+        // the main return, or a System.exit() call, we could flag a JPFException
+        checkPropertyViolation();
+        done = true;
+      }
+    }
+    notifySearchFinished();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/BFSHeuristic.java b/src/main/gov/nasa/jpf/search/heuristic/BFSHeuristic.java
new file mode 100644 (file)
index 0000000..9ba6999
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * breadth first search
+ */
+public class BFSHeuristic extends SimplePriorityHeuristic {
+  
+  public BFSHeuristic (Config config, VM vm) {    
+       super(config,vm);
+       Search.log.info("BFS Search");
+  }
+  
+  @Override
+  protected int computeHeuristicValue () {
+    int priority = vm.getPathLength();
+
+    return priority;
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/DFSHeuristic.java b/src/main/gov/nasa/jpf/search/heuristic/DFSHeuristic.java
new file mode 100644 (file)
index 0000000..df4cc5c
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * heuristic state prioritizer that favors search depth
+ */
+public class DFSHeuristic extends SimplePriorityHeuristic {
+  
+  public DFSHeuristic (Config conf, VM vm) {
+    super(conf, vm);
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    return Integer.MAX_VALUE - vm.getPathLength();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/GlobalSwitchThread.java b/src/main/gov/nasa/jpf/search/heuristic/GlobalSwitchThread.java
new file mode 100644 (file)
index 0000000..9d1ddbb
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * heuristic state prioritizer that tries to minimize re-scheduling
+ */
+public class GlobalSwitchThread extends SimplePriorityHeuristic {
+  private int[] threads;
+
+  public GlobalSwitchThread (Config config, VM vm) {
+    super(config, vm);
+    
+    int threadHistorySize = config.getInt("search.heuristic.thread_history_size", 10);
+    
+    threads = new int[threadHistorySize];
+
+    for (int i = 0; i < threads.length; i++) {
+      threads[i] = -1;
+    }
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    int aliveThreads = vm.getThreadList().getMatchingCount(aliveThread);
+
+    int lastRun = vm.getLastTransition().getThreadIndex();
+    int h_value = 0;
+
+    if (aliveThreads > 1) {
+      for (int i = 0; i < threads.length; i++) {
+        if (lastRun == threads[i]) {
+          h_value += ((threads.length - i) * aliveThreads);
+        }
+      }
+    }
+
+    int temp0 = threads[0];
+    int temp1;
+    threads[0] = lastRun;
+
+    for (int i = 1; i < threads.length; i++) {
+      temp1 = threads[i];
+      threads[i] = temp0;
+      temp0 = temp1;
+    }
+
+    return h_value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/HeuristicSearch.java b/src/main/gov/nasa/jpf/search/heuristic/HeuristicSearch.java
new file mode 100644 (file)
index 0000000..ae05396
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * a search strategy class that computes all immediate successors of a given
+ * state, puts them into a priority queue (the priority is provided by a
+ * Heuristic strategy object), and processes states in the sequence of
+ * highest priorities. Note that the queue can be search-global, i.e. we might hop
+ * between search levels.
+ */
+public abstract class HeuristicSearch extends Search {
+  
+  static final String DEFAULT_HEURISTIC_PACKAGE = "gov.nasa.jpf.search.heuristic.";
+  
+  protected HeuristicState parentState;
+  protected List<HeuristicState> childStates;
+  
+  protected boolean isPathSensitive = false;  
+  
+  /*
+   * do we use A* adaptation of state priorities, i.e. have a
+   * distance + cost heuristic (in this context, we just use the
+   * path length as the "distance")
+   */
+  protected boolean useAstar;
+  
+  /*
+   * a beam search is a HeuristicSearch with a state queue that is reset at each
+   * search level (i.e. it doesn't hop between search levels when fetching the
+   * next state from the queue)
+   */
+  protected boolean isBeamSearch;
+
+  
+  public HeuristicSearch (Config config, VM vm) {
+    super(config, vm);
+    
+    useAstar = config.getBoolean("search.heuristic.astar");
+    isBeamSearch = config.getBoolean("search.heuristic.beam_search");
+  }
+
+  
+  // add the current state to the queue
+  protected abstract HeuristicState queueCurrentState ();
+  
+  // return the next queued state, which becomes the new parentState
+  // implementors can also reset or modify the queue
+  protected abstract HeuristicState getNextQueuedState ();
+
+  public abstract int getQueueSize();
+  public abstract boolean isQueueLimitReached();
+  
+  public HeuristicState getParentState() {
+    return parentState;
+  }
+  
+  public List<HeuristicState> getChildStates() {
+    return childStates;
+  }
+  
+  public void setPathSensitive (boolean isPathSensitive) {
+    this.isPathSensitive = isPathSensitive;
+  }  
+  
+  void backtrackToParent () {
+    backtrack();
+
+    depth--;
+    notifyStateBacktracked();    
+  }
+  
+  /*
+   * generate the set of all child states for the current parent state
+   * 
+   * overriding methods can use the return value to determine if they
+   * have to process the childStates, e.g. to compute priorities
+   * that require the whole set
+   * 
+   * @returns false if this is cut short by a property termination or
+   * explicit termination request
+   */
+  protected boolean generateChildren () {
+
+    childStates = new ArrayList<HeuristicState>();
+    
+    while (!done) {
+      
+      if (!forward()) {
+        notifyStateProcessed();
+        return true;
+      }
+
+      depth++;
+      notifyStateAdvanced();
+
+      if (currentError != null){
+        notifyPropertyViolated();
+        if (hasPropertyTermination()) {
+          return false;
+        }
+        
+        // note that we don't store the error state anymore, which means we
+        // might encounter it along different paths. However, this is probably
+        // what we want for search.multiple_errors.
+        
+      } else {
+      
+        if (!isEndState() && !isIgnoredState()) {
+          boolean isNewState = isNewState();
+
+          if (isNewState && depth >= depthLimit) {
+            // we can't do this before we actually generated the VM child state
+            // since we don't want to report DEPTH_CONSTRAINTs for parents
+            // that have only visited or end state children
+            notifySearchConstraintHit("depth limit reached: " + depthLimit);
+
+          } else if (isNewState || isPathSensitive) {
+
+            if (isQueueLimitReached()) {
+              notifySearchConstraintHit("queue limit reached: " + getQueueSize());
+            }
+          
+            HeuristicState newHState = queueCurrentState();            
+            if (newHState != null) { 
+              childStates.add(newHState);
+              notifyStateStored();
+            }
+          }
+        
+        } else {
+          // end state or ignored transition
+        }
+      }
+      
+      backtrackToParent();
+    }
+    
+    return false;
+  }
+
+  
+  private void restoreState (HeuristicState hState) {    
+    vm.restoreState(hState.getVMState());
+
+    // note we have to query the depth from the VM because the state is taken from the queue
+    // and we have no idea when it was entered there
+    depth = vm.getPathLength();
+    notifyStateRestored();
+  }
+   
+  @Override
+  public void search () {
+        
+    queueCurrentState();
+    notifyStateStored();
+    
+    // kind of stupid, but we need to get it out of the queue, and we
+    // don't have to restore it since it's the first one
+    parentState = getNextQueuedState();
+    
+    done = false;
+    notifySearchStarted();
+    
+    if (!hasPropertyTermination()) {
+      generateChildren();
+
+      while (!done && (parentState = getNextQueuedState()) != null) {
+        restoreState(parentState);
+        
+        generateChildren();
+      }
+    }
+    
+    notifySearchFinished();
+  }
+
+  @Override
+  public boolean supportsBacktrack () {
+    // we don't do multi-level backtracks, but automatically do backtrackToParent()
+    // after each child state generation
+    return false;
+  }
+}
+
+
diff --git a/src/main/gov/nasa/jpf/search/heuristic/HeuristicState.java b/src/main/gov/nasa/jpf/search/heuristic/HeuristicState.java
new file mode 100644 (file)
index 0000000..81ce6e9
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.RestorableVMState;
+
+
+/**
+ * wrapper for states that are processed in an order that is
+ * defined by a heuristic (i.e. not just depends on the shape
+ * of the state graph)
+ */
+public abstract class HeuristicState {
+  
+  protected RestorableVMState vmState;
+  protected int     stateId;
+    
+  public HeuristicState (VM vm) {
+    stateId = vm.getStateId();
+    vmState = vm.getRestorableState();
+  }
+  
+  public RestorableVMState getVMState () {
+    return vmState;
+  }
+  
+  public int getStateId() {
+    return stateId;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/Interleaving.java b/src/main/gov/nasa/jpf/search/heuristic/Interleaving.java
new file mode 100644 (file)
index 0000000..4b1d25a
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Path;
+
+
+/**
+ * Heuristic to maximize thread interleavings. It is particularly good at
+ * flushing out concurrency errors, since it schedules different threads 
+ * as much as possible.
+ * 
+ */
+public class Interleaving extends SimplePriorityHeuristic {
+    
+  int historyLimit;
+
+  public Interleaving (Config config, VM vm) {
+    super(config,vm);
+    
+    historyLimit = config.getInt("search.heuristic.thread_history_limit", -1);
+  }
+
+  /*
+   * heuristic based on how often, how long ago, and within how many
+   * live threads a certain thread did run
+   */
+  @Override
+  protected int computeHeuristicValue () {
+    int aliveThreads = vm.getThreadList().getMatchingCount(aliveThread);
+
+    Path path = vm.getPath();
+    int  pathSize = path.size();
+    
+    int tid = vm.getCurrentThread().getId();
+    int h_value = 0;
+
+    if (aliveThreads > 1) { // otherwise there's nothing to interleave
+      
+      for (int i= Math.max(0, pathSize - historyLimit); i<pathSize; i++) {
+        if (path.get(i).getThreadIndex() == tid) {
+          h_value += (pathSize - i) * aliveThreads;
+        }
+      }
+    }
+
+    return h_value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/MinimizePreemption.java b/src/main/gov/nasa/jpf/search/heuristic/MinimizePreemption.java
new file mode 100644 (file)
index 0000000..63f5316
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * a simple heuristic that tries to minimize preemptive scheduling, i.e.
+ * switching from a thread that is not blocked.
+ * 
+ * This is supposed to be a less expensive and more robust version of the old
+ * IterativContextBounding search
+ */
+public class MinimizePreemption extends SimplePriorityHeuristic {
+  
+  // an optional threshold value of preemptions that cause states to be
+  // added at the end of the queue (or discarded if queue is full)
+  int threshold;
+  
+  public MinimizePreemption (Config config, VM vm) {
+    super(config,vm);
+    
+    threshold = config.getInt("search.mp.threshold", Integer.MAX_VALUE);
+  }
+  
+  @Override
+  protected int computeHeuristicValue () {
+    int preemptions = 0;
+
+    // this is redundant, but since it is easy enough to compute we don't store it
+    // <2do> this relies on that there are no cascaded SchedulingPoints (which would not work anyways)
+    for (ThreadChoiceGenerator tcg = vm.getLastChoiceGeneratorOfType(ThreadChoiceGenerator.class); tcg != null;){
+      ThreadInfo ti= tcg.getNextChoice();
+      ThreadChoiceGenerator tcgPrev = tcg.getPreviousChoiceGeneratorOfType(ThreadChoiceGenerator.class);
+
+      if (tcg.isSchedulingPoint()){
+        if (tcgPrev != null){
+          ThreadInfo tiPrev = tcgPrev.getNextChoice();
+          if (ti != tiPrev){
+            if (tcg.contains(tiPrev)){
+              // the previous thread is still in the runnable list, so it can't be blocked or terminated
+              preemptions++;
+              
+              if (preemptions >= threshold){
+                // we don't care, it gets the lowest priority (highest heuristic value)
+                return Integer.MAX_VALUE;
+              }
+            }            
+          }
+        }
+      }
+      
+      tcg = tcgPrev;
+    }
+    
+    return preemptions;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/MostBlocked.java b/src/main/gov/nasa/jpf/search/heuristic/MostBlocked.java
new file mode 100644 (file)
index 0000000..a9687d5
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * Heuristic state prioriizer that maximizes number of blocked states. This
+ * is a classic heuristic for finding deadlocks, since a deadlock requires 
+ * all threads to be blocked.
+ */
+public class MostBlocked extends SimplePriorityHeuristic {
+
+  public MostBlocked (Config config, VM vm) {
+    super(config,vm);
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    int alive = vm.getThreadList().getMatchingCount(aliveThread);
+    int runnable = vm.getThreadList().getMatchingCount(vm.getTimedoutRunnablePredicate());
+
+    // pcm - the (iSystemState based) condition was "!runnable && alive"
+    // the '10000' is just a potential max thread count
+    int h_value = (10000 - (alive - runnable));
+
+    return h_value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/PreferThreads.java b/src/main/gov/nasa/jpf/search/heuristic/PreferThreads.java
new file mode 100644 (file)
index 0000000..843bc83
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Transition;
+
+
+/**
+ * a heuristic state prioritizer that favors certain threads (specified
+ * by thread names during initialization)
+ * 
+ * <2do> for both efficiency and encapsulation reasons, this should be just
+ * a Scheduler policy (so that we don't have to expand all children)
+ */
+public class PreferThreads extends SimplePriorityHeuristic {
+  String[] preferredThreads;
+
+  public PreferThreads (Config config, VM vm) {
+    super(config,vm);
+    
+    preferredThreads = config.getStringArray("search.heuristic.preferredThreads");
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    Transition t = vm.getLastTransition();
+
+    if (t == null) {
+      return 1;
+    }
+
+    String tn = vm.getThreadName();
+
+    for (int i = 0; i < preferredThreads.length; i++) {
+      if (tn.equals(preferredThreads[i])) {
+        return 0;
+      }
+    }
+
+    return 1;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/PrioritizedState.java b/src/main/gov/nasa/jpf/search/heuristic/PrioritizedState.java
new file mode 100644 (file)
index 0000000..b620ad6
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * HeuristicState with a scalar, static priority.
+ * Due to legacy reasons, lower values represent higher priorities
+ */
+public class PrioritizedState extends HeuristicState implements Comparable<PrioritizedState>{
+
+  int heuristicValue; // watch out, this is inverted: 0 is max priority
+
+  public PrioritizedState(VM vm, int heuristicValue) {
+    super(vm);
+    
+    this.heuristicValue = heuristicValue;
+  }
+
+  public int getPriority () {
+    return heuristicValue;
+  }
+
+  /*
+   * NOTE - since we can't use the Java 1.6 pollFirst()/pollLast() yet,
+   * we have to use remove(o), which in turn requires "compareTo(a) == 0" to
+   * be identical to "equals(o) == true", so we should implement both
+   */
+  @Override
+  public int compareTo (PrioritizedState o) {
+     int diff = heuristicValue - o.heuristicValue;
+     if (diff == 0) {
+       return (stateId - o.stateId);
+     } else {
+       return diff;
+     }
+  }
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof PrioritizedState) {
+      PrioritizedState other = (PrioritizedState) o;
+      return ((stateId == other.stateId) && (heuristicValue == other.heuristicValue));
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public String toString() {
+    return "{"+stateId+','+heuristicValue+'}';
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/RandomHeuristic.java b/src/main/gov/nasa/jpf/search/heuristic/RandomHeuristic.java
new file mode 100644 (file)
index 0000000..3b58c1d
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.VM;
+
+import java.util.Random;
+
+
+/**
+ * heuristic state prioritizer that returns random priority values
+ */
+public class RandomHeuristic extends SimplePriorityHeuristic {
+
+  protected Random random;
+
+  public RandomHeuristic (Config config, VM vm) {
+    super(config,vm);
+    
+    random = new Random( config.getInt("choice.seed", 42));
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    return java.lang.Math.abs(random.nextInt());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/SimplePriorityHeuristic.java b/src/main/gov/nasa/jpf/search/heuristic/SimplePriorityHeuristic.java
new file mode 100644 (file)
index 0000000..76c6e99
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.Predicate;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * a heuristic that is based on static priorities that are determined
+ * at state storage time
+ */
+public abstract class SimplePriorityHeuristic extends HeuristicSearch {
+
+  StaticPriorityQueue queue;
+  
+  protected Predicate<ThreadInfo> aliveThread;
+  
+  public SimplePriorityHeuristic (Config config, VM vm) {
+    super(config,vm);
+
+    queue = new StaticPriorityQueue(config);
+    
+    aliveThread = new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (ti.isAlive());
+      }
+    };
+    
+  }
+
+  protected abstract int computeHeuristicValue ();
+
+  protected int computeAstarPathCost (VM vm) {
+    return vm.getPathLength();
+  }
+  
+  @Override
+  protected HeuristicState queueCurrentState () {
+    int heuristicValue;
+    
+    if (vm.isInterestingState()) {
+      heuristicValue = 0;
+    } else if (vm.isBoringState()) {
+      heuristicValue = Integer.MAX_VALUE;
+      
+    } else {
+      heuristicValue = computeHeuristicValue();
+      
+      if (useAstar) {
+        // <2do> we probably don't want this for isInteresting/isBoring?
+        heuristicValue += computeAstarPathCost(vm);
+      }
+    }
+    
+    PrioritizedState hState = new PrioritizedState(vm,heuristicValue);
+    
+    queue.add(hState);
+    
+    return hState;
+  }
+  
+  @Override
+  protected HeuristicState getNextQueuedState () {
+    
+    //HeuristicState hState = queue.pollFirst();  // only Java 1.6
+    //if (isBeanSearch) { queue.clear(); }
+    //return hState;
+
+    if (queue.size() == 0) {                      // the dreaded Java 1.5 version
+      return null;
+    }
+    HeuristicState hState = queue.first();
+    
+    if (isBeamSearch) {
+      queue.clear();
+    } else {
+      queue.remove(hState);      
+    }
+    
+    return hState;
+  }
+
+  @Override
+  public int getQueueSize() {
+    return queue.size();
+  }
+  
+  @Override
+  public boolean isQueueLimitReached() {
+    return queue.isQueueLimitReached();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/StaticPriorityQueue.java b/src/main/gov/nasa/jpf/search/heuristic/StaticPriorityQueue.java
new file mode 100644 (file)
index 0000000..c68d677
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+
+import java.util.Iterator;
+import java.util.TreeSet;
+
+/**
+ * container for statically prioritized states, based on bounded
+ * RB trees
+ * 
+ * TreeSet is a better choice than PriorityQueue since the size
+ * constraint means we have to remove elements from both ends, which
+ * is inefficient with heaps. A simple (binary) sorted array using
+ * block copy can be more efficient, but that very much depends on
+ * queue size and processor, so it's not really worth the trouble
+ * 
+ * <2do> this needs to be re-evaluated in light of the stupid fact
+ * that pollFirst()/pollLast() are only in Java 1.6 and we can't switch
+ * yet (no Java 1.6 for PPC), which forces us to do some shuffling to
+ * make remove(last()) work as expected
+ */
+@SuppressWarnings("serial")
+public class StaticPriorityQueue extends TreeSet<PrioritizedState> {
+
+  int maxQueueSize;
+  
+  public StaticPriorityQueue (Config config) {
+    // unbound queues are pretty pointless - we could probably better use
+    // DFSearch in this case
+    maxQueueSize = config.getInt("search.heuristic.queue_limit", 1024);
+    if (maxQueueSize < 0){
+      maxQueueSize = Integer.MAX_VALUE;
+    }
+  }
+    
+  @Override
+  public boolean add (PrioritizedState s) {
+    if (size() < maxQueueSize) { // limit not yet reached
+      return super.add(s);
+      
+    } else {
+      PrioritizedState last = last();
+      if (s.compareTo(last) < 0) {
+        //pollLast();   // that's only Java 1.6
+        remove(last);
+        
+        return super.add(s);
+        
+      } else {
+        // don't add with this priority value
+        return false;
+      }
+    }
+  }
+  
+  public boolean isQueueLimitReached() {
+    return size() >= maxQueueSize;
+  }
+  
+  // for debugging purposes
+  void dump() {
+    int i=0;
+    System.err.print('[');
+    for (Iterator<PrioritizedState> it=iterator(); it.hasNext();) {
+      if (i++ > 0) {
+        System.err.print(',');
+      }
+      System.err.print(it.next());
+    }
+    System.err.println(']');
+  }
+}
diff --git a/src/main/gov/nasa/jpf/search/heuristic/UserHeuristic.java b/src/main/gov/nasa/jpf/search/heuristic/UserHeuristic.java
new file mode 100644 (file)
index 0000000..cd83936
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.search.heuristic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.JPF_gov_nasa_jpf_vm_Verify;
+import gov.nasa.jpf.vm.VM;
+
+
+/**
+ * heuristic state prioritizer that is controlled by the system under test, which can
+ * use Verify.get/set/resetHeuristicSearchValue() to compute priorities
+ */
+public class UserHeuristic extends SimplePriorityHeuristic {
+  public UserHeuristic (Config config, VM vm) {
+    super(config, vm);
+  }
+
+  @Override
+  protected int computeHeuristicValue () {
+    return JPF_gov_nasa_jpf_vm_Verify.heuristicSearchValue;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/tool/GenPeer.java b/src/main/gov/nasa/jpf/tool/GenPeer.java
new file mode 100644 (file)
index 0000000..59d7101
--- /dev/null
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import gov.nasa.jpf.vm.Types;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+
+/**
+ * tool to automatically generate the framework of a native peer MJI class,
+ * given it's model class. GenPeer collects all the native methods from the 
+ * model class, and creates the corresponding native peer methods
+ */
+public class GenPeer {
+  static final String SYS_PKG = "gov.nasa.jpf.vm";
+  static final String MJI_ENV = "gov.nasa.jpf.vm.MJIEnv";
+  static final String NATIVEPEER = "gov.nasa.jpf.vm.NativePeer";
+  static final String INDENT = "  ";
+  static final String SUPERCLASS = "NativePeer";
+  static final String MJI_ANN = "@MJI";
+  static final String METHOD_PREFIX = "public";
+  static final String ENV_ARG = "MJIEnv env";
+  static final String OBJ_ARG = "int objRef";
+  static final String CLS_ARG = "int clsObjRef";
+  static final String REF_TYPE = "int";
+  static final String NULL = "MJIEnv.NULL";
+
+  static String       clsName;
+  static String[]     mths;
+
+  // our options
+  static boolean isSystemPkg;
+  static boolean allMethods;
+  static boolean mangleNames;
+  static boolean clinit;
+
+  public static void main (String[] args) {
+    if ((args.length == 0) || !readOptions(args)) {
+      showUsage();
+
+      return;
+    }
+
+    PrintWriter pw = new PrintWriter(System.out, true);
+    Class<?>       cls = getClass(clsName);
+
+    if (cls != null) {
+      printNativePeer(cls, pw);
+    }
+  }
+
+  static Class<?> getClass (String cname) {
+    Class<?> clazz = null;
+
+    try {
+      clazz = Class.forName(cname);
+    } catch (ClassNotFoundException cnfx) {
+      System.err.println("target class not found: " + cname);
+    } catch (Throwable x) {
+      x.printStackTrace();
+    }
+
+    return clazz;
+  }
+
+  static boolean isMJICandidate (Method m) {
+    if (allMethods) {
+      return true;
+    }
+
+    if (mths != null) {
+      String name = m.getName();
+
+      for (int i = 0; i < mths.length; i++) {
+        if (name.equals(mths[i])) {
+          return true;
+        }
+      }
+    } else {
+      if ((m.getModifiers() & Modifier.NATIVE) != 0) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  static void getMangledName (Method m) {
+    StringBuilder sb = new StringBuilder(50);
+
+    sb.append(m.getName());
+    sb.append("__");
+  }
+
+  static boolean isPrimitiveType (String t) {
+    return ("int".equals(t) || "long".equals(t) || "boolean".equals(t) || 
+           "void".equals(t) || // not really, but useful for returnTypes
+           "byte".equals(t) || "char".equals(t) || "short".equals(t) || 
+           "float".equals(t) || "double".equals(t));
+  }
+
+  static void printClinit (PrintWriter pw) {
+    pw.print(INDENT);
+    pw.print(METHOD_PREFIX);
+    pw.print(" void $clinit (");
+    pw.print(ENV_ARG);
+    pw.print(", ");
+    pw.print(CLS_ARG);
+    pw.println(") {");
+    pw.print(INDENT);
+    pw.println("}");
+  }
+
+  static void printFooter (Class<?> cls, PrintWriter pw) {
+    pw.println("}");
+  }
+
+  static void printHeader (Class<?> cls, PrintWriter pw) {
+    if (isSystemPkg) {
+      pw.print("package ");
+      pw.print(SYS_PKG);
+      pw.println(';');
+      pw.println();
+    }
+
+    pw.print("import ");
+    pw.print(MJI_ENV);
+    pw.println(";");
+    pw.print("import ");
+    pw.print(NATIVEPEER);
+    pw.println(";");
+    pw.println();
+
+    String cname = cls.getName().replace('.', '_');
+
+    pw.print("public class ");
+    pw.print("JPF_");
+    pw.print(cname);
+    pw.print(" extends ");
+    pw.print(SUPERCLASS);
+    pw.println(" {");
+  }
+
+  static void printMethodBody (String rt, String t, PrintWriter pw) {
+    if (!"void".equals(rt)) {
+      pw.print(INDENT);
+      pw.print(INDENT);
+      pw.print(rt);
+
+      if ((rt == REF_TYPE) && (rt != t)) {
+        pw.print(" r");
+        pw.print(t);
+        pw.print(" = ");
+        pw.print(NULL);
+        pw.println(";");
+
+        pw.print(INDENT);
+        pw.print(INDENT);
+        pw.print("return r");
+        pw.print(t);
+        pw.println(";");
+      } else {
+        pw.print(" v = (");
+        pw.print(rt);
+        pw.println(")0;");
+
+        pw.print(INDENT);
+        pw.print(INDENT);
+        pw.println("return v;");
+      }
+    }
+  }
+
+  static void printMethodName (Method m, PrintWriter pw) {
+    String name = null;
+
+    if (mangleNames) {
+      name = Types.getJNIMangledMethodName(m);
+    } else {
+      name = m.getName();
+    }
+
+    pw.print(name);
+  }
+
+  static void printMJIAnnotation(PrintWriter pw) {
+    pw.print(INDENT);
+    pw.println(MJI_ANN);
+  }
+
+  static void printMethodStub (String condPrefix, Method m, PrintWriter pw) {
+    String t = null;
+    String rt;
+
+    printMJIAnnotation(pw);
+
+    pw.print(INDENT);
+    pw.print(METHOD_PREFIX);
+    pw.print(' ');
+
+    if (condPrefix == null) {
+      t = rt = stripType(m.getReturnType().getName());
+
+      if (!isPrimitiveType(rt)) {
+        rt = REF_TYPE;
+      }
+    } else {
+      rt = "boolean";
+    }
+
+    pw.print(rt);
+
+    pw.print(' ');
+
+    if (condPrefix != null) {
+      pw.print(condPrefix);
+    }
+
+    printMethodName(m, pw);
+    pw.print(" (");
+
+    printStdArgs(m, pw);
+    printTargetArgs(m, pw);
+
+    pw.println(") {");
+
+    if (condPrefix == null) {
+      printMethodBody(rt, stripType(null, t), pw);
+    } else {
+      pw.print(INDENT);
+      pw.print(INDENT);
+      pw.println("return true;");
+    }
+
+    pw.print(INDENT);
+    pw.println('}');
+  }
+
+  static void printNativePeer (Class<?> cls, PrintWriter pw) {
+    Method[] mths = cls.getDeclaredMethods();
+
+    printHeader(cls, pw);
+
+    if (clinit) {
+      printClinit(pw);
+    }
+
+    for (int i = 0; i < mths.length; i++) {
+      Method m = mths[i];
+
+      if (isMJICandidate(m)) {
+        pw.println();
+        printMethodStub(null, m, pw);
+      }
+    }
+
+    printFooter(cls, pw);
+  }
+
+  static void printStdArgs (Method m, PrintWriter pw) {
+    pw.print(ENV_ARG);
+    pw.print(", ");
+
+    if ((m.getModifiers() & Modifier.STATIC) != 0) {
+      pw.print(CLS_ARG);
+    } else {
+      pw.print(OBJ_ARG);
+    }
+  }
+
+  static void printTargetArgs (Method m, PrintWriter pw) {
+    Class<?>[] pt = m.getParameterTypes();
+
+    for (int i = 0; i < pt.length; i++) {
+      String  t = stripType(pt[i].getName());
+      boolean isPrim = isPrimitiveType(t);
+
+      pw.print(", ");
+
+      if (isPrim) {
+        pw.print(t);
+        pw.print(" v");
+        pw.print(i);
+      } else {
+        pw.print(REF_TYPE);
+        pw.print(" r");
+        pw.print(stripType(null, t));
+        pw.print(i);
+      }
+    }
+  }
+
+  static String[] readNames (String[] args, int i) {
+    ArrayList<String> a = null;
+
+    for (; (i < args.length) && (args[i].charAt(0) != '-'); i++) {
+      if (a == null) {
+        a = new ArrayList<String>();
+      }
+
+      a.add(args[i]);
+    }
+
+    if (a != null) {
+      String[] names = new String[a.size()];
+      a.toArray(names);
+
+      return names;
+    }
+
+    return null;
+  }
+
+  static boolean readOptions (String[] args) {
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i];
+
+      if ("-s".equals(arg)) {
+        isSystemPkg = true;
+      } else if ("-m".equals(arg)) {
+        mangleNames = true;
+      } else if ("-a".equals(arg)) {
+        allMethods = true;
+      } else if ("-ci".equals(arg)) {
+        clinit = true;
+      } else if (arg.charAt(0) != '-') {
+        // rather simple
+        if (clsName == null) {
+          clsName = arg;
+        } else {
+          mths = readNames(args, i);
+          i += mths.length;
+        }
+      } else {
+        System.err.println("unknown option: " + arg);
+        showUsage();
+
+        return false;
+      }
+    }
+
+    return (clsName != null);
+  }
+
+  static void showUsage () {
+    System.out.println(
+          "usage:   'GenPeer [<option>..] <className> [<method>..]'");
+    System.out.println("options:  -s  : system peer class (gov.nasa.jpf.vm)");
+    System.out.println("          -ci : create <clinit> MJI method");
+    System.out.println("          -m  : create mangled method names");
+    System.out.println("          -a  : create MJI methods for all target class methods");
+  }
+
+  static String stripType (String s) {
+    return stripType("java.lang", s);
+  }
+
+  static String stripType (String prefix, String s) {
+    int i = s.lastIndexOf('.') + 1;
+    int l = s.length() - 1;
+
+    if (s.charAt(l) == ';') {
+      s = s.substring(0, l);
+    }
+
+    if (prefix == null) {
+      if (i == 0) {
+        return s;
+      } else {
+        return s.substring(i);
+      }
+    } else {
+      if (s.startsWith(prefix) && (prefix.length() + 1 == i)) {
+        return s.substring(i);
+      } else {
+        return s;
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/tool/LogConsole.java b/src/main/gov/nasa/jpf/tool/LogConsole.java
new file mode 100644 (file)
index 0000000..e77a4b9
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+/**
+ * simple logging facility that listens on a socket (e.g. for log output display)
+ */
+public class LogConsole {
+  
+  static int DEF_PORT = 20000; // keep this in sync with the gov.nasa.jpf.util.LogHandler
+  
+  class ShutdownHook implements Runnable {
+    @Override
+       public void run () {
+      if (running) {
+        // not very threadsafe, but worst thing that can happen is we close twice
+        killed = true;
+        System.out.println("\nLogConsole killed, shutting down");
+      }
+      try {
+        cs.close();
+        ss.close();
+      } catch (Throwable t) {
+        // not much we can do here anyway
+      }
+    }
+  }
+  
+  
+  boolean running;
+  
+  int port;
+  boolean autoclose;
+  boolean killed;
+  
+  ServerSocket ss;
+  Socket cs;
+  
+  public void run () {
+    running = true;
+    Runtime.getRuntime().addShutdownHook(new Thread(new ShutdownHook()));
+    
+    if (port == 0) {
+      port = DEF_PORT;
+    }
+    
+    try {
+      ss = new ServerSocket(port);
+      
+      try {          
+        do {
+          System.out.println("LogConsole listening on port: " + port);
+
+          cs = ss.accept();
+          BufferedReader in = new BufferedReader( new InputStreamReader(cs.getInputStream()));
+          String msg; 
+          
+          System.out.println("LogConsole connected");
+          System.out.println("--------------------------------------------------------------------------------");
+          try {
+            
+            while ((msg = in.readLine()) != null) {
+              System.out.println(msg);
+            }
+            
+            System.out.println("--------------------------------------------------------------------------------");            
+            System.out.println("LogConsole disconnected");
+          } catch (IOException iox) {
+            System.err.println(iox);
+          }
+
+          in.close();
+          cs.close();
+        } while (!autoclose);
+
+        System.out.println("LogConsole closing");
+        
+      } catch (IOException iox) {
+        if (!killed) {
+          System.err.println("Error: LogConsole accept failed on port: " + port);
+        }
+      }
+      
+    } catch (IOException iox) {
+      System.err.println("Error: LogConsole cannot listen on port: " + port);
+    }
+    
+    running = false;
+  }
+
+  public void showUsage () {
+    System.out.println("LogConsole: socket based console logger");
+    System.out.println("     usage: java gov.nasa.jpf.tools.LogConsole {flags} [<port>]");
+    System.out.println("      args: -help         show this message");
+    System.out.println("            -autoclose    close the application upon disconnect");
+    System.out.println("            <port>        optional port number, default: " + DEF_PORT);
+  }
+  
+  boolean processArgs (String[] args) {
+    for (int i=0; i<args.length; i++) {
+      if (args[i].charAt(0) == '-') {
+        if (args[i].equals("-autoclose")) {
+          args[i] = null;
+          autoclose = true;
+        } else if (args[i].equals("-help")) {
+          showUsage();
+          return false;
+        } else {
+          System.err.println("Warning: unknown argument (see -help for usage): " + args[i]);
+        }
+      } else {
+        if (args[i].matches("[0-9]+")) {
+          if (port != 0) {
+            System.err.println("Error: only one port parameter allowed (see -help for usage): " + args[i]);
+            return false;
+          }
+          
+          try {
+            port = Integer.parseInt(args[i]);
+          } catch (NumberFormatException nfx) {
+            System.err.println("Error: illegal port spec: " + args[i]);
+            return false;
+          }
+        } else {
+          System.out.println("Error: unknown argument: " + args[i]);
+          return false;
+        }
+      }
+    }
+    
+    return true;
+  }
+
+  public static void main (String[] args) {
+    LogConsole console = new LogConsole();
+    
+    if (console.processArgs(args)) {
+      console.run();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/tool/PrintEvents.java b/src/main/gov/nasa/jpf/tool/PrintEvents.java
new file mode 100644 (file)
index 0000000..2084f9b
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFClassLoader;
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.JPFSiteUtils;
+import gov.nasa.jpf.util.event.EventTree;
+
+
+/**
+ * very simple tool to print .util.script.EventTrees
+ * 
+ * <2do> this should use native_classpath / JPFClassLoader to load the EventTree
+ */
+public class PrintEvents {
+
+  static boolean printTree;
+  static boolean printPaths;
+  static String clsName;
+  
+  static void showUsage () {
+    System.out.println("usage:   'PrintEvents [<option>..] <className>'");
+    System.out.println("options:  -t  : print tree");
+    System.out.println("          -p  : print paths");
+  }
+
+  static boolean readOptions (String[] args) {
+    for (int i = 0; i < args.length; i++) {
+      String arg = args[i];
+
+      if ("-t".equals(arg)) {
+        printTree = true;
+      } else if ("-p".equals(arg)) {
+        printPaths = true;
+      } else if (arg.charAt(0) != '-') {
+          clsName = arg;
+          if (clsName.charAt(0) == '.'){
+            clsName = "gov.nasa.jpf" + clsName;
+          }
+      } else {
+        System.err.println("unknown option: " + arg);
+        showUsage();
+
+        return false;
+      }
+    }
+
+    return (clsName != null);
+  }
+
+  static String[] getTestPathElements (Config config){
+    String projectId = JPFSiteUtils.getCurrentProjectId();
+    
+    if (projectId != null) {
+      String testCpKey = projectId + ".test_classpath";
+      return  config.getCompactTrimmedStringArray(testCpKey);
+      
+    } else {
+      return new String[0];
+    }    
+  }
+  
+  static void addTestClassPath (JPFClassLoader cl, String[] testPathElements){
+    if (testPathElements != null) {
+      for (String pe : testPathElements) {
+        try {
+          cl.addURL(FileUtils.getURL(pe));
+        } catch (Throwable x) {
+          System.err.println("malformed test_classpath URL: " + pe);
+        }
+      }
+    }
+  }
+  
+  public static void main (String[] args){
+    if ((args.length == 0) || !readOptions(args)) {
+      showUsage();
+    }
+     
+    Config config = new Config(args);
+    String[] testPathElements = getTestPathElements(config);
+    JPFClassLoader cl = config.initClassLoader(RunTest.class.getClassLoader());
+    addTestClassPath( cl, testPathElements);
+
+    try {     
+      Class<EventTree> cls = (Class<EventTree>)cl.loadClass(clsName);
+      EventTree et = cls.newInstance();
+      
+      if (printTree){
+        System.out.println("---------------- event tree of " + clsName);
+        et.printTree();
+      }
+      
+      if (printPaths){
+        System.out.println("---------------- event paths of " + clsName);
+        et.printPaths();
+      }
+    } catch (ClassNotFoundException cnfx){
+      System.err.println("class not found: " + clsName);
+    } catch (NoClassDefFoundError ncdf){
+      System.err.println("class does not load: " + ncdf.getMessage());      
+    } catch (InstantiationException ex) {
+      System.err.println("cannot instantiate: " + ex.getMessage());      
+    } catch (IllegalAccessException ex) {
+      System.err.println("cannot instantiate: " + ex.getMessage());      
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/tool/Run.java b/src/main/gov/nasa/jpf/tool/Run.java
new file mode 100644 (file)
index 0000000..0aa61c8
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * common base for Run* classes
+ */
+public class Run {
+
+  protected static void error (String msg){
+    System.err.print("error: ");
+    System.err.println(msg);
+    System.exit(1);
+  }
+
+  protected static void warning (String msg){
+    System.err.print("warning: ");
+    System.err.println(msg);
+  }
+  
+  // filter out leading '+' arguments (Config initialization)
+  protected static String[] removeConfigArgs(String[]args){
+    int i;
+    for (i=0; i<args.length; i++){
+      String a = args[i];
+      if (a != null && a.length() > 0 && a.charAt(0) != '+'){
+        break;
+      }
+    }
+
+    String[] newArgs = new String[args.length - i];
+    System.arraycopy(args,i,newArgs,0,newArgs.length);
+
+    return newArgs;
+  }
+
+  protected static String checkClassName (String cls){
+    if (cls == null || cls.isEmpty()){
+      return null;
+    }
+
+    if (cls.charAt(0) == '.'){
+      cls = "gov.nasa.jpf" + cls;
+    }
+
+    return cls;
+  }
+
+  protected static boolean call( Class<?> cls, String mthName, Object[] args) throws InvocationTargetException {
+    try {
+      Class<?>[] argTypes = new Class<?>[args.length];
+      for (int i=0; i<args.length; i++){
+        argTypes[i] = args[i].getClass();
+      }
+
+      Method m = cls.getMethod(mthName, argTypes);
+
+      int modifiers = m.getModifiers();
+      if (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers)){
+        m.invoke(null, args);
+        return true;
+      }
+
+    } catch (NoSuchMethodException nsmx){
+      return false;
+    } catch (IllegalAccessException iax){
+      return false;
+    }
+
+    return false;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/tool/RunJPF.java b/src/main/gov/nasa/jpf/tool/RunJPF.java
new file mode 100644 (file)
index 0000000..ae16177
--- /dev/null
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFShell;
+import gov.nasa.jpf.util.JPFSiteUtils;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * This class is a wrapper for loading JPF or a JPFShell through a classloader
+ * that got initialized from a Config object (i.e. 'native_classpath').
+ *
+ * This is the main-class entry in the executable RunJPF.jar, which does not
+ * require any JPF specific classpath settings, provided the site.properties
+ * is configured correctly
+ *
+ * NOTE this class is not allowed to use any types that would require
+ * loading JPF classes during class resolution - this would result in
+ * NoClassDefFoundErrors if the respective class is not in RunJPF.jar
+ */
+public class RunJPF extends Run {
+
+  public static final int HELP         = 0x1;
+  public static final int SHOW         = 0x2;
+  public static final int LOG          = 0x4;
+  public static final int BUILD_INFO   = 0x8;
+  public static final int ADD_PROJECT  = 0x10;
+  public static final int VERSION      = 0x20;
+  public static final int DELAY_START  = 0x40;
+  public static final int DELAY_EXIT   = 0x80;
+
+  static final String JPF_CLASSNAME = "gov.nasa.jpf.JPF";
+
+  static void delay (String msg) {
+    System.out.println(msg);
+    try {
+      System.in.read();
+    } catch (IOException iox) {
+      // we don't care
+    }    
+  }
+  
+  public static void main (String[] args) {
+    try {
+      int options = getOptions(args);
+
+      if (args.length == 0 || isOptionEnabled(HELP, options)) {
+        showUsage();
+        return;
+      }
+
+      if (isOptionEnabled(ADD_PROJECT, options)){
+        addProject(args);
+        return;
+      }
+      
+      if (isOptionEnabled(DELAY_START, options)) {
+        delay("press any key to start");
+      }
+      
+      if (isOptionEnabled(LOG, options)) {
+        Config.enableLogging(true);
+      }
+
+      Config conf = new Config(args);
+
+      if (isOptionEnabled(SHOW, options)) {
+        conf.printEntries();
+      }
+
+      ClassLoader cl = conf.initClassLoader(RunJPF.class.getClassLoader());
+
+      if (isOptionEnabled(VERSION, options)) {
+        showVersion(cl);
+      }
+
+      if (isOptionEnabled(BUILD_INFO, options)) {
+        showBuild(cl);
+      }
+
+      // using JPFShell is Ok since it is just a simple non-derived interface
+      // note this uses a <init>(Config) ctor in the shell class if there is one, i.e. there is no need for a separate
+      // start(Config,..) or re-loading the config itself
+      JPFShell shell = conf.getInstance("shell", JPFShell.class);
+      if (shell != null) {
+        shell.start( removeConfigArgs(args)); // responsible for exception handling itself
+
+      } else {
+        // we have to load JPF explicitly through the URLClassLoader, and
+        // call its start() via reflection - interfaces would only work if
+        // we instantiate a JPF object here, which would force us to duplicate all
+        // the logging and event handling that preceedes JPF instantiation
+        Class<?> jpfCls = cl.loadClass(JPF_CLASSNAME);
+        if (!call( jpfCls, "start", new Object[] {conf,args})){
+          error("cannot find 'public static start(Config,String[])' in " + JPF_CLASSNAME);
+        }
+      }
+      
+      if (isOptionEnabled(DELAY_EXIT, options)) {
+        delay("press any key to exit");
+      }
+
+      
+    } catch (NoClassDefFoundError ncfx){
+      ncfx.printStackTrace();
+    } catch (ClassNotFoundException cnfx){
+      error("cannot find " + JPF_CLASSNAME);
+    } catch (InvocationTargetException ix){
+      // should already be handled by JPF
+      ix.getCause().printStackTrace();
+    }
+    
+  }
+
+  public static int getOptions (String[] args){
+    int mask = 0;
+
+    if (args != null){
+
+      for (int i = 0; i < args.length; i++) {
+        String a = args[i];
+        if ("-help".equals(a)){
+          args[i] = null;
+          mask |= HELP;
+
+        } else if ("-show".equals(a)) {
+          args[i] = null;
+          mask |= SHOW;
+
+        } else if ("-log".equals(a)){
+          args[i] = null;
+          mask |= LOG;
+
+        } else if ("-buildinfo".equals(a)){
+          args[i] = null;
+          mask |= BUILD_INFO;
+          
+        } else if ("-addproject".equals(a)){
+          args[i] = null;
+          mask |= ADD_PROJECT;
+
+        } else if ("-delay-start".equals(a)) {
+          args[i] = null;
+          mask |= DELAY_START;
+          
+        } else if ("-delay-exit".equals(a)) {
+          args[i] = null;
+          mask |= DELAY_EXIT;
+          
+        } else if ("-version".equals(a)){
+          args[i] = null;
+          mask |= VERSION;
+        }
+      }
+    }
+
+    return mask;
+  }
+
+  public static boolean isOptionEnabled (int option, int mask){
+    return ((mask & option) != 0);
+  }
+
+  public static void showUsage() {
+    System.out.println("Usage: \"java [<vm-option>..] -jar ...RunJPF.jar [<jpf-option>..] [<app> [<app-arg>..]]");
+    System.out.println("  <jpf-option> : -help : print usage information and exit");
+    System.out.println("               | -version : print JPF version information");    
+    System.out.println("               | -buildinfo : print build and runtime information");
+    System.out.println("               | -addproject [init] [<pathname>] : add project to site properties and exit");    
+    System.out.println("               | -log : print configuration initialization steps");
+    System.out.println("               | -show : print configuration dictionary contents");
+    System.out.println("               | +<key>=<value>  : add or override key/value pair to config dictionary");
+    System.out.println("  <app>        : *.jpf application properties file pathname | fully qualified application class name");
+    System.out.println("  <app-arg>    : arguments passed into main() method of application class");
+  }
+  
+  public static void addProject(String[] args){
+    boolean init = false;
+    int i=0;
+    String sitePathName = null;
+    
+    // check if the first non-null arg is 'init', which means this project
+    // should be added to the 'extensions' list
+    for(; i<args.length; i++){
+      if (args[i] != null){
+        if ("init".equals(args[i])){
+          init = true;
+          continue;
+        } else {
+          sitePathName = args[i];
+        }
+        break;
+      }
+    }
+    
+    File siteProps = (sitePathName == null) ? JPFSiteUtils.getStandardSiteProperties() : new File(sitePathName);
+    if (siteProps == null) {
+      siteProps = new File(JPFSiteUtils.getGlobalSitePropertiesPath());
+    }
+    
+    File curDir = new File( System.getProperty("user.dir"));
+    String pid = JPFSiteUtils.getCurrentProjectId();
+    if (pid == null){
+      error("current dir not a valid JPF project: " + curDir);
+    }
+    
+    if ("jpf-core".equals(pid)){ // jpf-core always needs to be in the extensions list
+      init = true;
+    }
+    
+    if (JPFSiteUtils.addProject( siteProps, pid, curDir, init)){
+      System.out.println("added project '" + pid + "' to site properties at: " + siteProps);
+    } else {
+      error("failed to add project: '" + pid + "' to site properties at: " + siteProps);
+    }
+  }
+
+  public static void showVersion (ClassLoader cl){
+    try {
+      InputStream is = cl.getResourceAsStream("gov/nasa/jpf/.version");
+      if (is != null){
+        System.out.print("JPF version: ");
+        
+        int len = is.available();
+        byte[] data = new byte[len];
+        is.read(data);
+        is.close();
+        String version = new String(data);
+        System.out.println(version);
+        
+      } else {
+        System.out.println("no JPF version information available");
+      }
+      
+
+    } catch (Throwable t){
+      System.err.println("error reading version information: " + t.getMessage());
+    }    
+  }
+  
+  // print out the build.properties settings
+  public static void showBuild(ClassLoader cl) {
+    try {
+      InputStream is = cl.getResourceAsStream("gov/nasa/jpf/build.properties");
+      if (is != null){
+        System.out.println("JPF build information:");
+
+        Properties buildProperties = new Properties();
+        buildProperties.load(is);
+
+        for (Map.Entry<Object, Object> e : buildProperties.entrySet()) {
+          System.out.print('\t');
+          System.out.print(e.getKey());
+          System.out.print(" = ");
+          System.out.println(e.getValue());
+        }
+
+        is.close();
+
+      } else {
+        System.out.println("no JPF build information available");
+      }
+
+    } catch (Throwable t){
+      System.err.println("error reading build information: " + t.getMessage());
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/tool/RunTest.java b/src/main/gov/nasa/jpf/tool/RunTest.java
new file mode 100644 (file)
index 0000000..2abecfb
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.tool;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFClassLoader;
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.JPFSiteUtils;
+import java.io.File;
+import java.lang.reflect.Field;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * tool to run JPF test with configured classpath
+ *
+ * arguments are supposed to be of type
+ *
+ *   {<config-option>} <JPF-test-class> {<test-method>}
+ *
+ * all leading config options are used to create the initial Config, but be
+ * aware of that each test (TestJPF.verifyX() invocation) uses its own
+ * Config and JPF object, i.e. can have different path settings
+ *
+ * This automatically adds <project>.test_classpath to the startup classpath
+ */
+public class RunTest extends Run {
+
+  public static final int HELP  = 0x1;
+  public static final int SHOW  = 0x2;
+  public static final int LOG   = 0x4;
+  public static final int QUIET = 0x8;
+
+  static final String TESTJPF_CLS = "gov.nasa.jpf.util.test.TestJPF";
+  
+  static Config config;
+
+  public static Config getConfig(){
+    return config;
+  }
+
+  public static class Failed extends RuntimeException {
+    public Failed (){
+    }
+  }
+
+  public static int getOptions (String[] args){
+    int mask = 0;
+
+    if (args != null){
+
+      for (int i = 0; i < args.length; i++) {
+        String a = args[i];
+        if ("-help".equals(a)){
+          args[i] = null;
+          mask |= HELP;
+
+        } else if ("-show".equals(a)) {
+          args[i] = null;
+          mask |= SHOW;
+
+        } else if ("-log".equals(a)){
+          args[i] = null;
+          mask |= LOG;
+
+        } else if ("-quiet".equals(a)){
+          args[i] = null;
+          mask |= QUIET;
+        }
+      }
+    }
+
+    return mask;
+  }
+
+  public static boolean isOptionEnabled (int option, int mask){
+    return ((mask & option) != 0);
+  }
+
+  public static void showUsage() {
+    System.out.println("Usage: \"java [<vm-option>..] -jar ...RunTest.jar [<jpf-option>..] [<class> [<app-arg>..]]");
+    System.out.println("  <jpf-option> : -help : print usage information and exit");
+    System.out.println("               | -log : print configuration initialization steps");
+    System.out.println("               | -show : print configuration dictionary contents"); 
+    System.out.println("               | -quiet : don't show System.out test output");
+    System.out.println("               | +<key>=<value>  : add or override <key>/<value> pair to global config");
+    System.out.println("               | +test.<key>=<value>  : add or override <key>/<value> pair in test config");
+    System.out.println("  <class>      : application class name");
+    System.out.println("  <methods>    : test methods of application class");
+  }
+  
+  public static void main(String[] args) {
+    int options = getOptions( args);
+    
+    if (isOptionEnabled(HELP, options)) {
+      showUsage();
+      return;
+    }
+
+    if (isOptionEnabled(LOG, options)) {
+      Config.enableLogging(true);
+    }
+
+    config = new Config(args);
+
+    if (isOptionEnabled(SHOW, options)) {
+      config.printEntries();
+    }
+    
+    args = removeConfigArgs( args);
+    String testClsName = getTestClassName(args);
+    String[] testArgs = getTestArgs(args);
+    
+    String[] testPathElements = getTestPathElements(config);
+    JPFClassLoader cl = config.initClassLoader(RunTest.class.getClassLoader());
+    addTestClassPath( cl, testPathElements);
+
+    Class<?> testJpfCls = null;
+    try {
+      testJpfCls = cl.loadClass( TESTJPF_CLS);
+      
+      if (isOptionEnabled(QUIET, options)){
+        Field f = testJpfCls.getDeclaredField("quiet");
+        f.setAccessible(true);
+        f.setBoolean( null, true);
+      }
+      
+    } catch (NoClassDefFoundError ncfx) {
+      error("class did not resolve: " + ncfx.getMessage());
+      return;
+    } catch (ClassNotFoundException cnfx) {
+      error("class not found " + cnfx.getMessage() + ", check native_classpath in jpf.properties");
+      return;
+      
+    // we let these pass for now since it only means the quiet option is not going to work
+    } catch (NoSuchFieldException ex) {
+      warning("incompatible " + TESTJPF_CLS + " version, quiet mode will not work");
+    } catch (IllegalAccessException ex) {
+      warning("incompatible " + TESTJPF_CLS + " version, quiet mode will not work");
+    }
+    
+    // <2do> refactor - each test class should be (optionally) loaded through a new ClassLoader instance
+    // to make sure tests don't have static field carry-over
+    
+    List<Class<?>> testClasses = getTestClasses(cl, testJpfCls, testPathElements, testClsName);
+    if (testClasses.isEmpty()){
+      System.out.println("no test classes found");
+      return;
+    }
+    
+    int nTested = 0;
+    int nPass = 0;
+    
+    for (Class<?> testCls : testClasses){
+      nTested++;
+      
+      try {
+        try {
+          try { // check if there is a main(String[]) method
+            Method mainEntry = testCls.getDeclaredMethod("main", String[].class);
+            mainEntry.invoke(null, (Object) testArgs);
+
+          } catch (NoSuchMethodException x) { // no main(String[]), call TestJPF.runTests(testCls,args) directly
+            Method mainEntry = testJpfCls.getDeclaredMethod("runTests", Class.class, String[].class);
+            mainEntry.invoke(null, new Object[]{testCls, testArgs});
+          }
+          
+          nPass++;
+          
+        } catch (NoSuchMethodException x) {
+          error("no suitable main() or runTests() in " + testCls.getName());
+        } catch (IllegalAccessException iax) {
+          error(iax.getMessage());
+        }
+        
+      } catch (NoClassDefFoundError ncfx) {
+        error("class did not resolve: " + ncfx.getMessage());
+      } catch (InvocationTargetException ix) {
+        Throwable cause = ix.getCause();
+        if (cause instanceof Failed){
+          // no need to report - the test did run and reported why it failed
+          System.exit(1);
+        } else {
+          error(ix.getCause().getMessage());
+        }
+      }
+    }    
+        
+    System.out.println();
+    System.out.printf("tested classes: %d, passed: %d\n", nTested, nPass);
+  }
+
+  static Class<?> loadTestClass (JPFClassLoader cl, Class<?> testJpfCls, String testClsName){
+    try {
+      Class<?> testCls = cl.loadClass(testClsName);
+      if (testJpfCls.isAssignableFrom(testCls)){
+        if (!Modifier.isAbstract(testCls.getModifiers())){
+          return testCls;
+        }
+      }
+      
+      return null;
+      
+    } catch (NoClassDefFoundError ncfx) {
+      error("class did not resolve: " + ncfx.getMessage());
+      return null;
+      
+    } catch (ClassNotFoundException cnfx) {
+      error("class not found " + cnfx.getMessage() + ", check test_classpath in jpf.properties");
+      return null;
+    }
+  }
+  
+  static boolean hasWildcard (String pattern){
+    return (pattern.indexOf('*') >= 0);
+  }
+  
+  static List<Class<?>> getTestClasses (JPFClassLoader cl, Class<?> testJpfCls, String[] testPathElements, String testClsPattern ){
+    List<Class<?>> testClasses = new ArrayList<Class<?>>();
+    
+    if (testClsPattern.startsWith(".")){
+      testClsPattern = "gov.nasa.jpf" + testClsPattern;
+    }
+    
+    if (!hasWildcard(testClsPattern)){ // that's simple, no need to look into dirs
+      Class<?> testCls = loadTestClass( cl, testJpfCls, testClsPattern);
+      if (testCls == null){ // error if this was an explicit classname
+        error ("specified class name not found or no TestJPF derived class: " + testClsPattern);  
+      }
+      testClasses.add(testCls);
+      
+    } else { // we have to recursively look into the testPathElements for potential test classes
+      List<String> classFileList = getClassFileList( testPathElements, testClsPattern);
+      
+      for (String candidate : classFileList){        
+        Class<?> testCls = loadTestClass( cl, testJpfCls, candidate);
+        if (testCls != null){
+          testClasses.add(testCls);
+        }
+      }
+    }
+    
+    return testClasses;
+  }
+  
+  static void collectMatchingFiles (int nPrefix, File dir, List<String> list, String pattern){
+    for (File e : dir.listFiles()){
+      if (e.isDirectory()){
+        collectMatchingFiles(nPrefix, e, list, pattern);
+        
+      } else if (e.isFile()){
+        String pn = e.getPath().substring(nPrefix);
+        if (pn.matches(pattern)){
+          String clsName = pn.substring(0, pn.length() - 6); // strip cp entry and ".class"
+          clsName = clsName.replace( File.separatorChar, '.');
+          list.add(clsName);
+        }
+      }
+    }
+  }
+  
+  static List<String> getClassFileList (String[] testPathElements, String testClsPattern){
+    List<String> list = new ArrayList<String>();
+    String tcp = testClsPattern.replace('.', File.separatorChar);
+    tcp = tcp.replace("*", ".*") + "\\.class";
+    
+    for (String tpe : testPathElements){
+      File tp = new File(tpe);
+      int nPrefix = tp.getPath().length()+1;
+      collectMatchingFiles( nPrefix, tp, list, tcp);
+    }
+    
+    return list;
+  }
+  
+  static boolean isPublicStatic (Method m){
+    int mod = m.getModifiers();
+    return ((mod & (Modifier.PUBLIC | Modifier.STATIC)) == (Modifier.PUBLIC | Modifier.STATIC));
+  }
+  
+  static String[] getTestPathElements (Config conf){
+    String projectId = JPFSiteUtils.getCurrentProjectId();
+    
+    if (projectId != null) {
+      String testCpKey = projectId + ".test_classpath";
+      return  config.getCompactTrimmedStringArray(testCpKey);
+      
+    } else {
+      return new String[0];
+    }    
+  }
+  
+  static void addTestClassPath (JPFClassLoader cl, String[] testPathElements){
+    if (testPathElements != null) {
+      for (String pe : testPathElements) {
+        try {
+          cl.addURL(FileUtils.getURL(pe));
+        } catch (Throwable x) {
+          error("malformed test_classpath URL: " + pe);
+        }
+      }
+    }
+  }
+
+  static boolean isOptionArg(String a){
+    if (a != null && !a.isEmpty()){
+      char c = a.charAt(0);
+      if ((c == '+') || (c == '-')){
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  static String getTestClassName(String[] args){
+    for (int i=0; i<args.length; i++){
+      String a = args[i];
+      if (a != null && !isOptionArg(a)){
+        return a;
+      }
+    }
+
+    return null;
+  }
+
+  // return everything after the first free arg
+  static String[] getTestArgs(String[] args){
+    int i;
+
+    for (i=0; i<args.length; i++){
+      String a = args[i];
+      if (a != null && !isOptionArg(a)){
+        break;
+      }
+    }
+
+    if (i >= args.length-1){
+      return new String[0];
+    } else {
+      String[] testArgs = new String[args.length-i-1];
+      System.arraycopy(args,i+1, testArgs, 0, testArgs.length);
+      return testArgs;
+    }
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/util/ArrayByteQueue.java b/src/main/gov/nasa/jpf/util/ArrayByteQueue.java
new file mode 100644 (file)
index 0000000..45d09f8
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/** 
+ * It is a dynamically growing, cyclic array buffer queue for bytes. It has 
+ * the similar implementation as ArrayObjectQueue.
+ * 
+ * Suitable buffer for storing data transmitted and received as a continuous 
+ * stream of bytes, e.g. buffers for socket based communications
+ */
+public class ArrayByteQueue  implements ObjectQueue<Byte>, Cloneable {
+
+  static final int DEFAULT_CAPACITY = 256;
+  
+  int size = 0;
+  int first;  // next index we will remove
+  int last;   // last index we did add
+  
+  byte[] buffer = null;
+  
+  class FIFOIterator implements Iterator<Byte> {
+    int next = first;
+    int remaining = size;
+    
+    @Override
+       public boolean hasNext() {
+      return (remaining > 0);
+    }
+
+    @Override
+       public Byte next() {
+      if (remaining == 0){
+        throw new NoSuchElementException();
+      } else {
+        Byte e = buffer[next];
+        next = (next+1) % buffer.length;
+        remaining--;
+        return e;
+      }
+    }
+
+    @Override
+       public void remove() { // its a queue
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  // just for debugging purposes
+  class StorageIterator implements Iterator<Byte> {
+    int next = 0;
+    
+    @Override
+       public boolean hasNext(){
+      return (next < buffer.length); 
+    }
+    
+    @Override
+       public Byte next(){
+      if (next == buffer.length){
+        throw new NoSuchElementException();
+      }
+      
+      Byte e = buffer[next];
+      next++;
+      return e;      
+    }
+    
+    @Override
+       public void remove() { // its a queue
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  public ArrayByteQueue (){
+    buffer = new byte[DEFAULT_CAPACITY];
+  }
+  
+  public ArrayByteQueue (int initialCapacity){
+    buffer = new byte[initialCapacity];
+  }
+  
+  protected void grow(){
+    byte[] newBuffer = new byte[buffer.length * 3 / 2];
+    
+    if (first < last){
+      System.arraycopy(buffer, first, newBuffer, 0, last - first +1);
+    } else if (first > last){
+      int nRight = buffer.length - first;
+      System.arraycopy(buffer, first, newBuffer, 0, nRight);
+      System.arraycopy(buffer, 0, newBuffer, nRight, last+1);      
+    } else { // just 1 element
+      newBuffer[0] = buffer[first];
+    }
+    
+    first = 0;
+    last = size-1;
+    buffer = newBuffer;
+  }
+  
+  @Override
+  public boolean isEmpty() {
+    return (size == 0);
+  }
+  
+  public int getCurrentCapacity(){
+    return buffer.length;
+  }
+  
+  @Override
+  public int size() {
+    return size;
+  }
+  
+  @Override
+  public boolean offer (Byte e){
+    return add(e);
+  }
+  
+  @Override
+  public boolean add (Byte e){
+    if (size == 0){  // first element
+      first = last = 0;
+      buffer[0] = e;
+      size = 1;
+      
+    } else {
+      int i = (last + 1) % buffer.length;
+      if (i == first) {
+        grow();
+        i = size;
+      }
+      
+      last = i;
+      buffer[i] = e;
+      size++;
+    }
+    
+    return true; // this is a dynamic queue, we never run out of space
+  }
+         
+  @Override
+  public Byte poll (){
+    if (size == 0){
+      return null;      
+    } else {
+      int i = first;
+      
+      first = (first+1) % buffer.length;
+      size--;
+      
+      Byte e = buffer[i];
+      //buffer[i] = null; // avoid memory leaks
+      return e;
+    }    
+  }
+  
+  @Override
+  public Byte remove () throws NoSuchElementException {
+    if (size == 0){
+      throw new NoSuchElementException();
+    } else {
+      return poll();
+    }
+  }
+
+  @Override
+  public Byte peek () {
+    if (size == 0){
+      return null;
+    } else {
+      return buffer[first];
+    }
+  }
+  
+  @Override
+  public Iterator<Byte> iterator() {
+    return new FIFOIterator();
+  }
+  public Iterator<Byte> storageIterator(){
+    return new StorageIterator();
+  }
+  
+  
+  @Override
+  public void clear(){
+    buffer = new byte[buffer.length]; // cheaper than iterating over the old one
+    size = 0;
+    first = last = -1;
+  }
+  
+  /**
+   * call Processor.process(e) on each queued object
+   * 
+   * This method does not return before the queue is empty, which makes it
+   * suitable for graph traversal. It also avoids iterator objects, allows
+   * adding new objects while processing the queue, and enables to keep
+   * processing state in the processor
+   */
+  @Override
+  public void process (Processor<Byte> processor){
+    while (size > 0){
+       Byte e = remove();
+      processor.process(e);
+    }
+  }
+  
+  @Override
+  public Object clone() {
+    try {
+      ArrayByteQueue clone = (ArrayByteQueue)super.clone();
+      clone.buffer = this.buffer.clone();
+      return clone;
+    } catch (CloneNotSupportedException cnx) {
+      return null;
+    }
+  }
+  
+  @Override
+  public String toString() {
+    Iterator<Byte> itr = iterator();
+    String result = "[";
+    while(itr.hasNext()) {
+      if(result.length()>1) {
+        result = result + ", ";
+      }
+      byte b = itr.next();
+      result = result + b;
+    }
+    result = result + "]";
+    return result;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ArrayIntSet.java b/src/main/gov/nasa/jpf/util/ArrayIntSet.java
new file mode 100644 (file)
index 0000000..1ff3693
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+import gov.nasa.jpf.JPFException;
+
+/**
+ * common base for array based IntSet implementations
+ */
+public abstract class ArrayIntSet implements IntSet, Cloneable {
+    
+  protected int size;
+  protected int[] elements;
+  
+  private class Iterator implements IntIterator {
+    int next = 0;
+
+    @Override
+    public void remove() {
+      int idx = next-1;
+      if (idx >=0){
+        if (idx < size-1){
+          System.arraycopy(elements, next, elements, idx, size-idx);
+        }
+        size--;
+        next = idx;
+      }
+    }
+
+    @Override
+    public boolean hasNext() {
+      return (next < size);
+    }
+
+    @Override
+    public int next() {
+      if (next < size){
+        return elements[next++];
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+  }
+  
+  protected ArrayIntSet (){
+    // nothing
+  }
+  
+  protected ArrayIntSet (int initialCapacity){
+    elements = new int[initialCapacity];
+  }
+  
+  @Override
+  public  boolean isEmpty(){
+    return (size == 0);
+  }
+   
+  @Override
+  public int size(){
+    return size;
+  }
+  
+  @Override
+  public void clear(){
+    size = 0;
+    elements = null;
+  }
+  
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder(/*getClass().getName()*/);
+    sb.append('{');
+    for (int i=0; i<size; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append(elements[i]);
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+  
+  @Override
+  public ArrayIntSet clone(){
+    try {
+      ArrayIntSet other = (ArrayIntSet) super.clone();
+      other.size = size;
+      if (elements != null) {
+        other.elements = elements.clone();
+      }
+      return other;
+      
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("clone failed " + this);
+    }
+  }
+  
+  /**
+   * this is probably a bad hash function, but we just need something that
+   * is order independent
+   */
+  @Override
+  public int hashCode(){
+    int[] a = elements;
+    int n = size;
+    int h = (n << 16) + (n % 3);
+
+    for (int i = 0; i < n; i++) {
+      int e = a[i];
+      if (e == 0){
+        e = Integer.MAX_VALUE;
+      }
+      int rot = e % 31;
+      h ^= (h << rot) | (h >>> (32 - rot)); // rotate left
+    }
+    
+    return h;
+  }
+  
+  @Override
+  public boolean equals (Object o){
+    if (o instanceof IntSet){
+      IntSet other = (IntSet)o;
+      if (size == other.size()){
+        int len = size;
+        int[] a = elements;
+        for (int i=0; i<len; i++){
+          if (!other.contains(a[i])){
+            return false;
+          }
+        }
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public IntIterator intIterator (){
+    return new Iterator();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ArrayObjectQueue.java b/src/main/gov/nasa/jpf/util/ArrayObjectQueue.java
new file mode 100644 (file)
index 0000000..0efca7a
--- /dev/null
@@ -0,0 +1,225 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * dynamically growing, cyclic array buffer queue for object references
+ */
+public class ArrayObjectQueue<E> implements ObjectQueue<E> {
+
+  static final int DEFAULT_CAPACITY = 256;
+  
+  int size = 0;
+  int first;  // next index we will remove
+  int last;   // last index we did add
+  
+  Object[] buffer = null;
+  
+  class FIFOIterator implements Iterator<E> {
+    int next = first;
+    int remaining = size;
+    
+    @Override
+       public boolean hasNext() {
+      return (remaining > 0);
+    }
+
+    @Override
+       public E next() {
+      if (remaining == 0){
+        throw new NoSuchElementException();
+      } else {
+        E e = (E) buffer[next];
+        next = (next+1) % buffer.length;
+        remaining--;
+        return e;
+      }
+    }
+
+    @Override
+       public void remove() { // its a queue
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  // just for debugging purposes
+  class StorageIterator implements Iterator<E> {
+    int next = 0;
+    
+    @Override
+       public boolean hasNext(){
+      return (next < buffer.length); 
+    }
+    
+    @Override
+       public E next(){
+      if (next == buffer.length){
+        throw new NoSuchElementException();
+      }
+      
+      E e = (E) buffer[next];
+      next++;
+      return e;      
+    }
+    
+    @Override
+       public void remove() { // its a queue
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  public ArrayObjectQueue (){
+    buffer = new Object[DEFAULT_CAPACITY];
+  }
+  
+  public ArrayObjectQueue (int initialCapacity){
+    buffer = new Object[initialCapacity];
+  }
+  
+  protected void grow(){
+    Object[] newBuffer = new Object[buffer.length * 3 / 2];
+    
+    if (first < last){
+      System.arraycopy(buffer, first, newBuffer, 0, last - first +1);
+    } else if (first > last){
+      int nRight = buffer.length - first;
+      System.arraycopy(buffer, first, newBuffer, 0, nRight);
+      System.arraycopy(buffer, 0, newBuffer, nRight, last+1);      
+    } else { // just 1 element
+      newBuffer[0] = buffer[first];
+    }
+    
+    first = 0;
+    last = size-1;
+    buffer = newBuffer;
+  }
+  
+  @Override
+  public boolean isEmpty() {
+    return (size == 0);
+  }
+  
+  public int getCurrentCapacity(){
+    return buffer.length;
+  }
+  
+  @Override
+  public int size() {
+    return size;
+  }
+  
+  @Override
+  public boolean offer (E e){
+    return add(e);
+  }
+  
+  @Override
+  public boolean add (E e){
+    if (size == 0){  // first element
+      first = last = 0;
+      buffer[0] = e;
+      size = 1;
+      
+    } else {
+      int i = (last + 1) % buffer.length;
+      if (i == first) {
+        grow();
+        i = size;
+      }
+      
+      last = i;
+      buffer[i] = e;
+      size++;
+    }
+    
+    return true; // this is a dynamic queue, we never run out of space
+  }
+  
+  @Override
+  public E poll (){
+    if (size == 0){
+      return null;
+      
+    } else {
+      int i = first;
+      
+      first = (first+1) % buffer.length;
+      size--;
+      
+      E e = (E) buffer[i];
+      buffer[i] = null; // avoid memory leaks
+      return e;
+    }
+    
+  }
+  
+  @Override
+  public E remove () throws NoSuchElementException {
+    if (size == 0){
+      throw new NoSuchElementException();
+    } else {
+      return poll();
+    }
+  }
+
+  @Override
+  public E peek () {
+    if (size == 0){
+      return null;
+    } else {
+      return (E)buffer[first];
+    }
+  }
+  
+  @Override
+  public Iterator<E> iterator() {
+    return new FIFOIterator();
+  }
+  public Iterator<E> storageIterator(){
+    return new StorageIterator();
+  }
+  
+  
+  @Override
+  public void clear(){
+    buffer = new Object[buffer.length]; // cheaper than iterating over the old one
+    size = 0;
+    first = last = -1;
+  }
+  
+  /**
+   * call Processor.process(e) on each queued object
+   * 
+   * This method does not return before the queue is empty, which makes it
+   * suitable for graph traversal. It also avoids iterator objects, allows
+   * adding new objects while processing the queue, and enables to keep
+   * processing state in the processor
+   */
+  @Override
+  public void process (Processor<E> processor){
+    while (size > 0){
+      E e = remove();
+      processor.process(e);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Attributable.java b/src/main/gov/nasa/jpf/util/Attributable.java
new file mode 100644 (file)
index 0000000..1640556
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * interface for types that support attributes
+ */
+public interface Attributable {
+
+  boolean hasAttr ();
+  boolean hasAttr (Class<?> attrType);
+  Object getAttr();
+  void setAttr (Object a);
+  void addAttr (Object a);
+  void removeAttr (Object a);
+  void replaceAttr (Object oldAttr, Object newAttr);
+  <T> T getAttr (Class<T> attrType);
+  <T> T getNextAttr (Class<T> attrType, Object prev);
+  ObjectList.Iterator attrIterator();
+  <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType);
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/AvailableBufferedInputStream.java b/src/main/gov/nasa/jpf/util/AvailableBufferedInputStream.java
new file mode 100644 (file)
index 0000000..ef45297
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+
+public class AvailableBufferedInputStream extends InputStream
+{
+   private static final boolean CLEAN               = false;
+           static final int     DEFAULT_BUFFER_SIZE = 16 * 1024;        // Not private for testing purposes
+   
+   private final InputStream m_input;
+   private final byte        m_buffer[];
+   private       int         m_read;
+   private       int         m_end;
+   
+   public AvailableBufferedInputStream(InputStream input)
+   {
+      this(input, DEFAULT_BUFFER_SIZE);
+   }
+   
+   public AvailableBufferedInputStream(InputStream input, int bufferSize)
+   {
+      m_input  = input;
+      m_buffer = new byte[bufferSize];
+      
+      if (input == null)
+         throw new NullPointerException("input == null");
+   }
+   
+   public int getBufferSize()
+   {
+      return(m_buffer.length);
+   }
+   
+   @Override
+  public int read() throws IOException
+   {
+      if (m_read >= m_end)
+      {
+         fill();
+      
+         if (m_read >= m_end)
+            return(m_input.read());
+      }
+      
+      return(m_buffer[m_read++] & 0x00FF);
+   }
+
+   @Override
+  public int read(byte buffer[], int offset, int length) throws IOException
+   {
+      if (m_read >= m_end)
+      {
+         fill();
+         
+         if (m_read >= m_end)
+            return(m_input.read(buffer, offset, length));
+      }
+      
+      length  = Math.min(length, m_end - m_read);
+      System.arraycopy(m_buffer, m_read, buffer, offset, length);
+      m_read += length;
+         
+      return(length);
+   }
+
+   public int peek() throws IOException   // Returns -1 if there is nothing to read.
+   {
+      if (m_read >= m_end)
+      {
+         fill();
+         
+         if (m_read >= m_end)
+            return(-1);
+      }
+      
+      return(m_buffer[m_read] & 0x00FF);
+   }
+
+   @Override
+  public int available() throws IOException
+   {
+      if (m_read >= m_end)
+         fill();
+      
+      return(m_end - m_read);
+   }
+   
+   public void unread(int data) throws IOException
+   {
+      if (m_read <= 0)
+      {
+         if (m_end >= m_buffer.length)
+            throw new IOException("Internal buffer overflow");
+         
+         System.arraycopy(m_buffer, m_read, m_buffer, m_buffer.length - m_end, m_end);
+         m_read = m_buffer.length - m_end;
+         m_end  = m_buffer.length;
+      }
+      
+      m_buffer[--m_read] = (byte) data;
+   }
+   
+   private void fill() throws IOException
+   {
+      if (CLEAN)
+         Arrays.fill(m_buffer, 0, m_buffer.length, (byte) 0);
+
+      m_read = 0;
+      m_end  = m_input.available();
+      m_end  = Math.max(m_end, 0);
+
+      if (m_end <= 0)                              // m_input.read(m_buffer, 0, 0) can be expensive.  Don't waste time.
+         return;
+
+      m_end  = Math.min(m_end, m_buffer.length);
+      m_end  = m_input.read(m_buffer, 0, m_end);
+      m_end  = Math.max(m_end, 0);                 // Defend against a bug where m_input.available() returning > 0 and m_input.read() returning -1
+   }
+
+   @Override
+  public String toString()  // For debugging purposes
+   {
+      return(new String(m_buffer, m_read, m_end - m_read));
+   }
+}
diff --git a/src/main/gov/nasa/jpf/util/BailOut.java b/src/main/gov/nasa/jpf/util/BailOut.java
new file mode 100644 (file)
index 0000000..15a13f4
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * this is just an evil control flow exception for non-local gotos
+ * Useful in a context where you have to terminate execution from different (recursive) levels
+ */
+public class BailOut extends RuntimeException {
+
+  public BailOut(){
+    // nothing here
+  }
+  
+  public BailOut (String details){
+    super(details);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/BinaryClassSource.java b/src/main/gov/nasa/jpf/util/BinaryClassSource.java
new file mode 100644 (file)
index 0000000..2195cd9
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassParseException;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * common root for classes that read classes from binary data
+ */
+public class BinaryClassSource {
+
+  protected byte[] data;
+  
+  protected int pos; // temp index value during parsing
+  protected int pc; // bytecode pos relative to method code start
+  
+  protected int[] posStack;
+  protected int top;
+  
+  protected ByteReader byteReader;
+  
+  //--------------------------------------------------------- variable endian support 
+  
+  public interface ByteReader {
+    int peekI2();
+    int peekU2();
+    int peekI4();
+    long peekU4();
+    
+    int readI2();
+    int readU2();
+    int readI4();
+    long readU4();
+    
+    void makeLittleEndian (short[] data);
+  }
+
+  public class LittleEndianReader implements ByteReader {
+    
+    @Override
+       public final int peekI2 () {
+      int idx = pos;
+      return (data[idx++] & 0xff) | (data[idx] << 8);
+    }
+
+    @Override
+       public final int peekU2 () {
+      int idx = pos;
+      return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
+    }
+    
+    @Override
+       public final int peekI4 () {
+      int idx = pos;
+      byte[] data = BinaryClassSource.this.data;
+      return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
+    }
+
+    @Override
+       public final long peekU4 () {
+      int idx = pos;
+      byte[] data = BinaryClassSource.this.data;
+      return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
+    }
+    
+    
+    @Override
+       public final int readI2 () {
+      int idx = pos;
+      pos += 2;
+      return (data[idx++] & 0xff) | (data[idx] << 8);
+    }
+
+    @Override
+       public final int readU2 () {
+      int idx = pos;
+      pos += 2;
+      return (data[idx++] & 0xff) | ((data[idx] & 0xff)<< 8);
+    }
+    
+    @Override
+       public final int readI4 () {
+      int idx = pos;
+      pos += 4;
+      byte[] data = BinaryClassSource.this.data;
+
+      return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | (data[idx] << 24);
+    }
+
+    @Override
+       public final long readU4 () {
+      int idx = pos;
+      pos += 4;
+      byte[] data = BinaryClassSource.this.data;
+
+      return (data[idx++] & 0xff) | ((data[idx++] & 0xff) << 8) | ((data[idx++] & 0xff) << 16) | ((data[idx] & 0xff) << 24);
+    }
+    
+    @Override
+       public final void makeLittleEndian (short[] data){
+      // nothing - we already are
+    }
+  }
+
+  
+  public class BigEndianReader implements ByteReader {
+    
+    @Override
+       public final int peekI2 () {
+      int idx = pos;
+      return (data[idx++] << 8) | (data[idx] & 0xff);
+    }
+
+    @Override
+       public final int peekU2 () {
+      int idx = pos;
+      return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+    
+    @Override
+       public final int peekI4 () {
+      int idx = pos;
+      byte[] data = BinaryClassSource.this.data;
+
+      return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+
+    @Override
+       public final long peekU4 () {
+      int idx = pos;
+      byte[] data = BinaryClassSource.this.data;
+
+      return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+
+    
+    @Override
+       public final int readI2 () {
+      int idx = pos;
+      pos += 2;
+      return (data[idx++] << 8) | (data[idx] & 0xff);
+    }
+
+    @Override
+       public final int readU2 () {
+      int idx = pos;
+      pos += 2;
+      return ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+    
+    @Override
+       public final int readI4 () {
+      int idx = pos;
+      pos += 4;
+      byte[] data = BinaryClassSource.this.data;
+
+      return (data[idx++] << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+
+    @Override
+       public final long readU4 () {
+      int idx = pos;
+      pos += 4;
+      byte[] data = BinaryClassSource.this.data;
+
+      return ((data[idx++] & 0xff) << 24) | ((data[idx++] & 0xff) << 16) | ((data[idx++] & 0xff) << 8) | (data[idx] & 0xff);
+    }
+    
+    @Override
+       public final void makeLittleEndian (short[] data){
+      for (int i=0; i<data.length; i++){
+        short s = data[i];
+        s = (short) (((s & 0xFF00) >> 8) | (s << 8));
+        data[i] = s;
+      }
+    }
+  }
+  
+  //----------------------------------- BinaryClassSource methods
+
+  
+  protected BinaryClassSource (byte[] data, int pos){
+   this.data = data;
+   this.pos = pos;
+   
+   this.byteReader = initializeByteReader();
+  }
+  
+  protected BinaryClassSource (File file) throws ClassParseException {
+    FileInputStream is = null;
+    try {
+      is = new FileInputStream(file);
+      long len = file.length();
+      if (len > Integer.MAX_VALUE || len <= 0){   // classfile of size > 2GB not supported
+        error("cannot read file of size: " + len);
+      }
+      data = new byte[(int)len];
+      readData(is);
+      
+    } catch (FileNotFoundException fnfx) {
+      error("classfile not found: " + file.getPath());
+
+    } finally {
+      if (is != null) {
+        try {
+          is.close();
+        } catch (IOException iox) {
+          error("failed to close file: " + file.getPath());
+        }
+      }
+    }
+    
+    this.byteReader = initializeByteReader();
+  }
+  
+  protected ByteReader initializeByteReader(){
+    // Java classfiles are big endian
+    return new BigEndianReader();
+  }
+  
+  protected void readData (InputStream is) throws ClassParseException {
+    try {
+      int nRead = 0;
+
+      while (nRead < data.length){
+        int n = is.read(data, nRead, (data.length - nRead));
+        if (n < 0){
+          error("premature end of dex file: " + data.length + '/' + nRead);
+        }
+        nRead += n;
+      }
+
+    } catch (IOException iox){
+      error("failed to read dex file");
+    }
+  }
+  
+  public void stopParsing(){
+    throw new BailOut();
+  }
+
+  protected void error(String msg) throws ClassParseException {
+    throw new ClassParseException(msg);
+  }
+  
+  /**
+   * obtain current classfile data. This is mainly provided to allow
+   * on-the-fly classfile instrumentation with 3rd party libraries
+   * 
+   * BEWARE - this is not a copy, i.e. any modification of the returned data
+   * might cause the parsing to fail.
+   */
+  public byte[] getData(){
+    return data;
+  }
+  
+  public int getPos(){
+    return pos;
+  }
+  
+  public boolean hasMoreData(){
+    return pos < data.length;
+  }
+  
+  // for selective parsing
+  public void setPos (int newPos){
+    pos = newPos;
+  }
+  
+  public void pushPos(){
+    if (posStack == null){
+      posStack = new int[4];
+      posStack[0] = pos;
+      top = 0;
+    } else {
+      top++;
+      if (top == posStack.length){
+        int[] newStack = new int[posStack.length * 2];
+        System.arraycopy(posStack, 0, newStack, 0, posStack.length);
+        posStack = newStack;
+      }
+      posStack[top] = pos;
+    }
+  }
+  
+  public void popPos(){
+    if (top >= 0){
+      pos = posStack[top];
+      top--;
+    }
+  }
+  
+  //--- the low level type specific read methods
+  
+  public static String readModifiedUTF8String( byte[] data, int pos, int len) throws ClassParseException {
+    
+    int n = 0; // the number of chars in buf
+    char[] buf = new char[len]; // it can't be more, but it can be less chars
+    
+    // \u0001 - \u007f             : single byte chars:  0xxxxxxx
+    // \u0000 and \u0080 - \u07ff  : double byte chars:  110xxxxx, 10xxxxxx
+    // \u0800 - \uffff             : tripple byte chars: 1110xxxx, 10xxxxxx, 10xxxxxx
+    
+    int max = pos+len;
+    for (int i=pos; i<max; i++){
+      int c = data[i] & 0xff;
+      if ((c & 0x80) == 0){ // single byte char  0xxxxxxx
+        buf[n++] = (char)c;
+        
+      } else {
+        if ((c & 0x40) != 0){      // 11xxxxxx
+          
+          // for the sake of efficiency, we don't check for the trailing zero bit in the marker,
+          // we just mask it out
+          if ((c & 0x20) == 0) {   // 110xxxxx - double byte char
+            buf[n++] = (char) (((c & 0x1f) << 6) | (data[++i] & 0x3f));
+            
+          } else {                 // 1110xxxx - tripple byte char
+            buf[n++] = (char) (((c & 0x0f) << 12) | ((data[++i] & 0x3f) << 6) | (data[++i] & 0x3f));
+          }
+          
+        } else {
+          throw new ClassParseException("malformed modified UTF-8 input: ");
+        }
+      }
+    }
+    
+    return new String(buf, 0, n);
+  }
+  
+  public final int readByte(){
+    return data[pos++];
+  }
+
+  public final int readUByte(){
+    return (data[pos++] & 0xff);
+  }
+  
+  public final byte[] read (int n){
+    byte[] b = new byte[n];
+    System.arraycopy(data,pos,b,0,n);
+    pos += n;
+    return b;
+  }
+  
+  public String readByteString(int nChars){
+    char[] buf = new char[nChars];
+    for (int i=0; i<nChars; i++){
+      buf[i] = (char)data[pos++];
+    }
+    return new String(buf);
+  }
+  
+  //--- debugging
+  protected void dumpData (int startPos, int nBytes){
+    System.out.printf("%d +%d: [", startPos, nBytes);
+    for (int i=0; i<nBytes; i++){
+      System.out.printf("%02X ", data[startPos+i]);
+    }
+    System.out.println(']');
+  }
+    
+  protected String dataToString (int startPos, int nBytes){
+    StringBuilder sb = new StringBuilder();
+    int i1 = startPos + nBytes;
+    for (int i=startPos; i<i1; i++){
+      sb.append( Integer.toHexString(data[i]));
+      sb.append(' ');
+    }
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/BitArray.java b/src/main/gov/nasa/jpf/util/BitArray.java
new file mode 100644 (file)
index 0000000..097357e
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+import java.util.BitSet;
+
+/**
+ * Faster version of BitSet for fixed size.
+ */
+public class BitArray {
+  public final int length;
+  final byte[] data;
+  
+  public BitArray(int len) {
+    length = len;
+    data = new byte[(len + 7) >> 3];
+  }
+
+  public void fromBitSet(BitSet in) {
+    int max = Math.min(data.length, (in.length() + 7) >> 3);
+    int i;
+    for (i = 0; i < max; i++) {
+      int j = i << 3;
+      data[i] = (byte)
+        ((in.get(j + 0) ? 1 : 0) |
+         (in.get(j + 1) ? 2 : 0) |
+         (in.get(j + 2) ? 4 : 0) |
+         (in.get(j + 3) ? 8 : 0) |
+         (in.get(j + 4) ? 16 : 0) |
+         (in.get(j + 5) ? 32 : 0) |
+         (in.get(j + 6) ? 64 : 0) |
+         (in.get(j + 7) ? 128 : 0));
+    }
+    Arrays.fill(data, max, data.length, (byte) 0);
+  }
+
+  public final void set(int idx, boolean val) {
+    if (idx >= length) throw new ArrayIndexOutOfBoundsException("" + idx + " >= " + length);
+    if (val) {
+      data[idx >> 3] |= (1 << (idx & 7));
+    } else {
+      data[idx >> 3] &= ~(1 << (idx & 7));
+    }
+  }
+
+  public final void set(int idx, int val) {
+    set(idx, val != 0);
+  }
+  
+  public final void set(int idx) {
+    if (idx >= length) throw new ArrayIndexOutOfBoundsException("" + idx + " >= " + length);
+    data[idx >> 3] |= (1 << (idx & 7));
+  }
+
+  public final void clear(int idx) {
+    if (idx >= length) throw new ArrayIndexOutOfBoundsException("" + idx + " >= " + length);
+    data[idx >> 3] &= ~(1 << (idx & 7));
+  }
+
+  public final void setAll() {
+    Arrays.fill(data, (byte) 0xff);
+    cleanup();
+  }
+
+  public final void clearAll() {
+    Arrays.fill(data, (byte) 0);
+  }
+  
+  public final void invert() {
+    int i;
+    for (i = 0; i < data.length; i++) {
+      data[i] = (byte) ~ data[i];
+    }
+    cleanup();
+  }
+  
+  // to keep all unused bits at 0
+  final void cleanup() {
+    if ((length & 7) != 0) {
+      int idx = data.length - 1;
+      data[idx] &= ~(0xff << (length & 7));
+    }
+  }
+  
+  public final boolean get(int idx) {
+    int a = idx >> 3;
+    return a < data.length && a >= 0 && (data[a] & (1 << (idx & 7))) != 0;
+  }
+  
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(data);
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (! (o instanceof BitArray)) return false;
+    byte[] thatData = ((BitArray)o).data;
+    byte[] thisData = this.data;
+    return Arrays.equals(thisData, thatData);
+  }
+  
+  public static final BitArray empty = new BitArray(0);
+}
diff --git a/src/main/gov/nasa/jpf/util/BitSet1024.java b/src/main/gov/nasa/jpf/util/BitSet1024.java
new file mode 100644 (file)
index 0000000..f0f2ccb
--- /dev/null
@@ -0,0 +1,896 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+/**
+ * a fixed size BitSet with 1024 bits.
+ *
+ * The main motivation for this class is to minimize memory size while maximizing
+ * performance and keeping a java.util.BitSet compatible interface. The only
+ * deviation from the standard BitSet is that we assume more cardinality() calls
+ * than set()/clear() calls, i.e. we want to cache this value
+ *
+ * Instances of this class do not allocate any additional memory, we keep all
+ * data in builtin type fields
+ */
+public class BitSet1024 extends AbstractFixedBitSet {
+
+  public static final int INDEX_MASK = 0xfffffc00;
+
+  long l0, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14, l15;
+
+  public BitSet1024 (){
+    // nothing in here
+  }
+
+  public BitSet1024 (int i){
+    set(i);
+  }
+
+  public BitSet1024 (int... idx){
+    for (int i : idx){
+      set(i);
+    }
+  }
+
+  private final int computeCardinality (){
+    int n= Long.bitCount(l0);
+    n += Long.bitCount(l1);
+    n += Long.bitCount(l2);
+    n += Long.bitCount(l3);
+    n += Long.bitCount(l4);
+    n += Long.bitCount(l5);
+    n += Long.bitCount(l6);
+    n += Long.bitCount(l7);
+    n += Long.bitCount(l8);
+    n += Long.bitCount(l9);
+    n += Long.bitCount(l10);
+    n += Long.bitCount(l11);
+    n += Long.bitCount(l12);
+    n += Long.bitCount(l13);
+    n += Long.bitCount(l14);
+    n += Long.bitCount(l15);   
+    return n;
+  }
+
+  //--- public interface (much like java.util.BitSet)
+
+  @Override
+  public void set (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          if ((l0 & bitPattern) == 0L) {
+            cardinality++;
+            l0 |= bitPattern;
+          }
+          break;
+        case 1:
+          if ((l1 & bitPattern) == 0L) {
+            cardinality++;
+            l1 |= bitPattern;
+          }
+          break;
+        case 2:
+          if ((l2 & bitPattern) == 0L) {
+            cardinality++;
+            l2 |= bitPattern;
+          }
+          break;
+        case 3:
+          if ((l3 & bitPattern) == 0L) {
+            cardinality++;
+            l3 |= bitPattern;
+          }
+          break;
+        case 4:
+            if ((l4 & bitPattern) == 0L) {
+              cardinality++;
+              l4 |= bitPattern;
+            }
+            break;
+        case 5:
+            if ((l5 & bitPattern) == 0L) {
+              cardinality++;
+              l5 |= bitPattern;
+            }
+            break;
+        case 6:
+            if ((l6 & bitPattern) == 0L) {
+              cardinality++;
+              l6 |= bitPattern;
+            }
+            break;
+        case 7:
+            if ((l7 & bitPattern) == 0L) {
+              cardinality++;
+              l7 |= bitPattern;
+            }
+            break;
+        case 8:
+            if ((l8 & bitPattern) == 0L) {
+              cardinality++;
+              l8 |= bitPattern;
+            }
+            break;
+        case 9:
+            if ((l9 & bitPattern) == 0L) {
+              cardinality++;
+              l9 |= bitPattern;
+            }
+            break;
+        case 10:
+            if ((l10 & bitPattern) == 0L) {
+              cardinality++;
+              l10 |= bitPattern;
+            }
+            break;
+        case 11:
+            if ((l11 & bitPattern) == 0L) {
+              cardinality++;
+              l11 |= bitPattern;
+            }
+            break;
+        case 12:
+            if ((l12 & bitPattern) == 0L) {
+              cardinality++;
+              l12 |= bitPattern;
+            }
+            break;
+        case 13:
+            if ((l13 & bitPattern) == 0L) {
+              cardinality++;
+              l13 |= bitPattern;
+            }
+            break;
+        case 14:
+            if ((l14 & bitPattern) == 0L) {
+              cardinality++;
+              l14 |= bitPattern;
+            }
+            break;
+        case 15:
+            if ((l15 & bitPattern) == 0L) {
+              cardinality++;
+              l15 |= bitPattern;
+            }
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet1024 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public void clear (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          if ((l0 & bitPattern) != 0L) {
+            cardinality--;
+            l0 &= ~bitPattern;
+          }
+          break;
+        case 1:
+          if ((l1 & bitPattern) != 0L) {
+            cardinality--;
+            l1 &= ~bitPattern;
+          }
+          break;
+        case 2:
+          if ((l2 & bitPattern) != 0L) {
+            cardinality--;
+            l2 &= ~bitPattern;
+          }
+          break;
+        case 3:
+          if ((l3 & bitPattern) != 0L) {
+            cardinality--;
+            l3 &= ~bitPattern;
+          }
+        case 4:
+            if ((l4 & bitPattern) != 0L) {
+              cardinality--;
+              l4 &= ~bitPattern;
+            }
+            break;
+        case 5:
+            if ((l5 & bitPattern) != 0L) {
+              cardinality--;
+              l5 &= ~bitPattern;
+            }
+            break;
+        case 6:
+            if ((l6 & bitPattern) != 0L) {
+              cardinality--;
+              l6 &= ~bitPattern;
+            }
+            break;
+        case 7:
+            if ((l7 & bitPattern) != 0L) {
+              cardinality--;
+              l7 &= ~bitPattern;
+            }
+            break;
+        case 8:
+            if ((l8 & bitPattern) != 0L) {
+              cardinality--;
+              l8 &= ~bitPattern;
+            }
+            break;
+        case 9:
+            if ((l9 & bitPattern) != 0L) {
+              cardinality--;
+              l9 &= ~bitPattern;
+            }
+            break;
+        case 10:
+            if ((l10 & bitPattern) != 0L) {
+              cardinality--;
+              l10 &= ~bitPattern;
+            }
+            break;
+        case 11:
+            if ((l11 & bitPattern) != 0L) {
+              cardinality--;
+              l11 &= ~bitPattern;
+            }
+            break;
+        case 12:
+            if ((l12 & bitPattern) != 0L) {
+              cardinality--;
+              l12 &= ~bitPattern;
+            }
+            break;
+        case 13:
+            if ((l13 & bitPattern) != 0L) {
+              cardinality--;
+              l13 &= ~bitPattern;
+            }
+            break;
+        case 14:
+            if ((l14 & bitPattern) != 0L) {
+              cardinality--;
+              l14 &= ~bitPattern;
+            }
+            break;
+        case 15:
+            if ((l15 & bitPattern) != 0L) {
+              cardinality--;
+              l15 &= ~bitPattern;
+            }
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet1024 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public boolean get (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          return ((l0 & bitPattern) != 0);
+        case 1:
+          return ((l1 & bitPattern) != 0);
+        case 2:
+          return ((l2 & bitPattern) != 0);
+        case 3:
+          return ((l3 & bitPattern) != 0);
+        case 4:
+            return ((l4 & bitPattern) != 0);
+        case 5:
+            return ((l5 & bitPattern) != 0);
+        case 6:
+            return ((l6 & bitPattern) != 0);
+        case 7:
+            return ((l7 & bitPattern) != 0);
+        case 8:
+            return ((l8 & bitPattern) != 0);
+        case 9:
+            return ((l9 & bitPattern) != 0);
+        case 10:
+            return ((l10 & bitPattern) != 0);
+        case 11:
+            return ((l11 & bitPattern) != 0);
+        case 12:
+            return ((l12 & bitPattern) != 0);
+        case 13:
+            return ((l13 & bitPattern) != 0);
+        case 14:
+            return ((l14 & bitPattern) != 0);
+        case 15:
+            return ((l15 & bitPattern) != 0);
+      }
+    }
+
+    throw new IndexOutOfBoundsException("BitSet1024 index out of range: " + i);
+  }
+
+  @Override
+  public int size() {
+    return 1024;
+  }
+
+  /**
+   * number of bits we can store
+   */
+  @Override
+  public int capacity() {
+    return 1024;
+  }
+
+  /**
+   * index of highest set bit + 1
+   */
+  @Override
+  public int length() {
+   if (l15 != 0){
+         return 1024 - Long.numberOfLeadingZeros(l15);
+   } else if (l14 != 0) {
+         return 960 - Long.numberOfLeadingZeros(l14);
+   } else if (l13 != 0) {
+         return 896 - Long.numberOfLeadingZeros(l13);
+   } else if (l12 != 0) {
+         return 832 - Long.numberOfLeadingZeros(l12);
+   } else if (l11 != 0) {
+         return 768 - Long.numberOfLeadingZeros(l11);
+   } else if (l10 != 0) {
+         return 704 - Long.numberOfLeadingZeros(l10);
+   } else if (l9 != 0) {
+         return 640 - Long.numberOfLeadingZeros(l9);
+   } else if (l8 != 0) {
+         return 576 - Long.numberOfLeadingZeros(l8);
+   } else if (l7 != 0) {
+         return 512 - Long.numberOfLeadingZeros(l7);
+   } else if (l6 != 0) {
+         return 448 - Long.numberOfLeadingZeros(l6);
+   } else if (l5 != 0) {
+         return 384 - Long.numberOfLeadingZeros(l5);
+   } else if (l4 != 0) {
+         return 320 - Long.numberOfLeadingZeros(l4);
+   } else if (l3 != 0){
+      return 256 - Long.numberOfLeadingZeros(l3);
+   } else if (l2 != 0){
+      return 192 - Long.numberOfLeadingZeros(l2);
+   } else if (l1 != 0){
+      return 128 - Long.numberOfLeadingZeros(l1);
+   } else if (l1 != 0){
+      return 64 - Long.numberOfLeadingZeros(l0);
+   } else {
+      return 0;
+   }
+  }
+
+  @Override
+  public void clear() {
+    l0 = l1 = l2 = l3 = l4 = l5 = l6 = l7
+    = l8 = l9= l10 = l11 = l12 = l13 = l14
+    = l15 =0L;
+    cardinality = 0;
+  }
+
+
+  @Override
+  public int nextSetBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0) {
+      int i;
+      int i0 = fromIdx & 0x3f;
+      switch (fromIdx >> 6){
+        case 0:
+          if ((i=Long.numberOfTrailingZeros(l0 & (0xffffffffffffffffL << i0))) <64) return i;
+          if ((i=Long.numberOfTrailingZeros(l1)) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 1:
+          if ((i=Long.numberOfTrailingZeros(l1 & (0xffffffffffffffffL << i0))) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 2:
+          if ((i=Long.numberOfTrailingZeros(l2 & (0xffffffffffffffffL << i0))) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 3:
+          if ((i=Long.numberOfTrailingZeros(l3 & (0xffffffffffffffffL << i0))) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 4:
+                 if ((i=Long.numberOfTrailingZeros(l4 & (0xffffffffffffffffL << i0))) <64) return i + 256;
+              if ((i=Long.numberOfTrailingZeros(l5)) <64) return i + 320;
+              if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+              if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+              if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+              if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+              if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+              if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 5:
+              if ((i=Long.numberOfTrailingZeros(l5 & (0xffffffffffffffffL << i0))) <64) return i + 320;
+              if ((i=Long.numberOfTrailingZeros(l6)) <64) return i + 384;
+              if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+              if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+              if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+              if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+              if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+          break;
+        case 6:
+         if ((i=Long.numberOfTrailingZeros(l6 & (0xffffffffffffffffL << i0))) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 7:
+                 if ((i=Long.numberOfTrailingZeros(l7 & (0xffffffffffffffffL << i0))) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 8:
+                 if ((i=Long.numberOfTrailingZeros(l8 & (0xffffffffffffffffL << i0))) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 9:
+         if ((i=Long.numberOfTrailingZeros(l9 & (0xffffffffffffffffL << i0))) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 10:
+         if ((i=Long.numberOfTrailingZeros(l10 & (0xffffffffffffffffL << i0))) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 11:
+                 if ((i=Long.numberOfTrailingZeros(l11 & (0xffffffffffffffffL << i0))) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 12:
+                 if ((i=Long.numberOfTrailingZeros(l12 & (0xffffffffffffffffL << i0))) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 13:
+                if ((i=Long.numberOfTrailingZeros(l13 & (0xffffffffffffffffL << i0))) <64) return i + 832;
+             if ((i=Long.numberOfTrailingZeros(l14)) <64) return i + 896;
+             if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 14:
+                if ((i=Long.numberOfTrailingZeros(l14 & (0xffffffffffffffffL << i0))) <64) return i + 896;
+             if ((i=Long.numberOfTrailingZeros(l15)) <64) return i + 960;
+            break;
+        case 15:
+                if ((i=Long.numberOfTrailingZeros(l15 & (0xffffffffffffffffL << i0))) <64) return i + 960;
+            break;
+      }
+      return -1;
+
+    }
+    return -1;
+  }
+
+  @Override
+  public int nextClearBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0) {
+      int i;
+      int i0 = fromIdx & 0x3f;
+      switch (fromIdx >> 6){
+        case 0:
+          if ((i=Long.numberOfTrailingZeros(~l0 & (0xffffffffffffffffL << i0))) <64) return i;
+          if ((i=Long.numberOfTrailingZeros(~l1)) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(~l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(~l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(~l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 1:
+          if ((i=Long.numberOfTrailingZeros(~l1 & (0xffffffffffffffffL << i0))) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(~l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(~l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(~l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 2:
+          if ((i=Long.numberOfTrailingZeros(~l2 & (0xffffffffffffffffL << i0))) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(~l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(~l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 3:
+          if ((i=Long.numberOfTrailingZeros(~l3 & (0xffffffffffffffffL << i0))) <64) return i + 192;
+          if ((i=Long.numberOfTrailingZeros(~l4)) <64) return i + 256;
+          if ((i=Long.numberOfTrailingZeros(~l5)) <64) return i + 320;
+          if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 4:
+                 if ((i=Long.numberOfTrailingZeros(~l4 & (0xffffffffffffffffL << i0))) <64) return i + 256;
+              if ((i=Long.numberOfTrailingZeros(~l5)) <64) return i + 320;
+              if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+              if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+              if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+              if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+              if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+              if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 5:
+              if ((i=Long.numberOfTrailingZeros(~l5 & (0xffffffffffffffffL << i0))) <64) return i + 320;
+              if ((i=Long.numberOfTrailingZeros(~l6)) <64) return i + 384;
+              if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+              if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+              if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+              if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+              if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+          break;
+        case 6:
+         if ((i=Long.numberOfTrailingZeros(~l6 & (0xffffffffffffffffL << i0))) <64) return i + 384;
+          if ((i=Long.numberOfTrailingZeros(~l7)) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 7:
+                 if ((i=Long.numberOfTrailingZeros(~l7 & (0xffffffffffffffffL << i0))) <64) return i + 448;
+          if ((i=Long.numberOfTrailingZeros(~l8)) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 8:
+                 if ((i=Long.numberOfTrailingZeros(~l8 & (0xffffffffffffffffL << i0))) <64) return i + 512;
+          if ((i=Long.numberOfTrailingZeros(~l9)) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 9:
+         if ((i=Long.numberOfTrailingZeros(~l9 & (0xffffffffffffffffL << i0))) <64) return i + 576;
+          if ((i=Long.numberOfTrailingZeros(~l10)) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 10:
+         if ((i=Long.numberOfTrailingZeros(~l10 & (0xffffffffffffffffL << i0))) <64) return i + 640;
+          if ((i=Long.numberOfTrailingZeros(~l11)) <64) return i + 704;
+          if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+          if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+          if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+          if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 11:
+                 if ((i=Long.numberOfTrailingZeros(~l11 & (0xffffffffffffffffL << i0))) <64) return i + 704;
+              if ((i=Long.numberOfTrailingZeros(~l12)) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 12:
+                 if ((i=Long.numberOfTrailingZeros(~l12 & (0xffffffffffffffffL << i0))) <64) return i + 768;
+              if ((i=Long.numberOfTrailingZeros(~l13)) <64) return i + 832;
+              if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+              if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 13:
+                if ((i=Long.numberOfTrailingZeros(~l13 & (0xffffffffffffffffL << i0))) <64) return i + 832;
+             if ((i=Long.numberOfTrailingZeros(~l14)) <64) return i + 896;
+             if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 14:
+                if ((i=Long.numberOfTrailingZeros(~l14 & (0xffffffffffffffffL << i0))) <64) return i + 896;
+             if ((i=Long.numberOfTrailingZeros(~l15)) <64) return i + 960;
+            break;
+        case 15:
+                if ((i=Long.numberOfTrailingZeros(~l15 & (0xffffffffffffffffL << i0))) <64) return i + 960;
+            break;
+      }
+
+      return -1;
+
+    } else {
+      //throw new IndexOutOfBoundsException("BitSet256 index out of range: " + fromIdx);
+      return -1;
+    }
+  }
+
+  public void and (BitSet1024 other){
+    l0 &= other.l0;
+    l1 &= other.l1;
+    l2 &= other.l2;
+    l3 &= other.l3;
+    l4 &= other.l4;
+    l5 &= other.l5;
+    l6 &= other.l6;
+    l7 &= other.l7;
+    l8 &= other.l8;
+    l9 &= other.l9;
+    l10 &= other.l10;
+    l11 &= other.l11;
+    l12 &= other.l12;
+    l13 &= other.l13;
+    l14 &= other.l14;
+    l15 &= other.l15;
+
+    cardinality = computeCardinality();
+  }
+
+  public void andNot (BitSet1024 other){
+    l0 &= ~other.l0;
+    l1 &= ~other.l1;
+    l2 &= ~other.l2;
+    l3 &= ~other.l3;
+    l4 &= ~other.l4;
+    l5 &= ~other.l5;
+    l6 &= ~other.l6;
+    l7 &= ~other.l7;
+    l8 &= ~other.l8;
+    l9 &= ~other.l9;
+    l10 &= ~other.l10;
+    l11 &= ~other.l11;
+    l12 &= ~other.l12;
+    l13 &= ~other.l13;
+    l14 &= ~other.l14;
+    l15 &= ~other.l15;
+
+    cardinality = computeCardinality();
+  }
+
+  public void or (BitSet1024 other){
+    l0 |= other.l0;
+    l1 |= other.l1;
+    l2 |= other.l2;
+    l3 |= other.l3;
+    l4 |= other.l4;
+    l5 |= other.l5;
+    l6 |= other.l6;
+    l7 |= other.l7;
+    l8 |= other.l8;
+    l9 |= other.l9;
+    l10 |= other.l10;
+    l11 |= other.l11;
+    l12 |= other.l12;
+    l13 |= other.l13;
+    l14 |= other.l14;
+    l15 |= other.l15;
+
+    cardinality = computeCardinality();
+  }
+
+  @Override
+  public boolean equals (Object o){
+    if (o instanceof BitSet1024){
+      BitSet1024 other = (BitSet1024)o;
+      if (l0 != other.l0) return false;
+      if (l1 != other.l1) return false;
+      if (l2 != other.l2) return false;
+      if (l3 != other.l3) return false;
+      if (l4 != other.l4) return false;
+      if (l5 != other.l5) return false;
+      if (l6 != other.l6) return false;
+      if (l7 != other.l7) return false;
+      if (l8 != other.l8) return false;
+      if (l9 != other.l9) return false;
+      if (l10 != other.l10) return false;
+      if (l11 != other.l11) return false;
+      if (l12 != other.l12) return false;
+      if (l13 != other.l13) return false;
+      if (l14 != other.l14) return false;
+      if (l15 != other.l15) return false;
+
+      return true;
+    } else {
+      // <2do> we could compare to a normal java.util.BitSet here
+      return false;
+    }
+  }
+
+  /**
+   * answer the same hashCodes as java.util.BitSet
+   */
+  @Override
+  public int hashCode() {
+    long hc = 1234;
+    hc ^= l0;
+    hc ^= l1*2;
+    hc ^= l2*3;
+    hc ^= l4*4;
+    hc ^= l5*5;
+    hc ^= l6*6;
+    hc ^= l7*7;
+    hc ^= l8*8;
+    hc ^= l9*9;
+    hc ^= l10*10;
+    hc ^= l11*11;
+    hc ^= l12*12;
+    hc ^= l13*13;
+    hc ^= l14*14;
+    hc ^= l15*15;
+    return (int) ((hc >>32) ^ hc);
+  }
+
+
+  @Override
+  public void hash (HashData hd){
+    hd.add(l0);
+    hd.add(l1);
+    hd.add(l2);
+    hd.add(l3);
+    hd.add(l4);
+    hd.add(l5);
+    hd.add(l6);
+    hd.add(l7);
+    hd.add(l8);
+    hd.add(l9);
+    hd.add(l10);
+    hd.add(l11);
+    hd.add(l12);
+    hd.add(l13);
+    hd.add(l14);
+    hd.add(l15);
+  }  
+}
diff --git a/src/main/gov/nasa/jpf/util/BitSet256.java b/src/main/gov/nasa/jpf/util/BitSet256.java
new file mode 100644 (file)
index 0000000..80efebc
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+/**
+ * a fixed size BitSet with 256 bits.
+ *
+ * The main motivation for this class is to minimize memory size while maximizing
+ * performance and keeping a java.util.BitSet compatible interface. The only
+ * deviation from the standard BitSet is that we assume more cardinality() calls
+ * than set()/clear() calls, i.e. we want to cache this value
+ *
+ * Instances of this class do not allocate any additional memory, we keep all
+ * data in builtin type fields
+ */
+public class BitSet256 extends AbstractFixedBitSet {
+
+  public static final int INDEX_MASK = 0xffffff00;
+
+  long l0, l1, l2, l3;
+
+  public BitSet256 (){
+    // nothing in here
+  }
+
+  public BitSet256 (int i){
+    set(i);
+  }
+
+  public BitSet256 (int... idx){
+    for (int i : idx){
+      set(i);
+    }
+  }
+
+  private final int computeCardinality (){
+    int n= Long.bitCount(l0);
+    n += Long.bitCount(l1);
+    n += Long.bitCount(l2);
+    n += Long.bitCount(l3);
+    return n;
+  }
+
+  //--- public interface (much like java.util.BitSet)
+
+  @Override
+  public void set (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          if ((l0 & bitPattern) == 0L) {
+            cardinality++;
+            l0 |= bitPattern;
+          }
+          break;
+        case 1:
+          if ((l1 & bitPattern) == 0L) {
+            cardinality++;
+            l1 |= bitPattern;
+          }
+          break;
+        case 2:
+          if ((l2 & bitPattern) == 0L) {
+            cardinality++;
+            l2 |= bitPattern;
+          }
+          break;
+        case 3:
+          if ((l3 & bitPattern) == 0L) {
+            cardinality++;
+            l3 |= bitPattern;
+          }
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet256 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public void clear (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          if ((l0 & bitPattern) != 0L) {
+            cardinality--;
+            l0 &= ~bitPattern;
+          }
+          break;
+        case 1:
+          if ((l1 & bitPattern) != 0L) {
+            cardinality--;
+            l1 &= ~bitPattern;
+          }
+          break;
+        case 2:
+          if ((l2 & bitPattern) != 0L) {
+            cardinality--;
+            l2 &= ~bitPattern;
+          }
+          break;
+        case 3:
+          if ((l3 & bitPattern) != 0L) {
+            cardinality--;
+            l3 &= ~bitPattern;
+          }
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet256 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public boolean get (int i){
+    if ((i & INDEX_MASK) == 0) {
+      long bitPattern = (1L << i);
+
+      switch (i >> 6) {
+        case 0:
+          return ((l0 & bitPattern) != 0);
+        case 1:
+          return ((l1 & bitPattern) != 0);
+        case 2:
+          return ((l2 & bitPattern) != 0);
+        case 3:
+          return ((l3 & bitPattern) != 0);
+      }
+    }
+
+    throw new IndexOutOfBoundsException("BitSet256 index out of range: " + i);
+  }
+
+  @Override
+  public int size() {
+    return 256;
+  }
+
+  
+  /**
+   * number of bits we can store
+   */
+  @Override
+  public int capacity() {
+    return 256;
+  }
+
+  /**
+   * index of highest set bit + 1
+   */
+  @Override
+  public int length() {
+    if (l3 != 0){
+      return 256 - Long.numberOfLeadingZeros(l3);
+    } else if (l2 != 0){
+      return 192 - Long.numberOfLeadingZeros(l2);
+    } else if (l1 != 0){
+      return 128 - Long.numberOfLeadingZeros(l1);
+    } else if (l1 != 0){
+      return 64 - Long.numberOfLeadingZeros(l0);
+    } else {
+      return 0;
+    }
+  }
+
+  @Override
+  public void clear() {
+    l0 = l1 = l2 = l3 = 0L;
+    cardinality = 0;
+  }
+
+
+  @Override
+  public int nextSetBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0) {
+      int i;
+      int i0 = fromIdx & 0x3f;
+      switch (fromIdx >> 6){
+        case 0:
+          if ((i=Long.numberOfTrailingZeros(l0 & (0xffffffffffffffffL << i0))) <64) return i;
+          if ((i=Long.numberOfTrailingZeros(l1)) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          break;
+        case 1:
+          if ((i=Long.numberOfTrailingZeros(l1 & (0xffffffffffffffffL << i0))) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          break;
+        case 2:
+          if ((i=Long.numberOfTrailingZeros(l2 & (0xffffffffffffffffL << i0))) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(l3)) <64) return i + 192;
+          break;
+        case 3:
+          if ((i=Long.numberOfTrailingZeros(l3 & (0xffffffffffffffffL << i0))) <64) return i + 192;
+      }
+
+      return -1;
+
+    } else {
+      //throw new IndexOutOfBoundsException("BitSet256 index out of range: " + fromIdx);
+      return -1;
+    }
+  }
+
+  @Override
+  public int nextClearBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0) {
+      int i;
+      int i0 = fromIdx & 0x3f;
+      switch (fromIdx >> 6){
+        case 0:
+          if ((i=Long.numberOfTrailingZeros(~l0 & (0xffffffffffffffffL << i0))) <64) return i;
+          if ((i=Long.numberOfTrailingZeros(~l1)) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(~l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          break;
+        case 1:
+          if ((i=Long.numberOfTrailingZeros(~l1 & (0xffffffffffffffffL << i0))) <64) return i + 64;
+          if ((i=Long.numberOfTrailingZeros(~l2)) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          break;
+        case 2:
+          if ((i=Long.numberOfTrailingZeros(~l2 & (0xffffffffffffffffL << i0))) <64) return i + 128;
+          if ((i=Long.numberOfTrailingZeros(~l3)) <64) return i + 192;
+          break;
+        case 3:
+          if ((i=Long.numberOfTrailingZeros(~l3 & (0xffffffffffffffffL << i0))) <64) return i + 192;
+      }
+
+      return -1;
+
+    } else {
+      //throw new IndexOutOfBoundsException("BitSet256 index out of range: " + fromIdx);
+      return -1;
+    }
+  }
+
+  public void and (BitSet256 other){
+    l0 &= other.l0;
+    l1 &= other.l1;
+    l2 &= other.l2;
+    l3 &= other.l3;
+
+    cardinality = computeCardinality();
+  }
+
+  public void andNot (BitSet256 other){
+    l0 &= ~other.l0;
+    l1 &= ~other.l1;
+    l2 &= ~other.l2;
+    l3 &= ~other.l3;
+
+    cardinality = computeCardinality();
+  }
+
+  public void or (BitSet256 other){
+    l0 |= other.l0;
+    l1 |= other.l1;
+    l2 |= other.l2;
+    l3 |= other.l3;
+
+    cardinality = computeCardinality();
+  }
+
+  @Override
+  public boolean equals (Object o){
+    if (o instanceof BitSet256){
+      BitSet256 other = (BitSet256)o;
+      if (l0 != other.l0) return false;
+      if (l1 != other.l1) return false;
+      if (l2 != other.l2) return false;
+      if (l3 != other.l3) return false;
+      return true;
+    } else {
+      // <2do> we could compare to a normal java.util.BitSet here
+      return false;
+    }
+  }
+
+  /**
+   * answer the same hashCodes as java.util.BitSet
+   */
+  @Override
+  public int hashCode() {
+    long hc = 1234;
+    hc ^= l0;
+    hc ^= l1*2;
+    hc ^= l2*3;
+    hc ^= l3*4;
+    return (int) ((hc >>32) ^ hc);
+  }
+
+  @Override
+  public void hash (HashData hd){
+    hd.add(l0);
+    hd.add(l1);
+    hd.add(l2);
+    hd.add(l3);
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append('{');
+
+    boolean first = true;
+    for (int i=nextSetBit(0); i>= 0; i = nextSetBit(i+1)){
+      if (!first){
+        sb.append(',');
+      } else {
+        first = false;
+      }
+      sb.append(i);
+    }
+
+    sb.append('}');
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/BitSet64.java b/src/main/gov/nasa/jpf/util/BitSet64.java
new file mode 100644 (file)
index 0000000..59d495e
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ *
+ */
+public class BitSet64 extends AbstractFixedBitSet implements Cloneable {
+
+  static final int INDEX_MASK = 0xffffffc0; // ( i>=0 && i<64)
+
+  long l0;
+
+  public BitSet64 (){
+    // nothing in here
+  }
+
+  public BitSet64 (int i){
+    set(i);
+  }
+
+  public BitSet64 (int... idx){
+    for (int i : idx){
+      set(i);
+    }
+  }
+
+  @Override
+  public void hash (HashData hd){
+    hd.add(l0);
+  }
+
+
+  private final int computeCardinality (){
+    return Long.bitCount(l0);
+  }
+
+  //--- public interface (much like java.util.BitSet)
+
+  @Override
+  public void set (int i){
+    if ((i & INDEX_MASK) == 0){
+      long bitPattern = (1L << i);
+      if ((l0 & bitPattern) == 0L) {
+        cardinality++;
+        l0 |= bitPattern;
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet64 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public void clear (int i){
+    if ((i & INDEX_MASK) == 0){
+      long bitPattern = (1L << i);
+      if ((l0 & bitPattern) != 0L) { // bit is set
+        cardinality--;
+        l0 &= ~bitPattern;
+      }
+    } else {
+      throw new IndexOutOfBoundsException("BitSet64 index out of range: " + i);
+    }
+  }
+
+
+  @Override
+  public boolean get (int i){
+    if ((i & INDEX_MASK) == 0){
+      long bitPattern = (1L << i);
+      return ((l0 & bitPattern) != 0);
+    } else {
+      throw new IndexOutOfBoundsException("BitSet64 index out of range: " + i);
+    }
+  }
+
+  @Override
+  public int capacity(){
+    return 64;
+  }
+  
+  /**
+   * number of bits we can store
+   */
+  @Override
+  public int size() {
+    return 64;
+  }
+
+  /**
+   * index of highest set bit + 1
+   */
+  @Override
+  public int length() {
+    return 64 - Long.numberOfLeadingZeros(l0);
+  }
+
+
+  @Override
+  public void clear() {
+    l0 = 0L;
+    cardinality = 0;
+  }
+
+  @Override
+  public int nextSetBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0){
+      //int n = Long.numberOfTrailingZeros(l0 & (0xffffffffffffffffL << fromIdx));
+      int n = Long.numberOfTrailingZeros(l0 >> fromIdx) + fromIdx;
+      if (n < 64) {
+        return n;
+      } else {
+        return -1;
+      }
+    } else {
+      //throw new IndexOutOfBoundsException("BitSet64 index out of range: " + fromIdx);
+      return -1;
+    }
+  }
+
+  @Override
+  public int nextClearBit (int fromIdx){
+    if ((fromIdx & INDEX_MASK) == 0){
+      //int n = Long.numberOfTrailingZeros(~l0 & (0xffffffffffffffffL << fromIdx));
+      int n = Long.numberOfTrailingZeros(~l0 >> fromIdx) + fromIdx;
+      if (n < 64) {
+        return n;
+      } else {
+        return -1;
+      }
+    } else {
+      //throw new IndexOutOfBoundsException("BitSet64 index out of range: " + fromIdx);
+      return -1;
+    }
+  }
+
+  public void and (BitSet64 other){
+    l0 &= other.l0;
+
+    cardinality = computeCardinality();
+  }
+
+  public void andNot (BitSet64 other){
+    l0 &= ~other.l0;
+
+    cardinality = computeCardinality();
+  }
+
+  public void or (BitSet64 other){
+    l0 |= other.l0;
+
+    cardinality = computeCardinality();
+  }
+
+  @Override
+  public boolean equals (Object o){
+    if (o instanceof BitSet64){
+      BitSet64 other = (BitSet64)o;
+      if (l0 != other.l0) return false;
+      return true;
+    } else {
+      // <2do> we could compare to a normal java.util.BitSet here
+      return false;
+    }
+  }
+
+
+  /**
+   * answer the same hashCodes as java.util.BitSet
+   */
+  @Override
+  public int hashCode() {
+    long hc = 1234;
+    hc ^= l0;
+    return (int) ((hc >>32) ^ hc);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/BitSetN.java b/src/main/gov/nasa/jpf/util/BitSetN.java
new file mode 100644 (file)
index 0000000..d5c2ed4
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.BitSet;
+import java.util.NoSuchElementException;
+
+/**
+ * a FixedBitSet implementation that is based on java.util.BitSet
+ */
+public class BitSetN extends BitSet implements FixedBitSet {
+  
+  class SetBitIterator implements IntIterator {
+    int cur = 0;
+    int nBits;
+    int cardinality;  // <2do> this should be lifted since it makes the iterator brittle
+    
+    SetBitIterator (){
+      cardinality = cardinality();
+    }
+    
+    @Override
+    public void remove() {
+      if (cur >0){
+        clear(cur-1);
+      }
+    }
+
+    @Override
+    public boolean hasNext() {
+      return nBits < cardinality;
+    }
+
+    @Override
+    public int next() {
+      if (nBits < cardinality){
+        int idx = nextSetBit(cur);
+        if (idx >= 0){
+          nBits++;
+          cur = idx+1;
+        }
+        return idx;
+        
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+  }
+  
+  
+  public BitSetN (int nBits){
+    super(nBits);
+  }
+  
+  @Override
+  public FixedBitSet clone() {
+    return (FixedBitSet) super.clone();
+  }
+
+  @Override
+  public int capacity() {
+    return size();
+  }
+
+
+  @Override
+  public void hash (HashData hd){
+    long[] data = toLongArray();
+    for (int i=0; i<data.length; i++){
+      hd.add(data[i]);
+    }
+  }
+
+  
+  //--- IntSet interface
+  @Override
+  public boolean add(int i) {
+    if (get(i)) {
+      return false;
+    } else {
+      set(i);
+      return true;
+    }
+  }
+
+  @Override
+  public boolean remove(int i) {
+    if (get(i)) {
+      clear(i);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public boolean contains(int i) {
+    return get(i);
+  }
+  
+  @Override
+  public IntIterator intIterator() {
+    return new SetBitIterator();
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/ClassInfoFilter.java b/src/main/gov/nasa/jpf/util/ClassInfoFilter.java
new file mode 100644 (file)
index 0000000..ac177f5
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+
+/**
+ * utility class that can be used by InstructionFactory implementations to
+ * selectively replace bytecodes for specified class sets.
+ *
+ * Filtering is based on include/exclude name patterns (e.g. for packages) and/or
+ * on inheritance (both down- and upwards)
+ */
+public class ClassInfoFilter {
+
+    // filter using an explicit set of class names (can be used for one-pass load)
+  protected StringSetMatcher includes;  // included classes that should use them
+  protected StringSetMatcher excludes;  // excluded classes (that should NOT use them)
+
+  // filter using base/derived class sets (only useful in subsequent pass)
+  ClassInfo ciLeaf;
+  ClassInfo ciRoot;
+
+  public ClassInfoFilter (String[] includeCls, String[] excludeCls,
+                                   ClassInfo rootCls, ClassInfo leafCls) {
+    includes = StringSetMatcher.getNonEmpty(includeCls);
+    excludes = StringSetMatcher.getNonEmpty(excludeCls);
+
+    ciRoot = rootCls;
+    ciLeaf = leafCls;
+  }
+
+
+  public boolean isPassing (ClassInfo ci){
+    if (ci == null){
+
+      // <??> not clear what to do in this case, since we have nothing to
+      // filter on. Since all reflection calls come in here, it's probably
+      // better to instrument by default (until we have a better mechanism)
+      return true;
+
+    } else {
+      String clsName = ci.getName();
+
+      if (StringSetMatcher.isMatch(clsName, includes, excludes)){
+        if (ciLeaf == null || ciLeaf.isInstanceOf(ci)){
+          if (ciRoot == null || ci.isInstanceOf(ciRoot)){
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/CloneableObject.java b/src/main/gov/nasa/jpf/util/CloneableObject.java
new file mode 100644 (file)
index 0000000..2ee0e37
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * the standard java.lang.Cloneable is just a type tag without methods, so
+ * we can't use it to enforce public access of clone()
+ */
+public interface CloneableObject {
+  Object clone();
+}
diff --git a/src/main/gov/nasa/jpf/util/Cloner.java b/src/main/gov/nasa/jpf/util/Cloner.java
new file mode 100644 (file)
index 0000000..1041c57
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * this is a helper to enable deep copy of generic containers, where the
+ * element types are generic type parameters and therefore can't be
+ * created via ctor or clone. In this case, we have to pass in a cloner
+ * object that knows about the concrete element types
+ */
+public interface Cloner<E> {
+  E clone (E other);
+}
diff --git a/src/main/gov/nasa/jpf/util/CommitOutputStream.java b/src/main/gov/nasa/jpf/util/CommitOutputStream.java
new file mode 100644 (file)
index 0000000..b045351
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+public class CommitOutputStream extends OutputStream
+{
+   private final OutputStream m_sink;
+   private       byte         m_buffer[];
+   private       int          m_size;
+   
+   public CommitOutputStream(OutputStream sink)
+   {
+      if (sink == null)
+         throw new NullPointerException("sink == null");
+      
+      m_sink   = sink;
+      m_buffer = new byte[1024];
+   }
+   
+   @Override
+  public void write(int data)
+   {
+      if (m_size >= m_buffer.length)
+         m_buffer = Arrays.copyOf(m_buffer, 2 * m_buffer.length);
+      
+      m_buffer[m_size++] = (byte) data;
+   }
+
+   @Override
+  public void write(byte buffer[], int offset, int length)
+   {
+      if (offset < 0)
+         throw new IndexOutOfBoundsException("offset < 0 : " + offset);
+      
+      if (length < 0)
+         throw new IndexOutOfBoundsException("length < 0 : " + length);
+         
+          if (offset + length > buffer.length)
+             throw new IndexOutOfBoundsException("offset + length > buffer.length : " + offset + " + " + length + " > " + buffer.length);
+      
+      if (length == 0)
+         return;
+      
+      if (m_size + length > m_buffer.length)
+         m_buffer = Arrays.copyOf(m_buffer, Math.max(m_size + length, 2 * m_buffer.length));
+      
+      System.arraycopy(buffer, offset, m_buffer, m_size, length);
+      
+      m_size += length;
+   }
+   
+   public int getSize()
+   {
+      return(m_size);
+   }
+   
+   public void commit() throws IOException
+   {
+      if (m_size == 0)
+         return;
+      
+      m_sink.write(m_buffer, 0, m_size);
+      
+      m_size = 0;
+   }
+   
+   public void rollback()
+   {
+      m_size = 0;
+   }
+   
+   @Override
+  public void flush() throws IOException
+   {
+      m_sink.flush();      
+   }
+   
+   @Override
+  public void close() throws IOException
+   {
+      m_sink.close();
+   }
+}
diff --git a/src/main/gov/nasa/jpf/util/ConsoleStream.java b/src/main/gov/nasa/jpf/util/ConsoleStream.java
new file mode 100644 (file)
index 0000000..123452d
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import javax.swing.JTextArea;
+
+/**
+ * a utility that can be used to write logs which are displayed in a JTextArea
+ */
+public class ConsoleStream extends PrintStream {
+  OutputStream os;
+  JTextArea textArea;
+  
+  public ConsoleStream (JTextArea textArea) {
+    super(System.out, true);
+    this.textArea = textArea;
+  }
+  
+  @Override
+  public void write (byte[] buf, int off, int len) {
+    String s = new String(buf, off, len);
+    textArea.append(s);
+  }
+  
+  @Override
+  public void print( String s) {
+    //super.print(s);
+    textArea.append(s);
+  }
+  
+  @Override
+  public void println (String s) {
+    //super.println(s);
+    textArea.append(s);
+    textArea.append("\n");
+  }
+  
+  @Override
+  public void print (Object o) {
+    textArea.append(o.toString());
+  }
+  
+  @Override
+  public void println (Object o) {
+    println(o.toString());
+  }
+  
+  @Override
+  public void println() {
+    textArea.append("\n");
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/util/ConstGrowth.java b/src/main/gov/nasa/jpf/util/ConstGrowth.java
new file mode 100644 (file)
index 0000000..cdfc1c5
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public class ConstGrowth implements Growth {
+  final int v;
+  public ConstGrowth(int v) {
+    if (v < 1 || v > 1000000000) {
+      throw new IllegalArgumentException();
+    }
+    this.v = v;
+  }
+  
+  @Override
+  public int grow(int oldSize, int minNewSize) {
+    int newSize = oldSize + v;
+    if (newSize < minNewSize) {
+      newSize = minNewSize + (v >> 1);
+    }
+    return newSize;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/CountDown.java b/src/main/gov/nasa/jpf/util/CountDown.java
new file mode 100644 (file)
index 0000000..8898691
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+final class CountDown extends RuntimeException {
+  private int remaining;
+  
+  CountDown (int remaining){
+    this.remaining = remaining;
+  }
+  
+  public final int dec(){
+    if (remaining <= 0){
+      throw this;
+    }
+    remaining--;
+    return remaining;
+  }
+  
+  public final void expire (){
+    remaining = 0;
+  }
+  
+  public final void set(int remaining){
+    this.remaining = remaining;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/util/DevNullPrintStream.java b/src/main/gov/nasa/jpf/util/DevNullPrintStream.java
new file mode 100644 (file)
index 0000000..38fa03b
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintStream;
+import java.io.ByteArrayOutputStream;
+import java.util.Locale;
+
+/**
+ * a PrintStream that doesn't print anything
+ */
+public class DevNullPrintStream extends PrintStream {
+  
+  public DevNullPrintStream(){
+    super( new ByteArrayOutputStream());
+  }
+  
+  @Override
+  public void flush(){}
+  @Override
+  public void close(){}
+  @Override
+  public boolean checkError(){
+    return false;
+  }
+  @Override
+  protected void setError(){}
+  @Override
+  protected void clearError(){}
+  
+  @Override
+  public void write(int a){}
+  @Override
+  public void write(byte[] a, int b, int c){}
+  @Override
+  public void print(boolean a){}
+  @Override
+  public void print(char a){}
+  @Override
+  public void print(int a){}
+  @Override
+  public void print(long a){}
+  @Override
+  public void print(float a){}
+  @Override
+  public void print(double a){}
+  @Override
+  public void print(char[] a){}
+  @Override
+  public void print(String a){}
+  @Override
+  public void print(Object a){}
+  @Override
+  public void println(){}
+  @Override
+  public void println(boolean a){}
+  @Override
+  public void println(char a){}
+  @Override
+  public void println(int a){}
+  @Override
+  public void println(long a){}
+  @Override
+  public void println(float a){}
+  @Override
+  public void println(double a){}
+  @Override
+  public void println(char[] a){}
+  @Override
+  public void println(String a){}
+  @Override
+  public void println(Object a){}
+  
+  @Override
+  public PrintStream printf(String a, Object... b){ return this; }
+  @Override
+  public PrintStream printf(Locale a, String b, Object... c){ return this; }
+  @Override
+  public PrintStream format(String a, Object... b){ return this; }
+  @Override
+  public PrintStream format(Locale a, String b, Object... c){ return this; }
+  @Override
+  public PrintStream append(CharSequence a){ return this; }
+  @Override
+  public PrintStream append(CharSequence a, int b, int c){ return this; }
+  @Override
+  public PrintStream append(char a){ return this; }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/DynamicIntArray.java b/src/main/gov/nasa/jpf/util/DynamicIntArray.java
new file mode 100644 (file)
index 0000000..3163921
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+
+/**
+ * simplistic dynamic array that differentiates from ArrayList by
+ *  - using chunks instead of exponential growth, thus efficiently dealing
+ *    with sparse arrays
+ *  - managing primitive 'int' types, i.e. not requiring box objects
+ *
+ * the motivation for this class is memory optimization, i.e. space efficient
+ * storage of potentially huge arrays without good a-priori size guesses
+ *
+ * the API of this class is between a primitive array and a AbstractList. It's
+ * not a Collection implementation because it handles primitive types, but the
+ * API could be extended to support iterators and the like.
+ *
+ * NOTE: like standard Collection implementations/arrays, this class is not
+ * synchronized
+ */
+public final class DynamicIntArray implements Iterable<Integer> {
+  final static int DEFAULT_CHUNKBITS = 8;
+  final static int INIT_CHUNKS = 16;
+
+  class DynIntIterator implements Iterator<Integer> {
+    int i;
+    
+    @Override
+       public boolean hasNext() {
+      return (i<size());
+    }
+
+    @Override
+       public Integer next() {
+      return new Integer(get(i++));
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException();
+    }
+  }
+  
+  /** growth strategy */
+  Growth growth;
+  
+  /** our allocation sizes */
+  int chunkBits;
+  int nPerChunk; // just a cache for (1<<chunkBits)
+  
+  /** mask for index within chunk */
+  int chunkMask;
+  
+  /** the real data. limitations in generics prevent use of E[][] */
+  int[][] data;
+  
+  /** the maximum index set so far */
+  int maxIndex = -1;
+  
+  public DynamicIntArray () {
+    this(Growth.defaultGrowth, DEFAULT_CHUNKBITS, INIT_CHUNKS);
+  }
+
+  public DynamicIntArray (int size) {
+    this(Growth.defaultGrowth, DEFAULT_CHUNKBITS,
+        (size + (1<<DEFAULT_CHUNKBITS)-1) / (1<<DEFAULT_CHUNKBITS));
+  }
+  
+  /**
+   * Creates a DynamicIntArray in which each chunk has 2**chunkBits elements
+   * and initChunks chunks are initially allocated. 
+   */
+  public DynamicIntArray (int chunkBits, int initChunks) {
+    this(Growth.defaultGrowth, chunkBits, initChunks);
+  }
+  
+  public DynamicIntArray (Growth strategy, int chunkBits, int initChunks) {
+    if (chunkBits > 20) throw new IllegalArgumentException();
+    this.chunkBits = chunkBits;
+    nPerChunk = 1<<chunkBits;
+    this.chunkMask = nPerChunk - 1;
+    data = new int[initChunks][];
+    growth = strategy;
+  }
+
+  public int get (int index) {
+    int i = index >> chunkBits;
+    if (i < data.length && data[i] != null) {
+      int j = index & chunkMask;
+      return data[i][j];
+    } else {
+      return 0;
+    }
+  }
+
+  // this is only the max size, not the max index that was accessed/set
+  public int size() {
+    return data.length * nPerChunk;
+  }
+  
+  public int getMaxIndex() {
+    return maxIndex;
+  }
+  
+  public void set (int index, int value) {
+    if (index > maxIndex) {
+      maxIndex = index;
+    }
+    
+    int i = index >> chunkBits;
+    int j = index & chunkMask;
+    
+    if (i >= data.length) {
+      int nChunks = growth.grow(data.length, i+1);
+      int[][] newChunks = new int[nChunks][];
+      System.arraycopy(data, 0, newChunks, 0, data.length);
+      data = newChunks;
+    }
+    if (data[i] == null) {
+      data[i] = new int[nPerChunk];
+    }
+    
+    data[i][j] = value;
+  }
+
+  @Override
+  public String toString() {
+    int length = data.length * nPerChunk;
+    while (length > 1 && get(length-1) == 0) length--;
+
+    StringBuilder sb = new StringBuilder(length);
+    
+    sb.append('{');
+    int l = length-1;
+    for (int i=0; i<l; i++) {
+      sb.append(get(i));
+      sb.append(',');
+    }
+    sb.append(get(l));
+    sb.append('}');
+    
+    return sb.toString();
+  }
+
+  @Override
+  public Iterator<Integer> iterator() {
+    return new DynIntIterator();
+  }
+
+
+  /**************************** debug & test ************
+  public void dump () {
+    int i, j;
+    for (i=0; i<data.length; i++) {
+      System.out.print( "[" + i + "]: ");
+      if (data[i] != null) {
+        System.out.print("{");
+        int l = data[i].length-1;
+        for (j=0; j<l; j++) {
+          System.out.print(data[i][j]);
+          System.out.print(',');
+        }
+        System.out.print( data[i][j]);
+        System.out.println("}");
+      } else {
+        System.out.println( "null");
+      }
+    }
+  }
+
+  public static void main (String[] args) {
+    int i;
+    DynamicIntArray a = new DynamicIntArray(8);
+
+    a.set(0, 42);
+    a.set(13,13);
+    a.set(24, 42);
+
+    a.set(600, -1);
+    System.out.println(a.get(599));
+    System.out.println(a.get(600));
+    
+    System.out.println( "--------- " + a.size());
+    //System.out.println(a);
+    System.out.println(); System.out.println();
+    //a.dump();
+  }
+  
+  ***************************** end debug & test *********/
+}
+
diff --git a/src/main/gov/nasa/jpf/util/DynamicObjectArray.java b/src/main/gov/nasa/jpf/util/DynamicObjectArray.java
new file mode 100644 (file)
index 0000000..5e84617
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+
+/**
+ * simplistic Object array that differentiates from ArrayList by
+ * using chunks instead of exponential growth, thus efficiently dealing
+ * with huge, potentially sparse arrays
+ *
+ * the motivation for this class is memory optimization, i.e. space efficient
+ * storage of potentially huge arrays without good a-priori size guesses
+ *
+ * this class is awfully lifted from DynamicIntArray (same motivation, but
+ * primitive types - not much factorizable functionality w/o excessive
+ * casting/boxing)
+ *
+ * the API of this class is between a primitive array and a AbstractList. Since
+ * it handles Objects, we could turn this into a Collection (and probably should)
+ *
+ * NOTE: like standard Collection implementations/arrays, this class is not
+ * synchronized
+ */
+
+public final class DynamicObjectArray<E> implements Iterable<E> {
+  final static int DEFAULT_CHUNKBITS = 8;
+  final static int INIT_CHUNKS = 16;
+
+  /** growth strategy */
+  Growth growth;
+
+  /** our allocation sizes */
+  int chunkBits;
+  int nPerChunk; // just a cache for (1<<chunkBits)
+
+  /** mask for index within chunk */
+  int chunkMask;
+
+  /** the real data. limitations in generics prevent use of E[][] */
+  Object[][] data;
+
+  /** the maximum index set so far */
+  int maxIndex = -1;
+
+  class DynIterator implements Iterator<E> {
+    int i;
+
+    @Override
+       public boolean hasNext() {
+      return (i<size());
+    }
+
+    @Override
+       public E next() {
+      return get(i++);
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  public DynamicObjectArray () {
+    this(Growth.defaultGrowth, DEFAULT_CHUNKBITS, INIT_CHUNKS);
+  }
+
+  /**
+   * Creates a DynamicObjectArray in which each chunk has 2**chunkBits elements
+   * and initChunks chunks are initially allocated.
+   */
+  public DynamicObjectArray (int chunkBits, int initChunks) {
+    this(Growth.defaultGrowth, chunkBits, initChunks);
+  }
+
+  public DynamicObjectArray (Growth strategy, int chunkBits, int initChunks) {
+    if (chunkBits > 20) throw new IllegalArgumentException();
+    this.chunkBits = chunkBits;
+    nPerChunk = 1<<chunkBits;
+    this.chunkMask = nPerChunk - 1;
+    data = new Object[initChunks][];
+    growth = strategy;
+  }
+
+  @Override
+  public Iterator<E> iterator() {
+    return new DynIterator();
+  }
+
+  @SuppressWarnings("unchecked")
+  public E get (int index) {
+    int i = index >> chunkBits;
+    if (i < data.length && data[i] != null) {
+      int j = index & chunkMask;
+      return (E) data[i][j];
+    } else {
+      return null;
+    }
+  }
+
+  // this is only the max size, not the max index that was accessed/set
+  public int size() {
+    return data.length * nPerChunk;
+  }
+
+  public int getMaxIndex() {
+    return maxIndex;
+  }
+
+  public void set (int index, E value) {
+    if (index > maxIndex) {
+      maxIndex = index;
+    }
+
+    int i = index >> chunkBits;
+    int j = index & chunkMask;
+
+    if (i >= data.length) {
+      int nChunks = growth.grow(data.length, i+1);
+      Object[][] newChunks = new Object[nChunks][];
+      System.arraycopy(data, 0, newChunks, 0, data.length);
+      data = newChunks;
+    }
+    if (data[i] == null) {
+      data[i] = new Object[1 << chunkBits];
+    }
+
+    data[i][j] = value;
+  }
+
+  @Override
+  public String toString() {
+    int length = data.length * (1 << chunkBits);
+    while (length > 1 && get(length-1) == null) length--;
+
+    StringBuilder sb = new StringBuilder(length);
+
+    sb.append('{');
+    int l = length-1;
+    for (int i=0; i<l; i++) {
+      sb.append(get(i));
+      sb.append(',');
+    }
+    sb.append(get(l));
+    sb.append('}');
+
+    return sb.toString();
+  }
+
+  // removed toArray method, which is confusing for 1.5
+}
diff --git a/src/main/gov/nasa/jpf/util/ElementCreator.java b/src/main/gov/nasa/jpf/util/ElementCreator.java
new file mode 100644 (file)
index 0000000..66380de
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * create an instance of type E out of a T instance
+ */
+public interface ElementCreator<T,E> {
+  E create (T o);
+}
diff --git a/src/main/gov/nasa/jpf/util/ExpGrowth.java b/src/main/gov/nasa/jpf/util/ExpGrowth.java
new file mode 100644 (file)
index 0000000..699209b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public class ExpGrowth implements Growth {
+  final float factor;
+  final float sqFactor;
+  final int plus;
+  
+  public ExpGrowth(float factor, int plus) {
+    if (factor < 1.001F || factor > 100.F) {
+      throw new IllegalArgumentException();
+    }
+    this.factor = factor;
+    this.sqFactor = (float) Math.sqrt(factor);
+    this.plus = plus;
+  }
+  
+  public ExpGrowth(float factor) {
+    this(factor,7);
+  }
+  
+  @Override
+  public int grow(int oldSize, int minNewSize) {
+    int newSize = (int)(factor * oldSize) + plus;
+    if (newSize < minNewSize) {
+      newSize = (int)(sqFactor * minNewSize) + plus;
+    }
+    return newSize;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/FeatureSpec.java b/src/main/gov/nasa/jpf/util/FeatureSpec.java
new file mode 100644 (file)
index 0000000..7a0d2bd
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.vm.ClassInfo;
+
+/**
+ * common base class for MethodSpec and FieldSpec
+ */
+public abstract class FeatureSpec {
+
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.util");
+
+  static class ParseData {
+    boolean matchInverted;
+    boolean matchSuperTypes;
+    String typeSpec;
+    String nameSpec;
+  }
+
+  protected static final char SUB = '+';
+  protected static final char INVERTED = '!';
+
+
+  protected String src;
+
+  // those can be wildcard expressions
+  protected StringMatcher  clsSpec;
+  protected StringMatcher  nameSpec;
+
+  protected boolean matchInverted;   // matches everything that does NOT conform to the specs
+  protected boolean matchSuperTypes; // matches supertypes of the specified one
+
+
+  protected static String parseInversion (String s, ParseData d){
+    if (s.length() > 0){
+      if (s.charAt(0) == INVERTED){
+        d.matchInverted = true;
+        s = s.substring(1).trim();
+      }
+    }
+    return s;
+  }
+
+  protected static String parseType (String s, ParseData d){
+    d.typeSpec = s;
+    return s;
+  }
+  
+  protected static String parseTypeAndName (String s, ParseData d){
+    int i = s.lastIndexOf('.'); // beginning of name
+    if (i >= 0){
+      if (i==0){
+        d.typeSpec = "*";
+      } else {
+        d.typeSpec = s.substring(0, i);
+      }
+
+      d.nameSpec = s.substring(i+1);
+      if (d.nameSpec.length() == 0){
+        d.nameSpec = "*";
+      }
+
+    } else { // no name, all fields
+      if (s.length() == 0){
+        d.typeSpec = "*";
+      } else {
+        d.typeSpec = s;
+      }
+      d.nameSpec = "*";
+    }
+
+    return s;
+  }
+
+  protected FeatureSpec (String rawSpec, String cls, String name, boolean inverted){
+    src = rawSpec;
+    matchInverted = inverted;
+
+    int l = cls.length()-1;
+    if (cls.charAt(l) == SUB){
+      cls = cls.substring(0, l);
+      matchSuperTypes = true;
+    }
+
+    clsSpec = new StringMatcher(cls);
+    
+    if (name != null){
+      nameSpec = new StringMatcher(name);
+    }
+  }
+
+  public String getSource() {
+    return src;
+  }
+
+  public StringMatcher getClassSpec() {
+    return clsSpec;
+  }
+
+  public StringMatcher getNameSpec() {
+    return nameSpec;
+  }
+
+  public boolean matchSuperTypes() {
+    return matchSuperTypes;
+  }
+
+  public boolean isMatchingType (Class cls){
+    if (clsSpec.matches(cls.getName())){
+      return true;
+    }
+    
+    if (matchSuperTypes){
+      for (Class c = cls.getSuperclass(); c != null; c = c.getSuperclass()){
+        if (clsSpec.matches(c.getName())){
+          return true;
+        }
+      }
+    }
+    
+    for (Class ifc : cls.getInterfaces()){
+      if (clsSpec.matches(ifc.getName())) {
+        return true;
+      }      
+    }
+    
+    return false;
+  }
+  
+  public boolean isMatchingType(ClassInfo ci){
+    if (clsSpec.matches(ci.getName())){  // also takes care of '*'
+      return true;
+    }
+
+    if (matchSuperTypes){
+      // check all superclasses
+      for (ClassInfo sci = ci.getSuperClass(); sci != null; sci = sci.getSuperClass()){
+        if (clsSpec.matches(sci.getName())){
+          return true;
+        }
+      }
+    }
+
+    // check interfaces (regardless of 'override' - interfaces make no sense otherwise
+    for (ClassInfo ifc : ci.getAllInterfaces()) {
+      if (clsSpec.matches(ifc.getName())) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public abstract boolean matches (Object feature);
+}
diff --git a/src/main/gov/nasa/jpf/util/FieldSpec.java b/src/main/gov/nasa/jpf/util/FieldSpec.java
new file mode 100644 (file)
index 0000000..c18211e
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+
+/**
+ * utility class that can match FieldInfos against specs.
+ * if the class or name part are omitted, "*" is assumed
+ * a preceeding '!' means the match is inverted
+ *
+ * spec examples
+ *   "x.y.Foo.bar  : field 'bar' in class 'x.y.Foo'
+ *   "x.y.Foo+.bar : all 'bar' fields in 'x.y.Foo' and all its supertypes
+ *   "x.y.Foo.*"   : all fields of x.y.Foo
+ *   "*.myData"    : all fields names 'myData'
+ *   "!x.y.*"      : all fields of types outside types in package x.y
+ */
+public class FieldSpec extends FeatureSpec {
+
+  /**
+   * factory method that includes the parser
+   */
+  public static FieldSpec createFieldSpec (String s){
+    ParseData d = new ParseData();
+
+    s = s.trim();
+    String src = s;
+
+    s = parseInversion(s,d);
+    parseTypeAndName(s,d);
+
+    try {
+      return new FieldSpec(src, d.typeSpec, d.nameSpec, d.matchInverted);
+    } catch (IllegalArgumentException iax){
+      return null;
+    }
+  }
+
+
+  public FieldSpec (String rawSpec, String cls, String name, boolean inverted){
+    super(rawSpec,cls,name,inverted);
+  }
+
+  @Override
+  public boolean matches (Object feature){
+    if (feature instanceof FieldInfo){
+      return matches( (FieldInfo) feature);
+    } else {
+      return false;
+    }
+  }
+
+  public boolean matches (FieldInfo fi){
+
+    ClassInfo ci = fi.getClassInfo();
+    if (isMatchingType(ci)) {
+      if (nameSpec.matches(fi.getName()) != matchInverted) {
+        return true;
+      }
+    }
+
+    return matchInverted;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("FieldSpec {");
+    if (clsSpec != null){
+      sb.append("clsSpec:\"");
+      sb.append(clsSpec);
+      sb.append('"');
+    }
+    if (nameSpec != null){
+      sb.append(",nameSpec:\"");
+      sb.append(nameSpec);
+      sb.append('"');
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/FieldSpecMatcher.java b/src/main/gov/nasa/jpf/util/FieldSpecMatcher.java
new file mode 100644 (file)
index 0000000..1e45d15
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.FieldInfo;
+
+/**
+ * a matcher for a collection of FieldSpecs
+ */
+public class FieldSpecMatcher {
+
+  protected FieldSpec[] fieldSpecs;
+  
+  public static FieldSpecMatcher create (String[] specs){
+    if (specs != null && specs.length > 0){
+      return new FieldSpecMatcher(specs);
+    } else {
+      return null;
+    }
+  }
+  
+  public FieldSpecMatcher(String[] specs){
+    int len = specs.length;
+    fieldSpecs = new FieldSpec[len];
+    for (int i=0; i<len; i++){
+      fieldSpecs[i] = FieldSpec.createFieldSpec(specs[i]);
+    }
+  }
+  
+  public boolean matches (FieldInfo fi){
+    for (int i=0; i<fieldSpecs.length; i++){
+      if (fieldSpecs[i].matches(fi)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/FileUtils.java b/src/main/gov/nasa/jpf/util/FileUtils.java
new file mode 100644 (file)
index 0000000..fcfcae5
--- /dev/null
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.*;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * utility class to find all files matching (possibly hierarchical)
+ * wildcard path specs
+ *
+ * we support single '*' wildcards as in filename matching, plus "**" patterns
+ * that match all (recursive) subdirectories
+ */
+// example:  List<File> list = findMatches("/U*/p*/tmp/**/*.java");
+
+public class FileUtils {
+
+  public static boolean containsWildcards (String pattern) {
+    return (pattern.indexOf('*') >= 0);
+  }
+
+  //--- processing wildcard path specs
+
+  public static String[] expandWildcards (String[] pathNames){
+    ArrayList<String> list = null;
+
+    if (pathNames == null){
+      return new String[0];
+    }
+
+    for (int i=0; i<pathNames.length; i++){
+      String e = pathNames[i];
+
+      if (containsWildcards(e)){
+        if (list == null){
+          list= new ArrayList<String>(pathNames.length + 20);
+          for (int j=0; j<i; j++){
+            list.add(pathNames[j]);
+          }
+        }
+
+        for (File f : findMatches(e)){
+          list.add(f.getAbsolutePath());
+        }
+
+      } else {
+        if (list != null){
+          list.add(e);
+        }
+      }
+    }
+
+    if (list != null){
+      return list.toArray(new String[list.size()]);
+    } else {
+      return pathNames;
+    }
+  }
+
+
+  private static List<File> splitPath (String pattern) {
+    ArrayList<File> list = new ArrayList<File>();
+
+    for (File f = new File(pattern); f != null; f = f.getParentFile()) {
+      list.add(f);
+    }
+
+    Collections.reverse(list);
+    return list;
+  }
+
+  private static void addSubdirs (List<File> list, File dir){
+    for (File f : dir.listFiles()) {
+      if (f.isDirectory()){
+        list.add(f);
+        addSubdirs(list, f);
+      }
+    }
+  }
+
+  private static List<File> findMatches (File dir, String pattern) {
+    ArrayList<File> list = new ArrayList<File>();
+
+    if (dir.isDirectory()) {
+      if ("**".equals(pattern)) { // recursively add all subdirectories
+        addSubdirs(list, dir);
+
+      } else {
+        StringMatcher sm = new StringMatcher(pattern);
+        for (File f : dir.listFiles()) {
+          if (sm.matches(f.getName())) {
+            list.add(f);
+          }
+        }
+      }
+    }
+
+    return list;
+  }
+
+  public static List<File> findMatches (String pattern) {
+    List<File> pathComponents = splitPath(pattern);
+    List<File> matches = null;
+
+    for (File f : pathComponents) {
+      String fname = f.getName();
+      if (matches == null) { // first one
+        if (fname.isEmpty()) { // filesystem root
+          matches = new ArrayList<File>();
+          matches.add(f);
+        } else {
+          matches = findMatches(new File(System.getProperty("user.dir")), fname);
+        }
+
+      } else {
+        List<File> newMatches = new ArrayList<File>();
+        for (File d : matches) {
+          newMatches.addAll(findMatches(d, fname));
+        }
+        matches = newMatches;
+      }
+
+      if (matches.isEmpty()) {
+        return matches;
+      }
+    }
+    return matches;
+  }
+
+
+  //--- URL conversion
+
+  public static URL getURL (String spec){
+    try {
+      // check if there is a protocol specification
+      if (spec.indexOf("://") >= 0) {
+        return new URL(spec);
+
+      } else {
+        File f = new File(spec).getCanonicalFile();
+        return f.toURI().toURL();
+      }
+    } catch (Throwable x) {
+      throw new RuntimeException("illegal pathname: " + spec);
+    }
+  }
+
+  public static URL[] getURLs (String[] paths){
+    ArrayList<URL> urls = new ArrayList<URL>();
+
+    for (String p : paths) {
+      urls.add( getURL(p));
+    }
+
+    return urls.toArray(new URL[urls.size()]);
+  }
+
+  public static URL[] getURLs (List<String> paths){
+    ArrayList<URL> urls = new ArrayList<URL>();
+
+    for (String p : paths) {
+      urls.add( getURL(p));
+    }
+
+    return urls.toArray(new URL[urls.size()]);
+  }
+
+
+  //--- platform specific path conversion
+
+  /**
+   * turn a mixed path list into a valid Unix path set without drive letters,
+   * and with '/' and ':' separators. Also remove multiple consecutive separators
+   * this assumes the path String to be already expanded
+   */
+  public static String asCanonicalUnixPath (String p) {
+    boolean changed = false;
+
+    int n = p.length();
+    char[] buf = new char[n];
+    p.getChars(0, n, buf, 0);
+
+    for (int i=0; i<n; i++) {
+      char c = buf[i];
+      if (c == '/' || c == '\\') {
+        if (c == '\\'){
+          buf[i] = '/'; changed = true;
+        }
+
+        // remove multiple occurrences of dir separators
+        int i1 = i+1;
+        if (i1 < n){
+          for (c = buf[i1]; i1 < n && (c == '/' || c == '\\'); c = buf[i1]) {
+            System.arraycopy(buf, i + 2, buf, i1, n - (i + 2));
+            n--;
+            changed = true;
+          }
+        }
+
+      } else if (c == ':') {
+        // strip drive letters - maybe this is trying to be too smart,
+        // since we only do this for a "...:X:\..." but not a
+        // "...:X:/...", which could be a valid unix path list
+
+        // is this part of a drive letter spec?
+        int i1 = i+1;
+        if (i1<n) {
+          if (buf[i1] == '\\') {
+            if (i>0) {
+              if (i == 1 || (buf[i-2] == ':')){  // strip the drive letter
+                System.arraycopy(buf, i1, buf, i-1, n - (i1));
+                n-=2;
+                changed = true;
+              }
+            }
+          }
+        }
+
+      } else if (c == ';'){
+        buf[i] = ':'; changed = true;
+
+      } else if (c == ',') {
+        buf[i] = ':'; changed = true;
+      }
+
+      if (buf[i] == ':') {  // remove multiple occurrences of path separators
+        int i1 = i+1;
+        if (i1<n) {
+          for (c = buf[i1] ;(c == ':' || c == ';' || c == ','); c = buf[i1]){
+            System.arraycopy(buf, i+2, buf, i1, n - (i+2));
+            n--;
+            changed = true;
+          }
+        }
+      }
+    }
+
+    if (changed) {
+      p = new String(buf, 0, n);
+    }
+
+    return p;
+  }
+
+  /**
+   * turn a mixed path list into a valid Windows path set with drive letters,
+   * and '\' and ';' separators. Also remove multiple consecutive separators
+   * this assumes the path String to be already expanded
+   */
+  public static String asCanonicalWindowsPath (String p) {
+    boolean changed = false;
+
+    int n = p.length();
+    char[] buf = new char[n];
+    p.getChars(0, n, buf, 0);
+
+    for (int i=0; i<n; i++) {
+      char c = buf[i];
+      if (c == '/' || c == '\\') {
+        if (c == '/'){
+          buf[i] = '\\'; changed = true;
+        }
+
+        // remove multiple occurrences of dir separators
+        int i1 = i+1;
+        if (i1 < n) {
+          for (c = buf[i1]; i1 < n && (c == '/' || c == '\\'); c = buf[i1]) {
+            System.arraycopy(buf, i + 2, buf, i1, n - (i + 2));
+            n--;
+            changed = true;
+          }
+        }
+
+      } else if (c == ':') {
+        // is this part of a drive letter spec?
+        int i1 = i+1;
+        if (i1<n && (buf[i1] == '\\' || buf[i1] == '/')) {
+          if (i>0) {
+            if (i == 1 || (buf[i-2] == ';')){
+              continue;
+            }
+          }
+        }
+        buf[i] = ';'; changed = true;
+
+      } else if (c == ',') {
+        buf[i] = ';'; changed = true;
+      }
+
+      if (buf[i] == ';') { // remove multiple occurrences of path separators
+        int i1 = i+1;
+        if (i1<n) {
+          for (c = buf[i1] ;(c == ':' || c == ';' || c == ','); c = buf[i1]){
+            System.arraycopy(buf, i+2, buf, i1, n - (i+2));
+            n--;
+            changed = true;
+          }
+        }
+      }
+    }
+
+    if (changed) {
+      p = new String(buf, 0, n);
+    }
+
+    return p;
+  }
+
+
+  public static String asPlatformPath (String p) {
+    if (File.separatorChar == '/') { // Unix'ish file system
+      p = asCanonicalUnixPath(p);
+    } else { // Windows'ish file system
+      p = asCanonicalWindowsPath(p);
+    }
+
+    return p;
+  }
+
+  public static void printFile (PrintWriter pw, File file){
+    try {
+      FileReader fr = new FileReader(file);
+      BufferedReader r = new BufferedReader(fr);
+
+      String line;
+      while ((line = r.readLine()) != null){
+        pw.println(line);
+      }
+
+      r.close();
+
+    } catch (IOException iox){
+      pw.println("!! error printing file: " + file.getPath());
+    }
+  }
+
+  public static boolean removeRecursively(File file) {
+    if (file.exists()) {
+      File[] childs = file.listFiles();
+
+      for (File child : childs) {
+        if (child.isDirectory()){
+          removeRecursively(child);
+        } else {
+          child.delete();
+        }
+      }
+
+      return file.delete();
+    }
+
+    return false;
+  }
+
+  public static byte[] getContents( File file) throws IOException {
+    if (file.isFile()){
+      long length = file.length();
+      byte[] data = new byte[(int)length];
+
+      FileInputStream is = new FileInputStream(file);
+      try {
+        getContents(is, data);
+
+      } catch (IOException iox){
+        return null;
+
+      } finally {
+        is.close();
+      }
+
+      return data;
+    }
+
+    return null;
+  }
+
+  public static void getContents(InputStream is, byte[] buf) throws IOException {
+    int nRead = 0;
+    while (nRead < buf.length) {
+      int n = is.read(buf, nRead, buf.length - nRead);
+      if (n < 0) {
+        throw new IOException("premature end of inputstream: " + buf.length + '/' + nRead);
+      }
+      nRead += n;
+    }
+  }
+
+  public static String getContentsAsString( File file) throws IOException {
+    byte[] data = getContents(file);
+    return new String(data);
+  }
+  
+  public static void setContents(File file, byte[] data) throws IOException {
+    FileOutputStream os = new FileOutputStream(file);
+    os.write(data);
+    os.close();
+  }
+
+  public static void setContents(File file, String data) throws IOException {
+    FileWriter fw = new FileWriter(file);
+    fw.append(data);
+    fw.close();
+  }
+    
+  public static String asCanonicalUserPathName (String path){
+    String userHome = System.getProperty("user.home");
+    int len = userHome.length();
+    if (path.startsWith(userHome) && path.charAt(len) == '/') {
+      return "${user.home}" + path.substring(len).replace('\\', '/');
+    } else {
+      return path.replace('\\', '/');
+    }
+  }
+  
+  public static String asUnixPathName (File file){
+    String userHome = System.getProperty("user.home") + File.separatorChar;
+    int uhLen = userHome.length();
+
+    String pn = file.getAbsolutePath();
+    if (pn.startsWith(userHome)) {
+      pn = "~/" + pn.substring(uhLen).replace('\\', '/');
+    } else {
+      pn = pn.replace('\\', '/');
+    }
+    return pn;
+  }
+
+  public static String unixToUserPathName (String unixPathName){
+    if (unixPathName.startsWith("~/")){
+      return "${user.home}" + unixPathName.substring(1);
+    } else {
+      String userHome = System.getProperty("user.home");
+      int len = userHome.length();
+      if (unixPathName.startsWith(userHome) && unixPathName.charAt(len) == '/'){
+        return "${user.home}" + unixPathName.substring(len);
+      } else {
+        return unixPathName;
+      }
+    }
+  }
+  
+  public static boolean ensureDirs (File file){
+    File dir = file.getParentFile();
+    if (!dir.isDirectory()){
+      return dir.mkdirs();
+    } else {
+      return true;
+    }
+  }
+  
+  public static String getRelativeUnixPath (File baseDir, File refFile) throws IOException {
+               String bpn = baseDir.getCanonicalPath().replace('\\', '/');
+               String rpn = refFile.getCanonicalPath().replace('\\', '/');
+
+               int len = Math.min(bpn.length(), rpn.length());
+               for (int i = 0, n = 0; i < len; i++) {
+                       char c = bpn.charAt(i);
+                       if (c == '/') {
+                               n = i + 1;
+                       } else if (c != rpn.charAt(i)) {
+                               bpn = bpn.substring(n);
+                               rpn = rpn.substring(n);
+                               break;
+                       }
+               }
+
+               len = bpn.length();
+               String up = "";
+               for (int i = 0; i < len; i++) {
+                       if (bpn.charAt(i) == '/') {
+                               up += "../";
+                       }
+               }
+
+               String relPath = up + rpn;
+               return relPath;
+  }
+  
+  public static boolean copyFile (File src, File toDir) throws IOException {
+    if (src.isFile()) {
+      File tgt = new File(toDir, src.getName());
+      if (tgt.createNewFile()) {
+        byte[] data = getContents(src);
+        setContents(tgt, data);
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/FinalBitSet.java b/src/main/gov/nasa/jpf/util/FinalBitSet.java
new file mode 100644 (file)
index 0000000..2b93289
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+
+/**
+ * Faster version of BitSet for those that never change.
+ */
+public class FinalBitSet {
+  final byte[] data;
+  
+  FinalBitSet(byte[] in) {
+    int len = in.length;
+    while (len > 0 && in[len - 1] == 0) len--;
+    this.data = new byte[len];
+    System.arraycopy(in, 0, this.data, 0, len);
+  }
+  
+  public final boolean get(int idx) {
+    int a = idx >> 3;
+    return a < data.length && a >= 0 && (data[a] & (1 << (idx & 7))) != 0;
+  }
+  
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(data);
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (! (o instanceof FinalBitSet)) return false;
+    byte[] thatData = ((FinalBitSet)o).data;
+    byte[] thisData = this.data;
+    return Arrays.equals(thisData, thatData);
+  }
+  
+  
+  /*======= Static Stuff ========*/
+  static final SimplePool<FinalBitSet> pool = new SimplePool<FinalBitSet>();
+  
+  public static final FinalBitSet empty = create(BitArray.empty);
+  
+  /**
+   * Creates a pooled FinalBitSet.
+   */
+  public static FinalBitSet create(BitArray in) {
+    return pool.pool(new FinalBitSet(in.data));
+  }
+
+  /**
+   * Creates a pooled FinalBitSet.
+   */
+  public static FinalBitSet create(byte[] in) {
+    return pool.pool(new FinalBitSet(in));
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/FixedBitSet.java b/src/main/gov/nasa/jpf/util/FixedBitSet.java
new file mode 100644 (file)
index 0000000..9614cf7
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+import java.util.NoSuchElementException;
+
+/**
+ * BitSet like interface for fixed size bit sets
+ * 
+ * We keep this as an interface so that we can have java.util.BitSet
+ * subclasses as implementations
+ */
+public interface FixedBitSet extends Cloneable, IntSet {
+
+  void set (int i);
+  void set (int i, boolean val);
+  boolean get (int i);
+  void clear (int i);
+  
+  int nextClearBit (int fromIndex);
+  int nextSetBit (int fromIndex);
+
+  boolean isEmpty();
+  int size();
+  
+  int cardinality();
+  int length();
+  int capacity();
+  
+  void clear();
+  
+  void hash (HashData hd);
+  
+  FixedBitSet clone();
+}
+
+/**
+ * this is the base class for our non java.util.BitSet based FixedBitSet implementations
+ */
+abstract class AbstractFixedBitSet implements FixedBitSet {
+  
+  class SetBitIterator implements IntIterator {
+    int cur = 0;
+    int nBits;
+    
+    @Override
+    public void remove() {
+      if (cur >0){
+        clear(cur-1);
+      }
+    }
+
+    @Override
+    public boolean hasNext() {
+      return nBits < cardinality;
+    }
+
+    @Override
+    public int next() {
+      if (nBits < cardinality){
+        int idx = nextSetBit(cur);
+        if (idx >= 0){
+          nBits++;
+          cur = idx+1;
+        }
+        return idx;
+        
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+  }
+
+  
+  protected int cardinality;
+  
+  @Override
+  public AbstractFixedBitSet clone(){
+    try {
+      return (AbstractFixedBitSet) super.clone();
+    } catch (CloneNotSupportedException ex) {
+      throw new JPFException("BitSet64 clone failed");
+    }  
+  }
+  
+  @Override
+  public void set (int i, boolean val){
+    if (val) {
+      set(i);
+    } else {
+      clear(i);
+    }
+  }
+
+  @Override
+  public int cardinality() {
+    return cardinality;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return (cardinality == 0);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append('{');
+
+    boolean first = true;
+    for (int i=nextSetBit(0); i>= 0; i = nextSetBit(i+1)){
+      if (!first){
+        sb.append(',');
+      } else {
+        first = false;
+      }
+      sb.append(i);
+    }
+
+    sb.append('}');
+
+    return sb.toString();
+  }
+
+  //--- IntSet interface
+  
+    
+  @Override
+  public boolean add(int i) {
+    if (get(i)) {
+      return false;
+    } else {
+      set(i);
+      return true;
+    }
+  }
+
+  @Override
+  public boolean remove(int i) {
+    if (get(i)) {
+      clear(i);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public boolean contains(int i) {
+    return get(i);
+  }
+
+  @Override
+  public IntIterator intIterator() {
+    return new SetBitIterator();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/Growth.java b/src/main/gov/nasa/jpf/util/Growth.java
new file mode 100644 (file)
index 0000000..ed1e079
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public interface Growth {
+  int grow(int oldSize, int minNewSize);
+  
+  public static final Growth defaultGrowth = new ExpGrowth(1.7f);
+}
diff --git a/src/main/gov/nasa/jpf/util/HashData.java b/src/main/gov/nasa/jpf/util/HashData.java
new file mode 100644 (file)
index 0000000..2cab3dc
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * object to compute complex hash values that can be accumulated and
+ * delegated (to aggregates etc.)
+ * used to obtain hashcodes for states
+ */
+public class HashData {
+  private static final int poly = 0x88888EEF;
+  private int m = -1;
+
+  public void reset() {
+    m = -1;
+  }
+  
+  public int getValue () {
+    return (m >>> 4) ^ (m & 15);
+  }
+
+  public void add (int value) {
+    if (m < 0) {
+      m += m;
+      m ^= poly;
+    } else {
+      m += m;
+    }
+
+    m ^= value;
+  }
+
+  public void add (long value){
+    add((int)(value ^ (value >>> 32)));
+  }
+
+  public void add (Object o) {
+    if (o != null) {
+      add(o.hashCode());
+    }
+  }
+  
+  public void add (boolean b) {
+    // a clear case of '42', but that's the "official" boolean hashing
+    add(b ? 1231 : 1237);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/HashPool.java b/src/main/gov/nasa/jpf/util/HashPool.java
new file mode 100644 (file)
index 0000000..518079e
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.ArrayList;
+
+
+/**
+ * data structure used to do hash collapsing. All the major state components
+ * (fields, Monitors, StackFrames, uThreadData) are stored in pools to
+ * determine if they are new. Only the pool index values are used to
+ * compute hash values.
+ * <p>
+ * 2006-06-14 - major rewrite by pcd
+ */
+public final class HashPool<V> {
+  private IntTable<V> pool;
+  private ArrayList<V> vect;
+  
+  public HashPool() {
+    this(8); // default to 256 slots
+  }
+  
+  public HashPool(int pow) {
+    pool = new IntTable<V>(pow);
+    vect = new ArrayList<V>(1 << pow);
+  }
+
+  /** optionally called only once after creation to link null to 0. */ 
+  public HashPool<V> addNull() {
+    if (size() == 0) {
+      pool.add(null, 0);
+      vect.add(null);
+      return this;
+    } else {
+      throw new IllegalStateException();
+    }
+  }
+  
+  public IntTable.Entry<V> getEntry (V o) {
+    int sz = pool.size(); // == vect.size();
+    
+    IntTable.Entry<V> e = pool.pool(o);
+    if (e.val == sz) {
+      vect.add(o);
+    }
+    return e;
+  }
+
+  public int getIndex (V o) {
+    return getEntry(o).val;
+  }
+
+  public V get (V o) {
+    return getEntry(o).key;
+  }
+
+  public V getObject (int idx) {
+    return vect.get(idx);
+  }
+
+  public void print () {
+    System.out.println("{");
+
+    for (IntTable.Entry<V> entry : pool) {
+      System.out.println("\t" + entry);
+    }
+
+    System.out.println("}");
+  }
+
+  public int size () {
+    return pool.size();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/IdentityArrayObjectSet.java b/src/main/gov/nasa/jpf/util/IdentityArrayObjectSet.java
new file mode 100644 (file)
index 0000000..81b9fb7
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * simple identity set for objects 
+ * we don't sort&bisect, assuming the number of entries will be small
+ * be aware this doesn't scale to large sets
+ */
+public class IdentityArrayObjectSet<E> implements IdentityObjectSet<E> {
+
+  static final int DEFAULT_CAPACITY = 4;
+  
+  private class StoreOrderIterator implements Iterator<E> {
+    int next = 0;
+
+    @Override
+    public void remove() {
+      int idx = next-1;
+      if (idx >=0){
+        if (idx < size-1){
+          System.arraycopy(elements, next, elements, idx, size-idx);
+        }
+        size--;
+        next = idx;
+      }
+    }
+
+    @Override
+    public boolean hasNext() {
+      return (next < size);
+    }
+
+    @Override
+    public E next() {
+      if (next < size){
+        return (E) elements[next++];
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+  }
+  
+  protected int size;
+  protected Object[] elements;
+  
+  public IdentityArrayObjectSet(){
+    // nothing, elements allocated on demand
+  }
+  
+  public IdentityArrayObjectSet (int initialCapacity){
+    elements = new Object[initialCapacity];
+  }
+  
+  public IdentityArrayObjectSet (E initialElement){
+    elements = new Object[DEFAULT_CAPACITY];
+    
+    elements[0] = initialElement;
+    size = 1;
+  }
+  
+  @Override
+  public int size(){
+    return size;
+  }
+  
+  public boolean isEmpty(){
+    return (size == 0);
+  }
+  
+  @Override
+  public boolean add (E obj){
+    for (int i=0; i<size; i++){
+      if (elements[i] == obj){
+        return false;
+      }
+    }
+    
+    if (size == 0){
+      elements = new Object[DEFAULT_CAPACITY];
+    } else if (size == elements.length){
+      Object[] newElements = new Object[elements.length * 3 / 2];
+      System.arraycopy(elements, 0, newElements, 0, size);
+      elements = newElements;
+    }
+    
+    elements[size] = obj;
+    size++;
+    return true;
+  }
+  
+  @Override
+  public boolean contains (E obj){
+    for (int i=0; i<size; i++){
+      if (elements[i] == obj){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  @Override
+  public boolean remove (E obj){
+    int len = size;
+    for (int i=0; i<len; i++){
+      if (elements[i] == obj){
+        len--;
+        if (len == 0){
+          size = 0;
+          elements = null;
+          
+        } else if (i < len){
+          System.arraycopy(elements, i+1, elements, i, len-i);
+        } else {
+          elements[len] = null; // avoid memory leak
+        }
+        
+        size = len;
+        return true;
+      }
+    }
+    
+    return false;    
+  }
+  
+  @Override
+  public ObjectSet<E> clone(){
+    try {
+      return (ObjectSet<E>)super.clone();
+    } catch (CloneNotSupportedException x){
+      // can't happen
+      return null;
+    }
+  }
+  
+  @Override
+  public Iterator<E> iterator(){
+    return new StoreOrderIterator();
+  }
+  
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder(/*getClass().getName()*/);
+    sb.append('{');
+    for (int i=0; i<size; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append(elements[i]);
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/IdentityObjectSet.java b/src/main/gov/nasa/jpf/util/IdentityObjectSet.java
new file mode 100644 (file)
index 0000000..bdacbe5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * an ObjectSet that uses reference comparison (identity) for inclusion checks
+ */
+public interface IdentityObjectSet<E> extends ObjectSet<E>, Iterable<E> {
+  // no new methods, just different inclusion checks
+}
diff --git a/src/main/gov/nasa/jpf/util/ImmutableList.java b/src/main/gov/nasa/jpf/util/ImmutableList.java
new file mode 100644 (file)
index 0000000..31fb96b
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * utility class for JPF internal linked lists that are tail-immutable 
+ */
+public class ImmutableList<E> implements Iterable<E> {
+
+  static class IteratorImpl<E> implements Iterator<E> {
+
+    private ImmutableList<E> next;
+    
+    private IteratorImpl(ImmutableList<E> list){
+      next = list;
+    }
+    
+    @Override
+       public boolean hasNext() {
+      return (next != null);
+    }
+
+    @Override
+       public E next() {
+      if (next != null){
+        E elem = next.head;
+        next = next.tail;
+        return elem;
+        
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("can't remove elements from ImmutableList");
+    }
+    
+  }
+  
+  public final E head;
+  public final ImmutableList<E> tail;
+  
+  
+  public ImmutableList(E data, ImmutableList<E> tail) {
+    this.head = data;
+    this.tail = tail;
+  }
+  
+  @Override
+  public Iterator<E> iterator() {
+    return new IteratorImpl(this);
+  }
+  
+  public boolean contains (E object){
+    for (E e : this){
+      if (e.equals(object)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/IndexIterator.java b/src/main/gov/nasa/jpf/util/IndexIterator.java
new file mode 100644 (file)
index 0000000..e2d2f51
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * specialized iterator class for index values [0..N]
+ */
+public interface IndexIterator {
+
+  /**
+   * answer next index value in iteration
+   *  @returns -1 if no next index
+   */
+  int next();
+}
diff --git a/src/main/gov/nasa/jpf/util/InstructionState.java b/src/main/gov/nasa/jpf/util/InstructionState.java
new file mode 100644 (file)
index 0000000..ba01b1f
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.SystemAttribute;
+
+/**
+ * a abstract SystemAttribute that can be used to store instruction
+ * execution state between top and bottom halves of respective instruction.execute() methods
+ */
+public abstract class InstructionState implements SystemAttribute {
+  static class Processed extends InstructionState {}
+  public static final Processed processed = new Processed(); // no need to burn lots of objects if we don't have state
+  
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/IntArray.java b/src/main/gov/nasa/jpf/util/IntArray.java
new file mode 100644 (file)
index 0000000..9e1839d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+
+/**
+ * Wrapper for int[] that provides proper equals() and hashCode() methods.
+ */
+public class IntArray {
+  public final int[] data;
+  
+  public IntArray(int size) {
+    data = new int[size];
+  }
+  
+  public IntArray(int[] data) {
+    this.data = data;
+  }
+  
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(data);
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (! (o instanceof IntArray)) return false;
+    int[] thatData = ((IntArray)o).data;
+    int[] thisData = this.data;
+    return Arrays.equals(thisData, thatData);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/IntIterator.java b/src/main/gov/nasa/jpf/util/IntIterator.java
new file mode 100644 (file)
index 0000000..cae3d76
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * just a little helper to iterate over collections of ints without doing
+ * a lot of boxing/unboxing
+ */
+public interface IntIterator {
+  void remove();
+  boolean hasNext();
+  int next();
+}
diff --git a/src/main/gov/nasa/jpf/util/IntSet.java b/src/main/gov/nasa/jpf/util/IntSet.java
new file mode 100644 (file)
index 0000000..1571975
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public interface IntSet extends Cloneable {
+
+  boolean add (int i);
+  boolean remove (int i);
+  boolean contains (int i);
+  
+  boolean isEmpty();
+  int size();
+  
+  void clear();
+  
+  IntIterator intIterator();
+  
+  IntSet clone();
+}
diff --git a/src/main/gov/nasa/jpf/util/IntTable.java b/src/main/gov/nasa/jpf/util/IntTable.java
new file mode 100644 (file)
index 0000000..b1a8069
--- /dev/null
@@ -0,0 +1,573 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+
+import java.util.Iterator;
+
+/**
+ * A hash map that holds int values associated with generic key objects.
+ * This is a straight forward linked list hashmap.
+ *
+ * Key objects have to be invariant, lookup uses equality but checks for
+ * identity as an optimization.
+ *
+ * note: this does deep copy clones, which can be quite expensive
+ */
+public final class IntTable<E> implements Iterable<IntTable.Entry<E>>, Cloneable{
+  static final int INIT_TBL_POW = 7;
+  static final double MAX_LOAD = 0.80;
+  
+  //--- inner types
+  
+  /**
+   * encapsulates an Entry in the table.  changes to val will be reflected
+   * in the table.
+   */  
+  public static class Entry<E> implements Cloneable {
+    public final E key;
+    public final int val;
+    protected Entry<E> next;
+    
+    protected Entry(E k, int v) {
+      key = k;
+      val = v;
+      next = null;
+      }
+    protected Entry(E k, int v, Entry<E> n) {
+      key = k; 
+      val = v;
+      next = n; 
+    }
+
+    @Override
+       @SuppressWarnings("unchecked")
+    public Entry<E> clone() {
+      try {
+        return (Entry<E>)super.clone();
+      } catch (CloneNotSupportedException x){
+        throw new JPFException("clone failed");
+      }
+    }
+
+    @Override
+       public String toString() {
+      return key.toString() + " => " + val;
+    }
+    
+    //--- methods required to use IntTable entries itself as HashMap keys
+    // but beware - val can be modified since we expose it (never modify
+    // key objects of HashMaps)
+
+    @Override
+       public int hashCode (){
+      return OATHash.hash(key.hashCode(), val);
+    }
+    
+    @Override
+       public boolean equals (Object o){
+      if (o instanceof Entry){
+        @SuppressWarnings("unchecked")
+        Entry<E> other = (Entry<E>)o;
+        if (val == other.val){
+          E k = other.key;
+          if (key == k || key.equals(k)){
+            return true;
+          }
+        }
+      }
+      
+      return false;
+    }
+  }
+  
+  /**
+   * helper class to store a compact, invariant representation of this table
+   */
+  public static class Snapshot<E> {
+    protected final int tblSize;
+    protected final int tblPow;
+    
+    protected final int[] indices;
+    protected final E[] keys;
+    protected final int[] vals;
+    
+    @SuppressWarnings("unchecked")
+    protected Snapshot (IntTable<E> t){
+      Entry<E>[] tbl = t.table;
+      int nEntries = t.size;
+            
+      tblSize = tbl.length;
+      tblPow = t.tblPow;
+      
+      indices = new int[nEntries];
+      keys = (E[]) new Object[nEntries];
+      vals = new int[nEntries];
+      
+      int j = 0;
+      for (int i=0; i<tbl.length && j<nEntries; i++){
+        Entry<E> e = tbl[i];
+        if (e != null){
+          if (e.next == null){ // just one entry under this head
+            
+            indices[j] = i;
+            keys[j] = e.key;
+            vals[j] = e.val;
+            j++;
+            
+          } else {
+            // we have to store in reverse order so that restore preserves it
+            // we do the revert here because storing happens once, whereas restore can happen many times
+            int n = 1;
+            for (Entry<E> ee = e.next; ee != null; ee = ee.next){
+              n++;
+            }
+
+            int k = j+n-1;
+            j += n;
+            for (; e != null; e = e.next){  
+              indices[k] = i;
+              keys[k] = e.key;
+              vals[k] = e.val;
+              k--;
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  //--- instance fields
+  
+  protected Entry<E>[] table;  // array of entry heads
+  protected int tblPow;        // = log_2(table.length)
+  protected int mask;          // = table.length - 1
+  protected int nextRehash;    // = ceil(MAX_LOAD * table.length);
+  protected int size;          // number of Entry<E> objects reachable from table
+  
+  protected Entry<E> nullEntry = null;
+  
+  Snapshot<E> lastSnapshot;   // cache for the last snapshot (nulled once the IntTable is changed)
+  
+  public IntTable() {
+    this(INIT_TBL_POW);
+  }
+  
+  public IntTable(int pow) {
+    newTable(pow);
+    size = 0;
+  }
+  
+  public Snapshot<E> getSnapshot(){
+    if (lastSnapshot == null) {
+      lastSnapshot = new Snapshot<E>(this);
+    }
+    
+    return lastSnapshot;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public void restore (Snapshot<E> snapshot){
+    Entry<E>[] tbl = new Entry[snapshot.tblSize];
+    
+    int[] indices = snapshot.indices;
+    E[] keys = snapshot.keys;
+    int[] vals = snapshot.vals; 
+    int nEntries = vals.length;
+    
+    for (int i=0; i<nEntries; i++){
+      int idx = indices[i];
+      tbl[idx] = new Entry<E>( keys[i], vals[i], tbl[idx]);
+    }
+    
+    table = tbl;
+    size = nEntries;
+    mask = table.length -1;
+    nextRehash = (int) Math.ceil(MAX_LOAD * table.length);
+    tblPow = snapshot.tblPow;
+    
+    lastSnapshot = snapshot;
+  }
+
+  // this is a deep copy (needs to be because entries are reused when growing the table)
+  @Override
+  public IntTable<E> clone() {
+    try {
+      @SuppressWarnings("unchecked")
+      IntTable<E> t = (IntTable<E>)super.clone();
+      Entry<E>[] tbl = table.clone();
+      t.table = tbl;
+
+      // clone entries
+      int len = table.length;
+      for (int i=0; i<len; i++){
+        Entry<E> eFirst = tbl[i];
+        if (eFirst != null){
+          eFirst = eFirst.clone();
+          Entry<E> ePrev = eFirst;
+          for (Entry<E> e = eFirst.next; e != null; e = e.next){
+            e = e.clone();
+            ePrev.next = e;
+            ePrev = e;
+          }
+          tbl[i] = eFirst;
+        }
+      }
+
+      return t;
+
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("clone failed");
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  protected void newTable(int pow) {
+    tblPow = pow;
+    table = new Entry[1 << tblPow];
+    mask = table.length - 1;
+    nextRehash = (int) Math.ceil(MAX_LOAD * table.length);
+  }
+  
+  protected int getTableIndex(E key) {
+    int hc = key.hashCode();
+    int ret = hc ^ 786668707;
+    ret += (hc >>> tblPow);
+    return (ret ^ 1558394450) & mask;
+  }
+
+  protected boolean maybeRehash() {
+    if (size < nextRehash){
+      return false;
+      
+    } else {
+      lastSnapshot = null;
+      size = 0;
+      Entry<E>[] old = table;
+      int oldTblLength = old.length;
+      newTable(tblPow + 1);
+      int len = oldTblLength;
+      for (int i = 0; i < len; i++) {
+        addList(old[i]);
+      }
+
+      return true;
+    }
+  }
+  
+  private void addList(Entry<E> e) {
+    Entry<E> cur = e;
+    while (cur != null) {
+      Entry<E> tmp = cur;
+      cur = cur.next;
+      addEntry(tmp, getTableIndex(tmp.key));
+    }
+  }
+  
+  //--- the methods traversing the entry lists
+  
+  // helper for adding
+  protected void addEntry(Entry<E> e, int idx) {
+    e.next = table[idx];
+    table[idx] = e;
+    
+    size++;
+    lastSnapshot = null;
+  }
+  
+  // helper for searching
+  protected Entry<E> getEntry(E key, int idx) {
+    Entry<E> cur = table[idx];
+    while (cur != null) {
+      E k = cur.key;
+      
+      // note - this assumes invariant keys !!
+      if (k == key || (k.equals(key))){
+        return cur;
+      }
+      cur = cur.next;
+    }
+    return null; // not found
+  }
+
+  // helper for value update
+  protected void replaceEntryValue( int idx, Entry<E> oldEntry, int newValue) {
+    Entry<E> last = null;
+    
+    for (Entry<E> e = table[idx]; e != null; e = e.next, last = e) {
+      if (e == oldEntry) {
+        Entry<E> newEntry = new Entry<E>(oldEntry.key, newValue);
+        newEntry.next = e.next;
+        lastSnapshot = null;
+        
+        if (last == null) {
+          table[idx] = newEntry;
+        } else {
+          last.next = newEntry;
+        }
+      }
+    }    
+  }
+  
+  //--- public methods
+  
+  /** returns number of bindings in the table. */
+  public int size() {
+    return size;
+  }
+  
+  /** ONLY USE IF YOU ARE SURE NO PREVIOUS BINDING FOR key EXISTS. */
+  public Entry<E> add (E key, int val) {
+    Entry<E> e = new Entry<E>(key,val);
+    if (key == null) {
+      nullEntry = e;
+    } else {
+      maybeRehash();
+      addEntry(e, getTableIndex(key));
+    }
+    
+    return e;
+  }
+  
+  /** lookup, returning null if no binding. */
+  public Entry<E> get (E key) {
+    return getEntry(key, getTableIndex(key));
+  }
+  
+  /**
+   * a little optimization to speed up counter increments
+   */
+  public Entry<E> getInc (E key){
+    int idx = getTableIndex(key);
+    
+    Entry<E> last = null;
+    for (Entry<E> e = table[idx]; e != null; e = e.next) {
+      if (e.key == key || e.key.equals(key)) { // found it, replace entry
+        Entry<E> newEntry = new Entry<E>(key, e.val+1, e.next);
+        lastSnapshot = null;
+        
+        if (last == null) {
+          table[idx] = newEntry;
+        } else {
+          last.next = newEntry;
+        }
+        
+        return newEntry;
+        
+      } else {
+        last = e;
+      }
+    }
+    
+    // it wasn't there, add a new entry with value 1
+    Entry<E> newEntry = new Entry<E>( key, 1);
+    if (maybeRehash()) {
+      idx = getTableIndex(key);
+    }
+    addEntry( newEntry, idx);
+
+    return newEntry;
+  }
+  
+  /** just like HashMap put. */
+  public void put(E key, int val) {    
+    if (key == null) {
+      if (nullEntry == null) {
+        nullEntry = new Entry<E>(null,val);
+        size++;
+      } else {
+        nullEntry = new Entry<E>(null, val);
+      }
+      return;
+    }
+    
+    int idx = getTableIndex(key);
+    Entry<E> e = getEntry(key, idx);
+    if (e == null) { // wasn't there
+      if (maybeRehash()){
+        idx = getTableIndex(key);
+      }
+      addEntry(new Entry<E>(key,val), idx);
+      
+    } else {
+      replaceEntryValue( idx, e, val);
+      lastSnapshot = null;
+    }
+  }
+
+  /** removes a binding/entry from the table. */
+  public Entry<E> remove(E key) {
+    int idx = getTableIndex(key);
+    Entry<E> prev = null;
+    Entry<E> cur = table[idx];
+    while (cur != null) {
+      E k = cur.key;
+      if (k == key || k.equals(key)) {
+        if (prev == null) {
+          table[idx] = cur.next;
+        } else {
+          prev.next = cur.next;
+        }
+        cur.next = null;
+        size--;
+        lastSnapshot = null;
+    
+        return cur;
+      }
+      prev = cur;
+      cur = cur.next;
+    }
+    
+    return null; // not found
+  }
+  
+  
+  /** empties the table, leaving it capacity the same. */
+  public void clear() {
+    table = new Entry[table.length];
+    nullEntry = null;
+    size = 0;
+    lastSnapshot = null;
+  }
+  
+  /** returns the next val to be assigned by a call to pool() on a fresh key. */
+  public int nextPoolVal() {
+    return size;
+  }
+  
+  /** gets the Entry associated with key, adding previous `size' if not yet bound. */
+  public Entry<E> pool(E key) {
+    if (key == null) {
+      if (nullEntry == null) {
+        nullEntry = new Entry<E>(null,size);
+        size++;
+      }
+      return nullEntry;
+    }
+    
+    int idx = getTableIndex(key);
+    Entry<E> e = getEntry(key, idx);
+    if (e == null) {
+      if (maybeRehash()) {
+        idx = getTableIndex(key);
+      }
+      e = new Entry<E>(key,size);
+      addEntry(e, idx);
+    }
+    return e;
+  }
+  
+  /** shorthand for <code>pool(key).val</code>. */
+  public int poolIndex(E key) {
+    return pool(key).val;
+  }
+  
+  /** shorthand for <code>pool(key).key</code>. */
+  public E poolKey(E key) {
+    return pool(key).key;
+  }
+  
+  /** shorthand for <code>get(key) != null</code>. */
+  public boolean hasEntry(E key) {
+    return get(key) != null;
+  }
+  
+
+
+  /**
+   * returns an iterator over the entries.  unpredictable behavior could result if
+   * using iterator after table is altered.
+   */
+  @Override
+  public Iterator<Entry<E>> iterator () {
+    return new TblIterator();
+  }
+
+  protected class TblIterator implements Iterator<Entry<E>> {
+    int idx;
+    Entry<E> cur;
+
+    public TblIterator() {
+      idx = -1; cur = null;
+      advance();
+    }
+    
+    void advance() {
+      if (cur != null) {
+        cur = cur.next;
+      }
+      int len = table.length;
+      while (idx < len && cur == null) {
+        idx++;
+        if (idx < len) {
+          cur = table[idx];
+        }
+      }
+    }
+    
+    @Override
+       public boolean hasNext () {
+      return idx < table.length;
+    }
+
+    @Override
+       public Entry<E> next () {
+      Entry<E> e = cur;
+      advance();
+      return e;
+    }
+
+    @Override
+       public void remove () { 
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  /**
+   * for debugging purposes
+   */
+  public void dump(){
+    System.out.print('{');
+    int n=0;
+    for (int i=0; i<table.length; i++){
+      for (Entry<E> e = table[i]; e != null; e = e.next){
+        if (n++>0){
+          System.out.print(',');
+        }
+        System.out.print('(');
+        System.out.print(e.key);
+        System.out.print("=>");
+        System.out.print(e.val);
+        System.out.print(')');
+      }
+    }
+    System.out.println('}');
+  }
+  
+  public int computeSize() {
+    int n=0;
+    for (int i=0; i<table.length; i++){
+      for (Entry<E> e = table[i]; e != null; e = e.next){
+        n++;
+      }
+    }
+    
+    return n;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/IntVector.java b/src/main/gov/nasa/jpf/util/IntVector.java
new file mode 100644 (file)
index 0000000..4ad54a2
--- /dev/null
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+
+
+/**
+ * (more efficient?) alternative to Vector<Integer>
+ * @author pcd
+ */
+public final class IntVector implements Comparable<IntVector>, Cloneable {
+  public static final int defaultInitCap = 40;
+
+  
+  /** <i>size</i> as in a java.util.Vector. */
+  protected int size;
+  
+  /** the backing array. */
+  protected int[] data;
+  
+  /** growth strategy. */
+  protected Growth growth;
+  
+  
+  public IntVector(Growth initGrowth, int initCap) {
+    growth = initGrowth;
+    data = new int[initCap];
+    size = 0;
+  }
+  
+  public IntVector(int... init) {
+    this(Growth.defaultGrowth, init.length);
+    size = init.length;
+    System.arraycopy(init, 0, data, 0, size);
+  }
+  
+  public IntVector(Growth initGrowth) { this(initGrowth,defaultInitCap); }
+  
+  public IntVector(int initCap) { this(Growth.defaultGrowth, initCap); }
+  
+  public IntVector() { this(Growth.defaultGrowth,defaultInitCap); }
+
+  @Override
+  public IntVector clone() {
+    try {
+      IntVector v = (IntVector)super.clone();
+      v.data = data.clone();
+      return v;
+
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("IntVector clone failed");
+    }
+  }
+
+  public void add(int x) {
+    try {
+      data[size] = x;
+      size++;
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      ensureCapacity(size+1);
+      data[size] = x;
+      size++;
+    }
+  }
+
+  public boolean addIfAbsent (int x) {
+    for (int i=0; i<size; i++){
+      if (data[i] == x){
+        return false;
+      }
+    }
+    add(x);
+    return true;
+  }
+
+  public void add (long x) {
+    if (size+2 > data.length) {
+      ensureCapacity(size+2);
+    }
+    data[size] = (int)(x >> 32);
+    size++;
+    data[size] = (int)x;
+    size++;    
+  }
+  
+  public void add(int x1, int x2) {
+    if (size+2 > data.length) {
+      ensureCapacity(size+2);
+    }
+    data[size] = x1;
+    size++;
+    data[size] = x2;
+    size++;
+  }
+  
+  public void add(int x1, int x2, int x3) {
+    if (size+3 > data.length) {
+      ensureCapacity(size+3);
+    }
+    data[size] = x1;
+    size++;
+    data[size] = x2;
+    size++;
+    data[size] = x3;
+    size++;
+  }
+  
+  public void addZeros (int length) {
+    int newSize = size + length;
+    if (newSize > data.length) {
+      ensureCapacity(size + length);
+    }
+    for (int i = size; i < newSize; i++) {
+      data[i] = 0;
+    }
+    size = newSize;
+  }
+
+  public void append(int[] a) {
+    int newSize = size + a.length;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    System.arraycopy(a, 0, data, size, a.length);
+    size = newSize;
+  }
+
+  public void append(int[] x, int pos, int len) {
+    int newSize = size + len;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    System.arraycopy(x, pos, data, size, len);
+    size = newSize;
+  }
+
+  public void append(boolean[] a){
+    int newSize = size + a.length;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    for (int i = size; i < newSize; i++) {
+      data[i] = a[i] ? 1 : 0;
+    }
+    size = newSize;
+  }
+
+  public void appendPacked(boolean[] a){
+    int len = (a.length+31) >> 5;  // new data length, 32 booleans per word
+    int n = a.length >> 5; // number of full data words
+    int len1 = n << 5;
+    int rem = a.length % 32;
+
+    int newSize = size + len;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+
+    int k=size;
+    int x=0;
+    int i=0;
+    while (i<len1){ // store full data words
+      x = a[i++] ? 1 : 0;
+      for (int j=1; j<32; j++){
+        x <<= 1;
+        if (a[i++]){
+          x |= 1;
+        }
+      }
+      data[k++] = x;
+    }
+
+    if (rem > 0) { // store partial word
+      if (i>0){
+        x = a[i-1] ? 1 : 0;
+      } else {
+        x = a[i++] ? 1 : 0;
+      }
+      while (i < a.length) {
+        x <<= 1;
+        if (a[i++]) {
+          x |= 1;
+        }
+      }
+      x <<= (32-rem);
+      data[k] = x;
+    }
+
+    size = newSize;
+  }
+
+  public void appendPacked(byte[] a){
+    int len = (a.length+3)/4;  // new data length, 4 bytes per word
+    int n = a.length >> 2; // number of full data words
+    int len1 = n << 2;
+
+    int newSize = size + len;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+
+    int j=0;
+    int k=size;
+    int x;
+    while (j<len1){
+      x = a[j++] & 0xff;  x <<= 8;
+      x |= a[j++] & 0xff; x <<= 8;
+      x |= a[j++] & 0xff; x <<= 8;
+      x |= a[j++] & 0xff;
+      data[k++] = x;
+    }
+
+    switch (a.length % 4){
+      case 0:
+        break;
+      case 1:
+        data[k] = (a[j] << 24);
+        break;
+      case 2:
+        x = a[j++] & 0xff;  x <<= 8;
+        x |= a[j] & 0xff;   x <<= 16;
+        data[k] = x;
+        break;
+      case 3:
+        x = a[j++] & 0xff;  x <<= 8;
+        x |= a[j++] & 0xff; x <<= 8;
+        x |= a[j] & 0xff;   x <<= 8;
+        data[k] = x;
+        break;
+    }
+
+    size = newSize;
+  }
+
+
+  public void appendPacked(char[] a){
+    int len = (a.length+1)/2;  // new data length, 2 chars per word
+    int n = a.length >> 1; // number of full data words
+    int len1 = n << 1;
+
+    int newSize = size + len;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+
+    int j=0;
+    int k=size;
+    while (j<len1){
+      int x = a[j++] & 0xffff;  x <<= 16;
+      x |= a[j++] & 0xffff;
+      data[k++] = x;
+    }
+
+    if (a.length % 2 > 0){
+      data[k] = (a[j] & 0xffff) << 16;
+    }
+
+    size = newSize;
+  }
+
+  public void appendPacked(short[] a){
+    int len = (a.length+1)/2;  // new data length, 2 chars per word
+    int n = a.length >> 1; // number of full data words
+    int len1 = n << 1;
+
+    int newSize = size + len;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+
+    int j=0;
+    int k=size;
+    while (j<len1){
+      int x = a[j++] & 0xffff;  x <<= 16;
+      x |= a[j++] & 0xffff;
+      data[k++] = x;
+    }
+
+    if (a.length % 2 > 0){
+      data[k] = (a[j] & 0xffff) << 16;
+    }
+
+    size = newSize;
+  }
+
+  public void appendRawBits(float[] a){
+    int newSize = size + a.length;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    int k=size;
+    for (int i = 0; i < a.length; i++) {
+      data[k++] = Float.floatToRawIntBits(a[i]);
+    }
+    size = newSize;
+  }
+
+
+  public void appendBits(long[] a){
+    int newSize = size + a.length * 2;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    
+    int k = size;
+    for (int i = 0; i<a.length; i++){
+      long l = a[i];
+      data[k++] = (int) (l >>> 32);
+      data[k++] = (int) (l & 0xffffffff);
+    }
+    
+    size = newSize;    
+  }
+
+  public void appendRawBits(double[] a){
+    int newSize = size + a.length * 2;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+
+    int k = size;
+    for (int i = 0; i<a.length; i++){
+      long l = Double.doubleToRawLongBits(a[i]);
+      data[k++] = (int) (l >>> 32);
+      data[k++] = (int) (l & 0xffffffff);
+    }
+
+    size = newSize;
+  }
+
+  
+  public void append(IntVector x) {
+    if (x == null) return;
+    if (size + x.size > data.length) {
+      ensureCapacity(size + x.size);
+    }
+    System.arraycopy(x.data, 0, data, size, x.size);
+    size += x.size;
+  }
+
+  public boolean removeFirst (int x){
+    for (int i=0; i<size; i++){
+      if (data[i] == x){
+        System.arraycopy(data,i+1, data,i, size-i);
+        size--;
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public int get(int idx) {
+    if (idx >= size) {
+      return 0;
+    } else {
+      return data[idx];
+    }
+  }
+  
+  public void set(int idx, int x) {
+    ensureSize(idx + 1);
+    data[idx] = x;
+  }
+
+  public int getFirstIndexOfValue(int x) {
+    for (int i=0; i<size; i++){
+      if (data[i] == x){
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  public boolean contains (int x) {
+    for (int i=0; i<size; i++){
+      if (data[i] == x){
+        return true;
+      }
+    }
+    return false;
+  }
+
+
+  public int[] toArray (int[] dst) {
+    System.arraycopy(data,0,dst,0,size);
+    return dst;
+  }
+
+  public int dumpTo (int[] dst, int pos) {
+    System.arraycopy(data,0,dst,pos,size);
+    return pos + size;
+  }
+
+  public void squeeze() {
+    while (size > 0 && data[size - 1] == 0) size--;
+  }
+  
+  public void setSize(int sz) {
+    if (sz > size) {
+      ensureCapacity(sz);
+      size = sz;
+    } else {
+      while (size > sz) {
+        size--;
+        data[size] = 0;
+      }
+    }
+  }
+
+  public void clear() { setSize(0); }
+  
+  public int size() { return size; }
+  
+  public int[] toArray() {
+    int[] out = new int[size];
+    System.arraycopy(data, 0, out, 0, size);
+    return out;
+  }
+
+  public void ensureSize(int sz) {
+    if (size < sz) {
+      ensureCapacity(sz);
+      size = sz;
+    }
+  }
+  
+  public void ensureCapacity(int desiredCap) {
+    if (data.length < desiredCap) {
+      int[] newData = new int[growth.grow(data.length, desiredCap)];
+      System.arraycopy(data, 0, newData, 0, size);
+      data = newData;
+    }
+  }
+  
+  public static void copy(IntVector src, int srcPos, IntVector dst, int dstPos, int len) {
+    if (len == 0) return;
+    src.ensureCapacity(srcPos + len);
+    dst.ensureSize(dstPos+len);
+    System.arraycopy(src.data, srcPos, dst.data, dstPos, len);
+  }
+
+  public static void copy(int[] src, int srcPos, IntVector dst, int dstPos, int len) {
+    if (len == 0) return;
+    dst.ensureSize(dstPos+len);
+    System.arraycopy(src, srcPos, dst.data, dstPos, len);
+  }
+
+  public static void copy(IntVector src, int srcPos, int[] dst, int dstPos, int len) {
+    if (len == 0) return;
+    src.ensureCapacity(srcPos + len);
+    System.arraycopy(src.data, srcPos, dst, dstPos, len);
+  }
+
+  /** dictionary/lexicographic ordering */
+  @Override
+  public int compareTo (IntVector that) {
+    if (that == null) return this.size; // consider null and 0-length the same
+    
+    int comp;
+    int smaller = Math.min(this.size, that.size);
+    for (int i = 0; i < smaller; i++) {
+      comp = this.data[i] - that.data[i];
+      if (comp != 0) return comp;
+    }
+    // same up to shorter length => compare sizes
+    return this.size - that.size;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Invocation.java b/src/main/gov/nasa/jpf/util/Invocation.java
new file mode 100644 (file)
index 0000000..6ddaf7d
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ObjRef;
+
+/**
+ * a record that includes all information to perform a call
+ */
+public class Invocation {
+  MethodInfo mi;
+  Object[] args;  // includes 'this' for instance methods
+  Object[] attrs;
+  
+  public Invocation (MethodInfo mi, Object[] args, Object[] attrs){
+    this.mi = mi;
+    this.args = args;
+    this.attrs = attrs;
+  }
+  
+  public MethodInfo getMethodInfo () {
+    return mi;
+  }
+  
+  public Object[] getExplicitArguments () {
+    if (!mi.isStatic()){
+      Object[] a = new Object[args.length-1];
+      System.arraycopy(args,1,a,0,a.length);
+      return a;
+    } else {
+      return args;
+    }
+  }
+  
+  public String[] getArgumentTypeNames() {
+    return mi.getArgumentTypeNames();
+  }
+  
+  public String getArgumentValueLiteral(Object a) {
+    Class<?> cls = a.getClass();
+    
+    if (cls == Boolean.class)   return ((Boolean)a).toString();
+    if (cls == Byte.class)      return ((Byte)a).toString();
+    if (cls == Character.class) return ((Character)a).toString();
+    if (cls == Short.class)     return ((Short)a).toString();
+    if (cls == Integer.class)   return ((Integer)a).toString();
+    if (cls == Long.class)      return ((Long)a).toString();
+    if (cls == Float.class)     return ((Float)a).toString();
+    if (cls == Double.class)    return ((Double)a).toString();
+
+    if (cls == ObjRef.class) {
+      int ref = ((ObjRef)a).getReference();
+      
+      if (ref != MJIEnv.NULL){
+        ElementInfo ei = VM.getVM().getElementInfo(ref);
+        ClassInfo ci = ei.getClassInfo();
+        String cname = ci.getName();
+        if (cname.equals("java.lang.String")){
+          return "\"" + ei.asString() + '"';
+        } 
+        // <2do> we could probably do some more literals for java.lang.Class etc.
+      } else {
+        return "null";
+      }
+    }
+    
+    return null; // no literal representation
+  }
+  
+  public Object[] getArguments() {
+    return args;
+  }
+  
+  public Object[] getAttrs() {
+    return attrs;
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    
+    sb.append("INVOKE[");
+    sb.append(mi.getName());
+    sb.append('(');
+    for (int i=0; i<args.length; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append(args[i]);
+    }
+    sb.append(")]");
+    
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/JPFLogger.java b/src/main/gov/nasa/jpf/util/JPFLogger.java
new file mode 100644 (file)
index 0000000..64e5d17
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.ResourceBundle;
+import java.util.logging.Filter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * this is a decorator for java.util.logging.JPFLogger
+ *
+ * We use this to avoid explicit Logger.isLoggable() checks in the code.
+ * The goal is to avoid time & memory overhead if logging is not enabled.
+ *
+ * We provide a fat interface to avoid Object[] creation for ellipsis method
+ * or auto boxing for Object arguments
+ *
+ */
+public class JPFLogger extends Logger {
+
+  protected Logger logger;
+
+  public JPFLogger (Logger logger) {
+    super(logger.getName(), logger.getResourceBundleName());
+
+    this.logger = logger;
+  }
+
+  @Override
+  public ResourceBundle getResourceBundle() {
+    return logger.getResourceBundle();
+  }
+
+  @Override
+  public String getResourceBundleName() {
+    return logger.getResourceBundleName();
+  }
+
+  @Override
+  public void setFilter(Filter newFilter) throws SecurityException {
+    logger.setFilter(newFilter);
+  }
+  
+  @Override
+  public Filter getFilter() {
+    return logger.getFilter();
+  }
+   
+  @Override
+  public void log(LogRecord record) {
+    logger.log(record);
+  }
+  
+  @Override
+  public void log(Level level, String msg) {
+    logger.log(level, msg);
+  }
+  
+  @Override
+  public void log(Level level, String msg, Object param1) {
+    logger.log(level, msg, param1);
+  }
+  
+  @Override
+  public void log(Level level, String msg, Object params[]) {
+    logger.log(level, msg, params);
+  }
+  
+  @Override
+  public void log(Level level, String msg, Throwable thrown) {
+    logger.log(level, msg, thrown);
+  }
+  
+  @Override
+  public void logp(Level level, String sourceClass, String sourceMethod, String msg) {
+    logger.logp(level, sourceClass, sourceMethod, msg);
+  }
+  
+  @Override
+  public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object param1) {
+    logger.logp(level, sourceClass, sourceMethod, msg, param1);
+  }
+  
+  @Override
+  public void logp(Level level, String sourceClass, String sourceMethod, String msg, Object params[]) {
+    logger.logp(level, sourceClass, sourceMethod, msg, params);
+  }
+  
+  @Override
+  public void logp(Level level, String sourceClass, String sourceMethod, String msg, Throwable thrown) {
+    logger.logp(level, sourceClass, sourceMethod, msg, thrown);
+  }
+  
+  @Override
+@Deprecated
+  public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg) {
+    logger.logrb(level, sourceClass, sourceMethod, bundleName, msg);
+  }
+  
+  @Override
+@Deprecated
+  public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Object param1) {
+    logger.logrb(level, sourceClass, sourceMethod, bundleName, msg, param1);
+  }
+  
+  @Override
+@Deprecated
+  public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Object params[]) {
+    logger.logrb(level, sourceClass, sourceMethod, bundleName, msg, params);
+  }
+  
+  @Override
+@Deprecated
+  public void logrb(Level level, String sourceClass, String sourceMethod, String bundleName, String msg, Throwable thrown) {
+    logger.logrb(level, sourceClass, sourceMethod, bundleName, msg, thrown);
+  }
+  
+  @Override
+  public void entering(String sourceClass, String sourceMethod) {
+    logger.entering(sourceClass, sourceMethod);
+  }
+
+  @Override
+  public void entering(String sourceClass, String sourceMethod, Object param1) {
+    logger.entering(sourceClass, sourceMethod, param1);
+  }
+  
+  @Override
+  public void entering(String sourceClass, String sourceMethod, Object params[]) {
+    logger.entering(sourceClass, sourceMethod, params);
+  }
+  
+  @Override
+  public void exiting(String sourceClass, String sourceMethod) {
+    logger.exiting(sourceClass, sourceMethod);
+  }
+  
+  @Override
+  public void exiting(String sourceClass, String sourceMethod, Object result) {
+    logger.exiting(sourceClass, sourceMethod, result);
+  }
+  
+  @Override
+  public void throwing(String sourceClass, String sourceMethod, Throwable thrown) {
+    logger.throwing(sourceClass, sourceMethod, thrown);
+  }
+  
+  @Override
+  public void severe(String msg) {
+    logger.severe(msg);
+  }
+  
+  @Override
+  public void warning(String msg) {
+    logger.warning(msg);
+  }
+  
+  @Override
+  public void info(String msg) {
+    logger.info(msg);
+  }
+  
+  @Override
+  public void config(String msg) {
+    logger.config(msg);
+  }
+  
+  @Override
+  public void fine(String msg) {
+    logger.fine(msg);
+  }
+  
+  @Override
+  public void finer(String msg) {
+    logger.finer(msg);
+  }
+  
+  @Override
+  public void finest(String msg) {
+    logger.finest(msg);
+  }
+  
+  @Override
+  public void setLevel(Level newLevel) throws SecurityException {
+    logger.setLevel(newLevel);
+  }
+  
+  @Override
+  public Level getLevel() {
+    return logger.getLevel();
+  }
+  
+  @Override
+  public boolean isLoggable(Level level) {
+    return logger.isLoggable(level);
+  }
+  
+  public boolean isInfoLogged(){
+    return isLoggable(Level.INFO);
+  }
+  
+  public boolean isFineLogged(){
+    return isLoggable(Level.FINE);
+  }  
+
+  public boolean isFinerLogged(){
+    return isLoggable(Level.FINER);
+  }
+  
+  @Override
+  public String getName() {
+    return logger.getName();
+  }
+  
+  @Override
+  public void addHandler(Handler handler) throws SecurityException {
+    logger.addHandler(handler);
+  }
+  
+  @Override
+  public void removeHandler(Handler handler) throws SecurityException {
+    logger.removeHandler(handler);
+  }
+  
+  @Override
+  public Handler[] getHandlers() {
+    return logger.getHandlers();
+  }
+  
+  @Override
+  public void setUseParentHandlers(boolean useParentHandlers) {
+    logger.setUseParentHandlers(useParentHandlers);
+  }
+  
+  @Override
+  public boolean getUseParentHandlers() {
+    return logger.getUseParentHandlers();
+  }
+  
+  @Override
+  public Logger getParent() {
+    return logger.getParent();
+  }
+  
+  @Override
+  public void setParent(Logger parent) {
+    logger.setParent(parent);
+  }
+  
+  private void log (Level level, Object... args) {
+    StringBuilder sb = new StringBuilder(256);
+    int length = args.length;
+    for (int i = 0; i < length; i++) {
+      sb.append(args[i]);
+    }
+    logger.log(level, sb.toString());
+  }
+
+  //--- the SEVERE
+  public void severe (Object s1, Object s2) {
+    if (isLoggable(Level.SEVERE)) {
+      logger.log(Level.SEVERE, s1.toString() + s2.toString());
+    }
+  }
+  // this is here to avoid auto boxing
+  public void severe (Object s1, int s2){
+    if (isLoggable(Level.SEVERE)) {
+         logger.log(Level.SEVERE, s1.toString() + s2);
+    }
+  }
+  public void severe (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.SEVERE)) {
+      logger.log(Level.SEVERE, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void severe (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.SEVERE)) {
+      logger.log(Level.SEVERE, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void severe (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.SEVERE)) {
+      logger.log(Level.SEVERE, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void severe (Object... args){
+    if (isLoggable(Level.SEVERE)) {
+      log(Level.SEVERE, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void fsevere (String format, Object... args){
+    if (isLoggable(Level.SEVERE)) {
+      logger.log(Level.SEVERE, String.format(format, args));
+    }
+  }
+
+  //--- the WARNING
+  public void warning (Object s1, Object s2) {
+    if (isLoggable(Level.WARNING)) {
+      logger.log(Level.WARNING, s1.toString() + s2.toString());
+    }
+  }
+  // this is here to avoid auto boxing
+  public void warning (Object s1, int s2){
+    if (isLoggable(Level.WARNING)) {
+         logger.log(Level.WARNING, s1.toString() + s2);
+    }
+  }
+  public void warning (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.WARNING)) {
+      logger.log(Level.WARNING, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void warning (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.WARNING)) {
+      logger.log(Level.WARNING, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void warning (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.WARNING)) {
+      logger.log(Level.WARNING, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void warning (Object... args){
+    if (isLoggable(Level.WARNING)) {
+      log(Level.WARNING, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void fwarning (String format, Object... args){
+    if (isLoggable(Level.WARNING)) {
+      logger.log(Level.WARNING, String.format(format, args));
+    }
+  }
+
+  //--- the INFO
+  public void info (Object s1, Object s2){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, s1.toString() + s2.toString());
+    }
+  }
+  public void info (Object s1, int s2){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, s1.toString() + s2);
+    }
+  }
+  public void info (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void info (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void info (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void info (Object... args){
+    if (isLoggable(Level.INFO)) {
+      log(Level.INFO, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void finfo (String format, Object... args){
+    if (isLoggable(Level.INFO)) {
+      logger.log(Level.INFO, String.format(format, args));
+    }
+  }
+
+  //--- the CONFIG
+  public void config (Object s1, Object s2){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, s1.toString() + s2.toString());
+    }
+  }
+  public void config (Object s1, int s2){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, s1.toString() + s2);
+    }
+  }
+  public void config (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void config (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void config (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void config (Object... args){
+    if (isLoggable(Level.CONFIG)) {
+      log(Level.CONFIG, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void fconfig (String format, String... args){
+    if (isLoggable(Level.CONFIG)) {
+      logger.log(Level.CONFIG, String.format(format, (Object)args));
+    }
+  }
+
+  //--- the FINE
+  public void fine (Object s1, Object s2){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, s1.toString() + s2.toString());
+    }
+  }
+  public void fine (Object s1, int s2){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, s1.toString() + s2);
+    }
+  }
+  public void fine (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void fine (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void fine (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void fine (Object... args){
+    if (isLoggable(Level.FINE)) {
+      log(Level.FINE, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void ffine (String format, Object... args){
+    if (isLoggable(Level.FINE)) {
+      logger.log(Level.FINE, String.format(format, args));
+    }
+  }
+
+  //--- the FINER
+  public void finer (Object s1, Object s2){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, s1.toString() + s2.toString());
+    }
+  }
+  public void finer (Object s1, int s2){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, s1.toString() + s2);
+    }
+  }
+  public void finer (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void finer (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void finer (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void finer (Object... args){
+    if (isLoggable(Level.FINER)) {
+      log(Level.FINER, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void ffiner (String format, Object... args){
+    if (isLoggable(Level.FINER)) {
+      logger.log(Level.FINER, String.format(format, args));
+    }
+  }
+
+  //--- the FINEST
+  public void finest (Object s1, Object s2){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, s1.toString() + s2.toString());
+    }
+  }
+  public void finest (Object s1, int s2){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, s1.toString() + s2);
+    }
+  }
+  public void finest (Object s1, Object s2, Object s3){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, s1.toString() + s2.toString() + s3.toString());
+    }
+  }
+  public void finest (Object s1, Object s2, Object s3, Object s4){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, s1.toString() + s2.toString() + s3.toString() + s4.toString());
+    }
+  }
+  public void finest (Object s1, int s2, Object s3, int s4){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, s1.toString() + s2 + s3.toString() + s4);
+    }
+  }
+  public void finest (Object... args){
+    if (isLoggable(Level.FINEST)) {
+      log(Level.FINEST, args);
+    }
+  }
+  // note this still wraps args into a String array - overhead
+  public void ffinest (String format, Object... args){
+    if (isLoggable(Level.FINEST)) {
+      logger.log(Level.FINEST, String.format(format, args));
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/JPFSiteUtils.java b/src/main/gov/nasa/jpf/util/JPFSiteUtils.java
new file mode 100644 (file)
index 0000000..77ff8f3
--- /dev/null
@@ -0,0 +1,476 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * utility class for JPF site configuration related functions. This is partially redundant to Config since
+ * it is used during bootstrapping, and gov.nasa.jpf.Config might not be in the classpath yet. It mostly
+ * differs in terms of key/value expansion, which is only partially supported here.
+ * 
+ * NOTE - this class is not allowed to use any JPF specific types
+ */
+public class JPFSiteUtils {
+
+  //--- preparse support - we need this if we use app properties to locat lower level property files
+
+  static Pattern keyValPattern = Pattern.compile("^[ \t]*([^# \t][^ \t]*)[ \t]*=[ \t]*(.+?)[ \t]*$");
+
+  /**
+   * minimal parsing - only local key, system property and and config_path expansion
+   * NOTE this stops after finding the key, and it doesn't add the file to the 'sources'
+   */
+  public static String getMatchFromFile (String pathName, String lookupKey){
+    String value = null;
+    Pattern lookupPattern = Pattern.compile(lookupKey);
+
+    File propFile = new File(pathName);
+    if (!propFile.isFile()){
+      return null;
+    }
+
+    HashMap<String, String> map = new HashMap<String, String>();
+    String dir = propFile.getParent();
+    if (dir == null) {
+      dir = ".";
+    }
+    map.put("config_path", dir);
+
+    try {
+      FileReader fr = new FileReader(propFile);
+      BufferedReader br = new BufferedReader(fr);
+
+      for (String line = br.readLine(); line != null; line = br.readLine()) {
+        Matcher m = keyValPattern.matcher(line);
+        if (m.matches()) {
+          String key = m.group(1);
+          String val = m.group(2);
+
+          val = expandLocal(val, map);
+
+          if ((key.length() > 0) && (val.length() > 0)) {
+            // check for continuation lines
+            if (val.charAt(val.length() - 1) == '\\') {
+              val = val.substring(0, val.length() - 1).trim();
+              for (line = br.readLine(); line != null; line = br.readLine()) {
+                line = line.trim();
+                int len = line.length();
+                if ((len > 0) && (line.charAt(len - 1) == '\\')) {
+                  line = line.substring(0, line.length() - 1).trim();
+                  val += expandLocal(line, map);
+                } else {
+                  val += expandLocal(line, map);
+                  break;
+                }
+              }
+            }
+
+            Matcher lookupMatcher = lookupPattern.matcher(key);
+            if (lookupMatcher.matches()) {
+              value = val;
+              break;
+            } else {
+              if (key.charAt(key.length() - 1) == '+') {
+                key = key.substring(0, key.length() - 1);
+                String v = map.get(key);
+                if (v != null) {
+                  val = v + val;
+                }
+              } else if (key.charAt(0) == '+') {
+                key = key.substring(1);
+                String v = map.get(key);
+                if (v != null) {
+                  val = val + v;
+                }
+              }
+              map.put(key, val);
+            }
+          }
+        }
+      }
+      br.close();
+
+    } catch (FileNotFoundException fnfx) {
+      return null;
+    } catch (IOException iox) {
+      return null;
+    }
+
+    return value;
+  }
+
+  /**
+   * this returns the contents of a config source in-order, without expanding values or keys
+   */
+  public static List<Pair<String,String>> getRawEntries (Reader reader) throws IOException {
+    ArrayList<Pair<String,String>> list = new ArrayList<Pair<String,String>>();
+    BufferedReader br = new BufferedReader(reader);
+    
+    for (String line = br.readLine(); line != null; line = br.readLine()) {
+      Matcher m = keyValPattern.matcher(line);
+      if (m.matches()) {
+        String key = m.group(1);
+        String val = m.group(2);
+        
+        if ((key.length() > 0) && (val.length() > 0)) {
+          // check for continuation lines
+          if (val.charAt(val.length() - 1) == '\\') {
+            val = val.substring(0, val.length() - 1).trim();
+            for (line = br.readLine(); line != null; line = br.readLine()) {
+              line = line.trim();
+              int len = line.length();
+              if ((len > 0) && (line.charAt(len - 1) == '\\')) {
+                line = line.substring(0, line.length() - 1).trim();
+                val += line;
+              } else {
+                val += line;
+                break;
+              }
+            }
+          }
+          
+          list.add( new Pair<String,String>(key,val));
+        }
+      }
+    }
+    
+    return list;
+  }
+  
+  // simple non-recursive, local key and system property expander
+  private static String expandLocal (String s, HashMap<String,String> map) {
+    int i, j = 0;
+    if (s == null || s.length() == 0) {
+      return s;
+    }
+
+    while ((i = s.indexOf("${", j)) >= 0) {
+      if ((j = s.indexOf('}', i)) > 0) {
+        String k = s.substring(i + 2, j);
+        String v = null;
+
+        if (map != null){
+          v = map.get(k);
+        }
+        if (v == null){
+          v = System.getProperty(k);
+        }
+
+        if (v != null) {
+          s = s.substring(0, i) + v + s.substring(j + 1, s.length());
+          j = i + v.length();
+        } else {
+          s = s.substring(0, i) + s.substring(j + 1, s.length());
+          j = i;
+        }
+      }
+    }
+
+    return s;
+  }
+
+  public static File getCoreDir (File siteProps){
+    if (siteProps != null){
+      String path = getMatchFromFile(siteProps.getAbsolutePath(), "jpf-core");
+      if (path != null) {
+        File coreDir = new File(path);
+        if (coreDir.isDirectory()) {
+          return coreDir;
+        }
+      }
+    }
+    return null;
+  }
+  
+  public static File getSiteCoreDir (String[] args){
+    File siteProps = getSiteProperties( args);    
+    return getCoreDir( siteProps);
+  }
+
+  /**
+   * get location of jpf-core from site.properties
+   * @return null if it doesn't exist
+   */
+  public static File getSiteCoreDir() {
+    File siteProps = getStandardSiteProperties();
+    return getCoreDir( siteProps);
+  }
+
+  /**
+   * find project properties (jpf.properties) from current dir
+   */
+  public static File getCurrentProjectProperties() {
+    File d = new File(System.getProperty("user.dir"));
+    do {
+      File f = new File(d, "jpf.properties");
+      if (f.isFile()){
+        return f;
+      }
+      d = d.getParentFile();
+    } while (d != null);
+
+    return null;
+  }
+
+
+  static Pattern idPattern = Pattern.compile("^[ \t]*([^# \t][^ \t]*)[ \t]*=[ \t]*\\$\\{config_path\\}");
+
+  static String projectId;
+
+  /**
+   * look for a "<id> = ${config_path}" entry in current dir/jpf.properties
+   * this looks recursively upwards
+   * @return null if no jpf.properties found
+   */
+  public static String getCurrentProjectId (){
+    if (projectId == null) {
+      File propFile = getCurrentProjectProperties();
+
+      if (propFile != null) {
+        try {
+          FileReader fr = new FileReader(propFile);
+          BufferedReader br = new BufferedReader(fr);
+
+          for (String line = br.readLine(); line != null; line = br.readLine()) {
+            Matcher m = idPattern.matcher(line);
+            if (m.matches()) {
+              projectId = m.group(1);
+            }
+          }
+          br.close();
+
+        } catch (FileNotFoundException fnfx) {
+          return null;
+        } catch (IOException iox) {
+          return null;
+        }
+      }
+    }
+
+    return projectId;
+  }
+  
+  public static boolean isFreeArg (String a){
+    return ((a != null) && (a.length() > 0) && a.charAt(0) != '+' && a.charAt(0) != '-');
+  }
+  
+  public static File getSiteProperties (String[] args){
+    //--- 1. check for a +site=<path> argument up to first free arg
+    for (int i=0; i<args.length; i++){
+      String a = args[i];
+      if (!isFreeArg(a)){
+        if (a.startsWith("+site=")) {
+          String path = a.substring(6).trim();
+          return new File(path);
+        }
+      } else {
+        break;
+      }
+    }
+    
+    //--- 2. check if the first free arg is an application property file (*.jpf), and it contains a 'site=..' setting
+    for (int i=0; i<args.length; i++){
+      String a = args[i];
+      if (isFreeArg(a)){
+        if (a.matches("[^+-].*\\.jpf")) {
+          String path = getMatchFromFile(a, "site");
+          if (path != null) {
+            return new File(path);
+          }
+        }
+        break;
+      }
+    }
+    
+    //--- 3. finally, check upwards from the current dir up to the home dir 
+    return JPFSiteUtils.getStandardSiteProperties();
+  } 
+  
+  /**
+   * locate the site.properties. Start with the current dir, go upwards until the
+   * user.home is reached. If site.properties isn't found there, look for '.jpf' and
+   * 'jpf' dirs within the home dir. If no site.properties is found there either, give up
+   */
+  public static File getStandardSiteProperties(){    
+    String userDir = System.getProperty("user.dir");
+    File dir = new File(userDir);
+    for (; dir != null; dir = dir.getParentFile()) {
+      File f = new File(dir, "site.properties");
+      if (f.isFile()) {
+        return f;
+      }
+    }
+
+    String[] jpfDirCandidates = { ".jpf", "jpf" };
+    String userHome = System.getProperty("user.home");
+    
+    for (String jpfDir : jpfDirCandidates){
+      dir = new File(userHome, jpfDir);
+      if (dir.isDirectory()) {
+        File f = new File(dir, "site.properties");
+        if (f.isFile()) {
+          return f;
+        }
+      }
+    }
+    
+    return null;
+  }
+
+  public static String getGlobalSitePropertiesPath() {
+    String userHome = System.getProperty("user.home");
+    String globalPath = userHome + File.separator + ".jpf"
+         + File.separator + "site.properties";
+    return globalPath;
+  }
+  
+  public static List<Pair<String,String>> getRawEntries (File siteProps){
+    FileReader fr = null;
+    if (siteProps.isFile()) {
+      try {
+        fr = new FileReader(siteProps);
+        List<Pair<String,String>> entries = getRawEntries(fr);
+        fr.close();
+        
+        return entries;
+
+      } catch (IOException iox) {
+      } finally {
+        try { fr.close(); } catch (IOException _ignore){}
+      }
+    }    
+    
+    return new ArrayList<Pair<String,String>>();
+  }
+    
+  /**
+   * this returns a list of all the project ids in the 'extensions' entries (also
+   * handles accumulated 'extensions+=.." entries
+   */
+  public static List<String> getExtensions (List<Pair<String,String>> entries){
+    ArrayList<String> list = new ArrayList<String>();
+    
+    for (Pair<String,String> p : entries){
+      if (p._1.startsWith("extensions")){
+        for (String pid : p._2.split("[,;]")){
+          pid = pid.trim();
+          if (pid.charAt(0) == '$'){
+            pid = pid.substring(2, pid.length()-1);
+            list.add( pid);
+          }
+        }
+      }
+    }
+    
+    return list;
+  }
+  
+  
+  public static boolean addProject (File siteProps, String projectId, File projectDir, boolean isExt){
+    List<Pair<String,String>> entries = getRawEntries(siteProps);
+    List<String> extensions = getExtensions(entries);
+
+    if ("jpf-core".equals(projectId)){ // jpf-core always has to be in the extensions list
+      isExt = true;
+    }
+    
+    try {
+      FileUtils.ensureDirs(siteProps);
+      String projectPath = FileUtils.asCanonicalUserPathName(projectDir.getAbsolutePath());
+
+      PrintWriter pw = new PrintWriter(siteProps);
+
+      pw.println("# auto-generated JPF site properties");
+      pw.println();
+
+      boolean alreadyThere = false;
+      for (Pair<String, String> e : entries) {
+        if (!"extensions".equals(e._1)) {
+
+          pw.print(e._1);
+          pw.print(" = ");
+
+          if (projectId.equals(e._1)) {
+            alreadyThere = true;
+            // Hmm, not sure its best to use absolute pathnames here (e.g. when doing local site installs)
+            pw.println(projectPath);
+            
+            // check if we have to update extensions
+            if (extensions.contains(projectId)){
+              if (!isExt){
+                extensions.remove(projectId);
+              }
+            } else {
+              extensions.add(projectId);
+            }
+            
+          } else {
+            pw.println(e._2);
+          }
+        }
+      }
+
+      if (!alreadyThere) {
+        if (isExt) {
+          extensions.add(projectId);
+        }
+
+        pw.print(projectId);
+        pw.print(" = ");
+        pw.println(projectPath);
+      }
+
+      pw.println();
+      pw.print("extensions = ");
+
+      boolean isFirst = true;
+      for (String e : extensions) {
+        if (isFirst) {
+          isFirst = false;
+        } else {
+          pw.print(',');          
+        }
+        pw.print("${");
+        pw.print(e);
+        pw.print('}');
+      }
+      
+      pw.println();
+      pw.close();
+
+    } catch (IOException iox) {
+      iox.printStackTrace();
+      return false;
+    }
+    
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Left.java b/src/main/gov/nasa/jpf/util/Left.java
new file mode 100644 (file)
index 0000000..fb64dcf
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+
+/**
+ * left justified output
+ * <2do> this is not worth a class! use a general TextFormatter
+ */
+public class Left {
+  public static String format (String value, int spaces) {
+    int vlen = value.length();
+    int newLen = Math.max(spaces, vlen);
+    char[] result = new char[newLen];
+    value.getChars(0, vlen, result, 0);
+    Arrays.fill(result, vlen, newLen, ' ');
+    return new String(result);
+  }
+
+  public static String format (int value, int digits) {
+    return format(Integer.toString(value),digits);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/LimitedInputStream.java b/src/main/gov/nasa/jpf/util/LimitedInputStream.java
new file mode 100644 (file)
index 0000000..ff9fc44
--- /dev/null
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class LimitedInputStream extends InputStream {
+
+  private final InputStream m_source;
+  private int m_limit;
+
+  public LimitedInputStream(InputStream source) {
+    if (source == null) {
+      throw new NullPointerException("source == null");
+    }
+
+    m_source = source;
+  }
+
+  public int getLimit() {
+    return (m_limit);
+  }
+
+  public void setLimit(int length) {
+    if (length < 0) {
+      throw new IllegalArgumentException("length < 0 : " + length);
+    }
+
+    m_limit = length;
+  }
+
+  @Override
+  public int read() throws IOException {
+    int result;
+
+    if (m_limit <= 0) {
+      return (-1);
+    }
+
+    result = m_source.read();
+
+    if (result >= 0) {
+      m_limit--;
+    }
+
+    return (result);
+  }
+
+  @Override
+  public int read(byte buffer[], int offset, int length) throws IOException {
+    if (buffer == null) {
+      throw new NullPointerException("buffer == null");
+    }
+
+    if (offset < 0) {
+      throw new IndexOutOfBoundsException("offset < 0 : " + offset);
+    }
+
+    if (length < 0) {
+      throw new IndexOutOfBoundsException("length < 0 : " + length);
+    }
+
+    if (offset + length > buffer.length) {
+      throw new IndexOutOfBoundsException("offset + length > buffer.length : " + offset + " + " + length + " > " + buffer.length);
+    }
+
+    if (length == 0) {
+      return (0);
+    }
+
+    length = Math.min(m_limit, length);
+
+    if (length == 0) {
+      return (-1);
+    }
+
+    length = m_source.read(buffer, offset, length);
+
+    if (length > 0) {
+      m_limit -= length;
+    }
+
+    return (length);
+  }
+
+  @Override
+  public long skip(long n) throws IOException {
+    n = Math.min(n, m_limit);
+
+    if (n <= 0) {
+      return (0);
+    }
+
+    n = m_source.skip(n);
+
+    if (n > 0) {
+      m_limit -= n;
+    }
+
+    return (n);
+  }
+
+  @Override
+  public int available() throws IOException {
+    int result;
+
+    if (m_limit <= 0) {
+      return (0);
+    }
+
+    result = m_source.available();
+    result = Math.min(result, m_limit);
+
+    return (result);
+  }
+
+  @Override
+  public void close() throws IOException {
+    m_limit = 0;
+
+    m_source.close();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/LinkedObjectQueue.java b/src/main/gov/nasa/jpf/util/LinkedObjectQueue.java
new file mode 100644 (file)
index 0000000..3b05322
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * object queue that uses cached link entries
+ */
+public class LinkedObjectQueue<E> implements ObjectQueue<E> {
+
+  static class Entry {
+    Entry next; // single linked list
+    Object obj;  // referenced object
+  }
+
+  protected Entry last;
+  protected Entry first;
+
+  protected int size;
+  
+  protected int maxCache;
+  protected int nFree;
+  protected Entry free;
+
+  class FIFOIterator implements Iterator<E> {
+    Entry e = first;
+
+    @Override
+       public boolean hasNext() {
+      return e != null;
+    }
+
+    @Override
+       public E next() {
+      if (e == null){
+        throw new NoSuchElementException();
+      } else {
+        E obj = (E)e.obj;
+        e = e.next;
+        return obj;
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("arbitrary remove from queue not supported");
+    }
+  }
+  
+  public LinkedObjectQueue (){
+    maxCache = 256;
+  }
+  
+  public LinkedObjectQueue (int maxCache){
+    this.maxCache = maxCache;
+  }
+  
+  @Override
+  public int size() {
+    return size;
+  }
+  
+  @Override
+  public boolean add(E obj) {
+    Entry e;
+
+    if (nFree > 0){ // reuse a cached Entry object
+      e = free;
+      free = e.next;
+      nFree--;
+
+    } else {
+      e = new Entry();
+    }
+
+    e.obj = obj;
+    e.next = null;
+
+    if (last != null) {
+      last.next = e;
+    } else {
+      first = e;
+    }
+
+    last = e;
+    
+    size++;
+    return true;
+  }
+  
+  @Override
+  public boolean offer( E obj){
+    return add(obj);
+  }
+
+  @Override
+  public boolean isEmpty(){
+    return size > 0;
+  }
+  
+  @Override
+  public E peek (){
+    if (size == 0){
+      return null;
+    } else {
+      return (E)first.obj;
+    }
+  }
+  
+  @Override
+  public E poll(){
+    if (size == 0){
+      return null;
+      
+    } else {
+      Entry e = first;
+      first = first.next;
+      size--;
+      
+      E obj = (E)e.obj;
+      
+      if (nFree < maxCache){
+        Entry next = e.next;
+        e.next = (nFree++ > 0) ? free : null;
+        e.obj = null;
+        free = e;
+      }
+      
+      return obj;
+    }
+  }
+  
+  @Override
+  public E remove() throws NoSuchElementException {
+    if (size == 0){
+      throw new NoSuchElementException();
+    } else {
+      return poll();
+    }
+  }
+  
+  @Override
+  public Iterator<E> iterator(){
+    return new FIFOIterator();
+  }
+  
+  @Override
+  public void process( Processor<E> proc) {
+    for (Entry e = first; e != null; ) {
+      proc.process( (E)e.obj);
+
+      e.obj = null; // avoid memory leaks
+
+      if (nFree < maxCache){
+        // recycle to save some allocation and a lot of shortliving garbage
+        Entry next = e.next;
+        e.next = (nFree++ > 0) ? free : null;
+        free = e;
+        e = next;
+
+      } else {
+        e = e.next;
+      }
+    }
+    clear();
+  }
+
+  @Override
+  public void clear () {
+    first = null;
+    last = null;
+    size = 0;
+
+    // don't reset nFree and free since we limit the memory size of our cache
+    // and the Entry object do not reference anything
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/LocationSpec.java b/src/main/gov/nasa/jpf/util/LocationSpec.java
new file mode 100644 (file)
index 0000000..877cb7d
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.File;
+
+/**
+ * support for specification of source locations
+ *
+ * This maps sourcefile:line1-range strings into instructions that can be efficiently
+ * checked for by listeners. Example formats are:
+ *
+ *  X.java                (the whole of file X.java)
+ *  MyClass.java:42       (file MyClass.java, line1 42)
+ *  FooBar.java:42-48     (file FooBar.java, lines 42 to 48)
+ *  FooBar.java:42+3      (range of lines from 42 to 45)
+ *  x/y/z/whatever:42+    (file with pathname x/y/z/whatever, lines 42 to end)
+ * 
+ *
+ * NOTE path names are given in Unix notation, to avoid the usual Java string
+ * quoting problem with backslashes
+ */
+public class LocationSpec {
+
+  public static final int ANYLINE = -1;
+
+  protected StringMatcher pathSpec;
+  protected StringMatcher fileSpec;
+  protected int fromLine = ANYLINE;   // inclusive
+  protected int toLine = ANYLINE;     // inclusive
+
+  /**
+   * factory method that includes the parser
+   */
+  public static LocationSpec createLocationSpec (String s){
+    s = s.replace('\\', '/');
+
+    String pspec = null, fspec;
+    int line1 = -1, line2 = -1;
+
+    int idx = s.lastIndexOf(':');
+    // check if it is just a dreaded "drive letter"
+    if (idx == s.length()-1 || s.charAt(idx + 1) == '/') {
+      idx = -1;
+    }
+
+    if (idx < 0){ // no line1 spec
+      fspec = s.trim();
+
+    } else if (idx > 0){ // line1 spec present
+      fspec = s.substring(0, idx).trim();
+
+      // now get the line1 (range)
+      s = s.substring(idx+1).trim();
+      int len = s.length();
+
+      if (len > 0){
+        int i = 0;
+        for (; i < len; i++) {
+          char c = s.charAt(i);
+          if (c == '-' || c == '+') {
+            line1 = Integer.parseInt(s.substring(0, i));
+
+            if (i == len - 1) { // open interval
+              line2 = Integer.MAX_VALUE;
+            } else {
+              line2 = Integer.parseInt(s.substring(i + 1));
+              if (c == '+') {
+                line2 = line1 + line2;
+              }
+            }
+            break;
+          }
+        }
+
+        if (i == len) { // single line
+          line1 = Integer.parseInt(s);
+        }
+      }
+
+    } else {
+      throw new RuntimeException("no filename in LocationSpec: " + s);
+    }
+
+    idx = fspec.lastIndexOf('/');
+    if (idx > 0){
+      pspec = fspec.substring(0, idx);
+      fspec = fspec.substring(idx+1);
+    } else if (idx == 0){
+      pspec = "/";
+      fspec = fspec.substring(1);
+    } else {
+      pspec = null;
+    }
+
+    return new LocationSpec(pspec, fspec, line1, line2);
+  }
+
+  public LocationSpec (String pspec, String fspec, int line1, int line2){
+    if (pspec != null){
+      pathSpec = new StringMatcher(pspec);
+    }
+    fileSpec = new StringMatcher(fspec);
+
+    fromLine = line1;
+    toLine = line2;
+  }
+
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+
+    if (pathSpec != null){
+      sb.append(pathSpec);
+      sb.append('/');
+    }
+
+    sb.append(fileSpec);
+
+    if (fromLine != ANYLINE){
+      sb.append(':');
+      sb.append(fromLine);
+    }
+    if (toLine != ANYLINE){
+      sb.append('-');
+      if (toLine != Integer.MAX_VALUE){
+        sb.append(toLine);
+      }
+    }
+
+    return sb.toString();
+  }
+
+  public boolean matchesFile (File f){
+    if (fileSpec.matches(f.getName())){
+      if (pathSpec != null){
+        String pspec = f.getParent();
+        pspec = pspec.replace('\\', '/'); // use Unix '/' to avoid quoting issue
+
+        return pathSpec.matches(pspec);
+
+      } else { // no path
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public boolean isAnyLine(){
+    return fromLine == ANYLINE;
+  }
+
+  public boolean isLineInterval(){
+    return toLine != ANYLINE;
+  }
+
+  public int getLine(){ // for specs that are single locations
+    return fromLine;
+  }
+
+  public int getFromLine() {
+    return fromLine;
+  }
+
+  // note - this is < 0 if there was only a
+  public int getToLine() {
+    if (toLine < 0){
+      if (fromLine >= 0){
+        return fromLine;
+      }
+    }
+
+    return toLine;
+  }
+
+  /**
+   * 'pathName' has to use '/' as separators
+   */
+  public boolean matchesFile (String pathName){
+    if (pathName != null){
+      pathName = pathName.replace('\\', '/');
+      int idx = pathName.lastIndexOf('/');
+
+      if (idx >= 0) {
+        String fname = pathName.substring(idx + 1);
+
+        if (fileSpec.matches(fname)) {
+          if (pathSpec != null) {
+            String pname = idx > 0 ? pathName.substring(0, idx) : "/";
+            return pathSpec.matches(pname);
+          } else {
+            return true;
+          }
+        }
+
+      } else { // no path
+        return fileSpec.matches(pathName);
+      }
+    }
+
+    return false;
+  }
+  
+  public boolean includesLine (int line){
+    if (fromLine == ANYLINE){
+      return true;
+
+    } else {
+      if (fromLine == line){
+        return true;
+      } else if (fromLine < line){
+        if (toLine == ANYLINE){ // single location
+          return false;
+        } else {
+          return (line <= toLine);
+        }
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/LogHandler.java b/src/main/gov/nasa/jpf/util/LogHandler.java
new file mode 100644 (file)
index 0000000..51c5808
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.Config;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintWriter;
+import java.net.ConnectException;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
+import java.util.logging.LogRecord;
+import java.util.logging.Logger;
+
+/**
+ * log handler class that deals with output selection and formatting. This is the
+ * only handler we use for our own logging. We do our own little formatting
+ * on the fly
+ */
+public class LogHandler extends Handler {
+
+  static class DefaultFormatter extends Formatter {
+    boolean format;
+    boolean showName;
+    boolean showLevel;
+    //..and potentially more
+    
+    DefaultFormatter (Config conf) {
+      showName = conf.getBoolean("log.show_name", false);
+      showLevel = conf.getBoolean("log.show_level", true);
+      format = showName || showLevel;
+    }
+
+    DefaultFormatter (boolean showName, boolean showLevel){
+      this.showName = showName;
+      this.showLevel = showLevel;
+      format = showName || showLevel;
+    }
+
+    // we might want to parameterize this
+    @Override
+       public String format (LogRecord r) {
+      if (format) {
+        StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        if (showLevel) {          
+          sb.append(r.getLevel().getName());
+        }
+        if (showName) {
+          if (showLevel) {
+            sb.append(":");
+          }
+          sb.append(r.getLoggerName());
+        }
+        sb.append("] ");
+        
+        String msg = r.getMessage();
+        Object[] params = r.getParameters();
+        
+        if (params == null){
+          sb.append(msg);
+        } else {
+          sb.append(String.format(msg,params));
+        }
+        
+        return sb.toString();
+        
+      } else { // raw
+        return r.getMessage();
+      }
+    }
+  }
+  
+  public static String LOG_HOST = "localhost";
+  public static int LOG_PORT = 20000;
+  
+  File file;
+  Socket socket;
+  OutputStream ostream;
+  
+  PrintWriter out;
+  
+  public LogHandler (Config conf) {
+    LOG_HOST = conf.getString("log.host", LOG_HOST);
+    LOG_PORT = conf.getInt("log.port", LOG_PORT);
+    
+    String output = conf.getString("log.output", "out");
+    
+    if (output.matches("[a-zA-Z0-9.]*:[0-9]*")) { // we assume that's a hostname:port spec
+      int idx = output.indexOf(':');
+      String host = output.substring(0, idx);
+      String port = output.substring(idx+1, output.length());
+      ostream = connectSocket( host, port);
+    } else if (output.equalsIgnoreCase("socket")){
+      ostream = connectSocket( LOG_HOST, Integer.toString(LOG_PORT));
+    } else if (output.equalsIgnoreCase("out") || output.equals("System.out")) {
+      ostream = System.out;
+    } else if (output.equalsIgnoreCase("err") || output.equals("System.err")) {
+      ostream = System.err;
+    } else {
+      ostream = openFile(output);
+    }
+    
+    if (ostream == null) {
+      ostream = System.out;
+    }
+    
+    setFormatter(new DefaultFormatter(conf));
+    setOutput(ostream);
+  }
+
+  protected LogHandler() {
+    // for derived classes
+  }
+
+  OutputStream connectSocket (String host, String portSpec) {
+    int port = -1;
+    
+    if ((host == null) || (host.length() == 0)) {
+      host = LOG_HOST;
+    }
+    
+    if (portSpec != null) {
+      try {
+        port = Integer.parseInt(portSpec);
+      } catch (NumberFormatException x) {
+        // just catch it
+      }
+    }
+    if (port == -1) {
+      port = LOG_PORT;
+    }
+    
+    
+    try {
+      socket = new Socket(host, port);
+      return socket.getOutputStream();
+    } catch (UnknownHostException uhx) {
+      //System.err.println("unknown log host: " + host);
+    } catch (ConnectException cex) {
+      //System.err.println("no log host detected);
+    } catch (IOException iox) {
+      //System.err.println(iox);
+    }
+
+    return null;
+  }
+  
+  OutputStream openFile (String fileName) {
+    file = new File(fileName);
+    
+    try {
+      if (file.exists()) {
+        file.delete();
+      }
+      file.createNewFile();
+      return new FileOutputStream(file);
+    } catch (IOException iox) {
+      // just catch it
+    }
+    
+    return null;
+  }
+  
+  public void setOutput (OutputStream ostream) {
+    out = new PrintWriter(ostream, true);
+  }
+  
+  @Override
+  public void close () throws SecurityException {
+    if ((ostream != System.err) && (ostream != System.out)) {
+      out.close();
+    }
+    
+    if (socket != null) {
+      try {
+        socket.close();
+      } catch (IOException iox) {
+        // not much we can do
+      }
+    }
+  }
+
+  @Override
+  public void flush () {
+    out.flush();
+  }
+
+  @Override
+  public void publish (LogRecord r) {
+    String msg = getFormatter().format(r);
+    out.println(msg);
+  }
+
+  public void printStatus (Logger log) {   
+    if (socket != null) {
+      log.config("logging to socket: " + socket);
+    } else if (file != null) {
+      log.config("logging to file: " + file.getAbsolutePath());
+    } else if (ostream == System.err) {
+      log.config("logging to System.err");
+    } else if (ostream == System.out) {
+      log.config("logging to System.out");
+    } else {
+      log.warning("unknown log destination");
+    }
+  }
+
+
+  // a dfault handler that doesn't need Config
+  public static class DefaultConsoleHandler extends LogHandler {
+    public DefaultConsoleHandler() {
+      ostream = System.out;
+
+      setFormatter(new DefaultFormatter(false,true));
+      setOutput(ostream);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/LogManager.java b/src/main/gov/nasa/jpf/util/LogManager.java
new file mode 100644 (file)
index 0000000..74b33d4
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.Config;
+
+import java.io.OutputStream;
+import java.util.HashMap;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * this class is responsible for returning properly JPF-configured
+ * Loggers. It is not supposed to be used directly by clients, but rather
+ * is a JPF delegatee.
+ * 
+ * While we could modify/replace the standard java.util.logging facility
+ * at various levels (own LogManager, own initialization class etc.), we choose
+ * the approach to piggyback on it, because these mechanisms either require
+ * changing system properties, rely on only partly documented features, or
+ * don't give us the full functionality we need. By having our own log
+ * encapsulator, we could also replace the underlying mechanism if we really
+ * want to
+ */
+public class LogManager {
+    
+  static HashMap<String,JPFLogger> loggers = new HashMap<String, JPFLogger>(); // our own set
+  
+  static Level defaultLevel = Level.WARNING;
+  static Handler handler;  // we have only one
+  
+  // I don't like these categories too much, but we want to act as a stand in
+  static String[] activeSevere;
+  static String[] activeWarning;
+  static String[] activeInfo;
+  static String[] activeConfig;
+  static String[] activeFine;
+  static String[] activeFiner;
+  static String[] activeFinest;
+  
+  /**
+   * note - this is not allowed to fail, since we couldn't log that. Hardcoded default
+   * values have to do in this case (make sure we catch the proper Config exceptions)
+   */
+  public static void init (Config conf) {
+    try {
+      defaultLevel = Level.parse( conf.getString("log.level", "INFO").toUpperCase());
+    } catch (Throwable x) {
+      defaultLevel = Level.WARNING;
+    }
+    
+    activeSevere = conf.getStringArray("log.severe");
+    activeWarning = conf.getStringArray("log.warning");
+    activeInfo = conf.getStringArray("log.info");
+    activeConfig = conf.getStringArray("log.config");
+    activeFine = conf.getStringArray("log.fine");
+    activeFiner = conf.getStringArray("log.finer");
+    activeFinest = conf.getStringArray("log.finest");
+    
+    handler = conf.getInstance("log.handler.class", Handler.class);
+  }
+  
+  static boolean checkInclusion (String[] actives, String name) {
+    if (actives == null) {
+      return false;
+    }
+    
+    for (int i=0; i<actives.length; i++) {
+      if (name.matches(actives[i])) {
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  static Level getLevel (String name) {
+    if (checkInclusion(activeSevere, name)) return Level.SEVERE;
+    if (checkInclusion(activeWarning, name)) return Level.WARNING;
+    if (checkInclusion(activeInfo, name)) return Level.INFO;
+    if (checkInclusion(activeConfig, name)) return Level.CONFIG;
+    if (checkInclusion(activeFine, name)) return Level.FINE;
+    if (checkInclusion(activeFiner, name)) return Level.FINER;
+    if (checkInclusion(activeFinest, name)) return Level.FINEST;
+    
+    return defaultLevel;
+  }
+  
+  public static JPFLogger getLogger (String name) {
+    if (handler == null){
+      // <2do> this is only a band aid for missing LogManager.init(conf) calls
+      handler = new LogHandler.DefaultConsoleHandler();
+    }
+
+    // how often can you say 'Logger' in one method..
+    JPFLogger logger = loggers.get(name);
+    
+    if (logger == null) {
+      // we haven't had this one yet - create and init a new one from the host logging system
+      Logger baseLogger = Logger.getLogger(name);
+      baseLogger.setLevel( getLevel(name));
+      baseLogger.addHandler(handler);
+      baseLogger.setUseParentHandlers(false); // we don't want to pass this up
+      
+      // wrap it
+      logger = new JPFLogger(baseLogger);
+      loggers.put(name, logger);
+    }
+    
+    return logger;
+  }
+  
+  public static void setOutput(OutputStream out) {
+    // need to have init() called first
+    if (handler instanceof LogHandler){
+      ((LogHandler)handler).setOutput(out);
+    }
+  }
+  
+  public static void printStatus (Logger log) {
+    if (handler instanceof LogHandler){
+      ((LogHandler)handler).printStatus(log);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Loggable.java b/src/main/gov/nasa/jpf/util/Loggable.java
new file mode 100644 (file)
index 0000000..1e4eeda
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.logging.Level;
+
+/**
+ * convenience interface that mixes in JPFLogger interface methods
+ */
+public interface Loggable {
+
+  // the primitive method used by the defaults
+  JPFLogger getLogger();
+
+  default void setLogLevel (Level newLevel){
+    getLogger().setLevel(newLevel);
+  }
+
+  default void severe (String msg){
+    getLogger().severe(msg);
+  }
+  default void severe (String a1, String a2){
+    getLogger().severe(a1, a2);
+  }
+  default void severe (String a1, String a2, String a3){
+    getLogger().severe(a1, a2, a3);
+  }
+  default void severe (String a1, String a2, String a3, String a4){
+    getLogger().severe(a1, a2, a3, a4);
+  }
+  default void severe (String a1, String a2, String a3, String a4, String a5){
+    getLogger().severe(a1, a2, a3, a4, a5);
+  }
+  default void severe (String... a) {
+    getLogger().severe((Object[])a);
+  }
+
+  default void severe (Object msg){
+    getLogger().severe(msg);
+  }
+  default void severe (Object a1, Object a2){
+    getLogger().severe(a1, a2);
+  }
+  default void severe (Object a1, Object a2, Object a3){
+    getLogger().severe(a1, a2, a3);
+  }
+  default void severe (Object a1, Object a2, Object a3, Object a4){
+    getLogger().severe(a1, a2, a3, a4);
+  }
+  default void severe (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().severe(a1, a2, a3, a4, a5);
+  }
+  default void severe (Object... a) {
+    getLogger().severe((Object[])a);
+  }
+  default void fsevere (String format, Object... a) {
+    getLogger().fsevere(format, a);
+  }
+
+
+  default void warning (String msg){
+    getLogger().warning(msg);
+  }
+  default void warning (String a1, String a2){
+    getLogger().warning(a1, a2);
+  }
+  default void warning (String a1, String a2, String a3){
+    getLogger().warning(a1, a2, a3);
+  }
+  default void warning (String a1, String a2, String a3, String a4){
+    getLogger().warning(a1, a2, a3, a4);
+  }
+  default void warning (String a1, String a2, String a3, String a4, String a5){
+    getLogger().warning(a1, a2, a3, a4, a5);
+  }
+  default void warning (String... a) {
+    getLogger().warning((Object[]) a);
+  }
+
+  default void warning (Object msg){
+    getLogger().warning(msg);
+  }
+  default void warning (Object a1, Object a2){
+    getLogger().warning(a1, a2);
+  }
+  default void warning (Object a1, Object a2, Object a3){
+    getLogger().warning(a1, a2, a3);
+  }
+  default void warning (Object a1, Object a2, Object a3, Object a4){
+    getLogger().warning(a1, a2, a3, a4);
+  }
+  default void warning (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().warning(a1, a2, a3, a4, a5);
+  }
+  default void warning (Object... a) {
+    getLogger().warning((Object[]) a);
+  }
+  default void fwarning (String format, Object... a) {
+    getLogger().fwarning(format, a);
+  }
+
+
+  default void info (String msg){
+    getLogger().info(msg);
+  }
+  default void info (String a1, String a2){
+    getLogger().info(a1, a2);
+  }
+  default void info (String a1, String a2, String a3){
+    getLogger().info(a1, a2, a3);
+  }
+  default void info (String a1, String a2, String a3, String a4){
+    getLogger().info(a1, a2, a3, a4);
+  }
+  default void info (String a1, String a2, String a3, String a4, String a5){
+    getLogger().info(a1, a2, a3, a4, a5);
+  }
+  default void info (String... a) {
+    getLogger().info((Object[]) a);
+  }
+
+  default void info (Object msg){
+    getLogger().info(msg);
+  }
+  default void info (Object a1, Object a2){
+    getLogger().info(a1, a2);
+  }
+  default void info (Object a1, Object a2, Object a3){
+    getLogger().info(a1, a2, a3);
+  }
+  default void info (Object a1, Object a2, Object a3, Object a4){
+    getLogger().info(a1, a2, a3, a4);
+  }
+  default void info (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().info(a1, a2, a3, a4, a5);
+  }
+  default void info (Object... a) {
+    getLogger().info((Object[]) a);
+  }
+  default void finfo (String format, Object... a) {
+    getLogger().finfo(format, a);
+  }
+
+
+  default void fine (String msg){
+    getLogger().fine(msg);
+  }
+  default void fine (String a1, String a2){
+    getLogger().fine(a1, a2);
+  }
+  default void fine (String a1, String a2, String a3){
+    getLogger().fine(a1, a2, a3);
+  }
+  default void fine (String a1, String a2, String a3, String a4){
+    getLogger().fine(a1, a2, a3, a4);
+  }
+  default void fine (String a1, String a2, String a3, String a4, String a5){
+    getLogger().fine(a1, a2, a3, a4, a5);
+  }
+  default void fine (String... a) {
+    getLogger().fine((Object[]) a);
+  }
+
+  default void fine (Object msg){
+    getLogger().fine(msg);
+  }
+  default void fine (Object a1, Object a2){
+    getLogger().fine(a1, a2);
+  }
+  default void fine (Object a1, Object a2, Object a3){
+    getLogger().fine(a1, a2, a3);
+  }
+  default void fine (Object a1, Object a2, Object a3, Object a4){
+    getLogger().fine(a1, a2, a3, a4);
+  }
+  default void fine (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().fine(a1, a2, a3, a4, a5);
+  }
+  default void fine (Object... a) {
+    getLogger().fine((Object[]) a);
+  }
+  default void ffine (String format, Object... a) {
+    getLogger().ffine(format, a);
+  }
+
+
+  default void finer (String msg){
+    getLogger().finer(msg);
+  }
+  default void finer (String a1, String a2){
+    getLogger().finer(a1, a2);
+  }
+  default void finer (String a1, String a2, String a3){
+    getLogger().finer(a1, a2, a3);
+  }
+  default void finer (String a1, String a2, String a3, String a4){
+    getLogger().finer(a1, a2, a3, a4);
+  }
+  default void finer (String a1, String a2, String a3, String a4, String a5){
+    getLogger().finer(a1, a2, a3, a4, a5);
+  }
+  default void finer (String... a) {
+    getLogger().finer((Object[]) a);
+  }
+
+  default void finer (Object msg){
+    getLogger().finer(msg);
+  }
+  default void finer (Object a1, Object a2){
+    getLogger().finer(a1, a2);
+  }
+  default void finer (Object a1, Object a2, Object a3){
+    getLogger().finer(a1, a2, a3);
+  }
+  default void finer (Object a1, Object a2, Object a3, Object a4){
+    getLogger().finer(a1, a2, a3, a4);
+  }
+  default void finer (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().finer(a1, a2, a3, a4, a5);
+  }
+  default void finer (Object... a) {
+    getLogger().finer((Object[]) a);
+  }
+  default void ffiner (String format, Object... a) {
+    getLogger().ffiner(format, a);
+  }
+
+
+  default void finest (String msg){
+    getLogger().finest(msg);
+  }
+  default void finest (String a1, String a2){
+    getLogger().finest(a1, a2);
+  }
+  default void finest (String a1, String a2, String a3){
+    getLogger().finest(a1, a2, a3);
+  }
+  default void finest (String a1, String a2, String a3, String a4){
+    getLogger().finest(a1, a2, a3, a4);
+  }
+  default void finest (String a1, String a2, String a3, String a4, String a5){
+    getLogger().finest(a1, a2, a3, a4, a5);
+  }
+  default void finest (String... a) {
+    getLogger().finest((Object[]) a);
+  }
+
+  default void finest (Object msg){
+    getLogger().finest(msg);
+  }
+  default void finest (Object a1, Object a2){
+    getLogger().finest(a1, a2);
+  }
+  default void finest (Object a1, Object a2, Object a3){
+    getLogger().finest(a1, a2, a3);
+  }
+  default void finest (Object a1, Object a2, Object a3, Object a4){
+    getLogger().finest(a1, a2, a3, a4);
+  }
+  default void finest (Object a1, Object a2, Object a3, Object a4, Object a5){
+    getLogger().finest(a1, a2, a3, a4, a5);
+  }
+  default void finest (Object... a) {
+    getLogger().finest((Object[]) a);
+  }
+  default void ffinest (String format, Object... a) {
+    getLogger().ffinest(format, a);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/LongVector.java b/src/main/gov/nasa/jpf/util/LongVector.java
new file mode 100644 (file)
index 0000000..f3b7c15
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * (more efficient?) alternative to Vector<Integer>
+ * @author pcd
+ */
+public final class LongVector {
+  public static final int defaultInitCap = 40;
+
+  
+  /** <i>size</i> as in a java.util.Vector. */
+  protected int size;
+  
+  /** the backing array. */
+  protected long[] data;
+  
+  /** growth strategy. */
+  protected Growth growth;
+  
+  
+  public LongVector(Growth initGrowth, int initCap) {
+    growth = initGrowth;
+    data = new long[initCap];
+    size = 0;
+  }
+  
+  public LongVector(Growth initGrowth) { this(initGrowth,defaultInitCap); }
+  
+  public LongVector(int initCap) { this(Growth.defaultGrowth, initCap); }
+  
+  public LongVector() { this(Growth.defaultGrowth,defaultInitCap); }
+  
+  
+  public void add(long x) {
+    if (size+1 > data.length) {
+      ensureCapacity(size+1);
+    }
+    data[size] = x;
+    size++;
+  }
+  
+  public long get(int idx) {
+    if (idx >= size) {
+      return 0;
+    } else {
+      return data[idx];
+    }
+  }
+  
+  public void set(int idx, long x) {
+    ensureSize(idx + 1);
+    data[idx] = x;
+  }
+  
+
+  public void squeeze() {
+    while (size > 0 && data[size - 1] == 0) size--;
+  }
+  
+  public void setSize(int sz) {
+    if (sz > size) {
+      ensureCapacity(sz);
+      size = sz;
+    } else {
+      while (size > sz) {
+        size--;
+        data[size] = 0;
+      }
+    }
+  }
+
+  public void clear() { setSize(0); }
+  
+  public int size() { return size; }
+  
+  public void ensureSize(int sz) {
+    if (size < sz) {
+      ensureCapacity(sz);
+      size = sz;
+    }
+  }
+  
+  public void ensureCapacity(int desiredCap) {
+    if (data.length < desiredCap) {
+      long[] newData = new long[growth.grow(data.length, desiredCap)];
+      System.arraycopy(data, 0, newData, 0, size);
+      data = newData;
+    }
+  }
+  
+  public static void copy(LongVector src, int srcPos, LongVector dst, int dstPos, int len) {
+    if (len == 0) return;
+    src.ensureCapacity(srcPos + len);
+    dst.ensureSize(dstPos+len);
+    System.arraycopy(src.data, srcPos, dst.data, dstPos, len);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/MethodInfoRegistry.java b/src/main/gov/nasa/jpf/util/MethodInfoRegistry.java
new file mode 100644 (file)
index 0000000..7c61484
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+
+/**
+ * just a little helper for java.lang.reflect peers
+ * 
+ * <2do> - it's overly simplistic for now
+ */
+public class MethodInfoRegistry {
+  
+  final int NREG = 10;
+  MethodInfo[] registered;
+  int nRegistered;
+  
+  public MethodInfoRegistry () {
+    registered = new MethodInfo[NREG];
+    nRegistered = 0;
+  }
+    
+  public int registerMethodInfo (MethodInfo mi) {
+    int idx;
+    
+    for (idx=0; idx < nRegistered; idx++) {
+      if (registered[idx] == mi) {
+        return idx;
+      }
+    }
+    
+    if (idx == registered.length) {
+      MethodInfo[] newReg = new MethodInfo[registered.length+NREG];
+      System.arraycopy(registered, 0, newReg, 0, registered.length);
+      registered = newReg;
+    }
+    
+    registered[idx] = mi;
+    nRegistered++;
+    return idx;
+  }
+  
+  public MethodInfo getRegisteredFieldInfo (int idx) {
+    return registered[idx];
+  }
+
+  public MethodInfo getMethodInfo (MJIEnv env, int objRef, String fieldName) {
+    int idx = env.getIntField( objRef, fieldName);
+    
+    assert ((idx >= 0) || (idx < nRegistered)) : "illegal MethodInfo request: " + idx + ", " + nRegistered;
+    
+    return registered[idx];
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/MethodSpec.java b/src/main/gov/nasa/jpf/util/MethodSpec.java
new file mode 100644 (file)
index 0000000..4a3dbce
--- /dev/null
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.Types;
+
+import java.util.BitSet;
+
+/**
+ * utility class that can match methods/args against specs.
+ * argument signatures can be given in dot notation (like javap), arguments
+ * can be marked with a preceeding '^'
+ * if the class or name part are omitted, "*" is assumed
+ * a preceeding '!' means the match is inverted
+ *
+ * spec examples
+ *   "x.y.Foo.*"
+ *   "java.util.HashMap.add(java.lang.Object,^java.lang.Object)"
+ *   "*.*(x.y.MyClass)"
+ *   
+ * Note: with a single '*' we can't tell if this is for the typename
+ * or the method, so something like "java.*" is probably not doing
+ * what you expect - it uses the wildcard for the method and 'java' for
+ * the type
+ *   
+ * <2do> we should extend this to allow alternatives
+ */
+public class MethodSpec extends FeatureSpec {
+
+  static class MethodParseData extends ParseData {
+    String sigSpec;
+  }
+
+  static final char MARK = '^';  // to mark arguments
+
+  String  sigSpec;  // this is only the argument part, including parenthesis
+  BitSet  markedArgs;
+
+  /**
+   * factory method that includes the parser
+   */
+  public static MethodSpec createMethodSpec (String s){
+    MethodParseData d = new MethodParseData();
+
+    s = s.trim();
+    String src = s;
+
+    s = parseInversion(s,d);
+
+    int i = s.indexOf(('('));
+    if (i >= 0){ // we have a signature part
+      int j = s.lastIndexOf(')');
+      if (j > i){
+        d.sigSpec = s.substring(i, j+1);
+        s = s.substring(0, i);
+
+      } else {
+        return null; // error, unbalanced parenthesis
+      }
+    }
+
+    parseTypeAndName(s,d);
+
+    try {
+      return new MethodSpec(src, d.typeSpec, d.nameSpec, d.sigSpec, d.matchInverted);
+    } catch (IllegalArgumentException iax){
+      return null;
+    }
+  }
+
+
+  public MethodSpec (String rawSpec, String cls, String name, String argSig, boolean inverted){
+    super(rawSpec,cls,name,inverted);
+
+    if (argSig != null){
+      parseSignature(argSig);
+    }
+  }
+
+  /**
+   * assumed to be comma separated type list using fully qualified dot notation 
+   * like javap, but arguments can be marked with preceeding '^', 
+   * like "(java.lang.String,^int[])"
+   * spec includes parenthesis
+   */
+  void parseSignature (String spec){
+    BitSet m = null;
+    StringBuilder sb = new StringBuilder();
+    String al = spec.substring(1, spec.length()-1);
+    String[] args = al.split(",");
+
+    sb.append('(');
+    int i=0;
+    for (String a : args){
+      a = a.trim();
+      if (a.length() > 0){
+        if (a.charAt(0) == MARK){
+          if (m == null){
+            m = new BitSet(args.length);
+          }
+          m.set(i);
+          a = a.substring(1);
+        }
+        String tc = Types.getTypeSignature(a, false);
+        sb.append(tc);
+        i++;
+
+      } else {
+        // error in arg type spec
+      }
+    }
+    sb.append(')');
+
+    sigSpec = sb.toString();
+    markedArgs = m;
+  }
+
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("MethodSpec {");
+    sb.append("matchInverted:");
+    sb.append(matchInverted);
+    if (clsSpec != null){
+      sb.append(",clsSpec:\"");
+      sb.append(clsSpec);
+      sb.append('"');
+    }
+    if (nameSpec != null){
+      sb.append(",nameSpec:\"");
+      sb.append(nameSpec);
+      sb.append('"');
+    }
+    if (sigSpec != null){
+      sb.append(",sigSpec:\"");
+      sb.append(sigSpec);
+      sb.append('"');
+    }
+    if (markedArgs != null){
+      sb.append(",marked:");
+      sb.append(markedArgs);
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+
+  
+  public BitSet getMarkedArgs () {
+    return markedArgs;
+  }
+
+  public boolean isMarkedArg(int idx){
+    return (markedArgs == null || markedArgs.get(idx));
+  }
+
+
+  //--- our matchers
+
+  @Override
+  public boolean matches (Object feature){
+    if (feature instanceof MethodInfo){
+      return matches((MethodInfo)feature);
+    } else {
+      return false;
+    }
+  }
+
+  public boolean matches (MethodInfo mi){
+    boolean isMatch = false;
+
+    ClassInfo ci = mi.getClassInfo();
+    if (isMatchingType(ci)){
+      if (nameSpec.matches(mi.getName())){
+        if (sigSpec != null){
+          // sigSpec includes '(',')' but not return type
+          isMatch = mi.getSignature().startsWith(sigSpec);
+        } else { // no sigSpec -> matches all signatures
+          isMatch = true;
+        }
+      }
+    }
+
+    return (isMatch != matchInverted);
+  }
+
+  public boolean matches (String clsName, String mthName){
+    boolean isMatch = clsSpec.matches(clsName) && nameSpec.matches(mthName);
+    return isMatch != matchInverted;
+  }
+
+  public boolean matchesClass (String clsName){
+    return clsSpec.matches(clsName) != matchInverted;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/MethodSpecMatcher.java b/src/main/gov/nasa/jpf/util/MethodSpecMatcher.java
new file mode 100644 (file)
index 0000000..071f41c
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.MethodInfo;
+
+/**
+ * a matcher for a collection of MethodSpecs
+ */
+public class MethodSpecMatcher {
+
+  protected MethodSpec[] methodSpecs;
+  
+  public static MethodSpecMatcher create (String[] specs){
+    if (specs != null && specs.length > 0){
+      return new MethodSpecMatcher(specs);
+    } else {
+      return null;
+    }
+  }
+  
+  public MethodSpecMatcher(String[] specs){
+    int len = specs.length;
+    methodSpecs = new MethodSpec[len];
+    for (int i=0; i<len; i++){
+      methodSpecs[i] = MethodSpec.createMethodSpec(specs[i]);
+    }
+  }
+  
+  public boolean matches (MethodInfo mi){
+    for (int i=0; i<methodSpecs.length; i++){
+      if (methodSpecs[i].matches(mi)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Misc.java b/src/main/gov/nasa/jpf/util/Misc.java
new file mode 100644 (file)
index 0000000..1add60a
--- /dev/null
@@ -0,0 +1,713 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+
+public class Misc {
+  public static int hashCode(Object o) {
+    return o == null ? 0 : o.hashCode();
+  }
+
+  public static boolean equals(Object a, Object b) {
+    if (a == b) {
+      return true;
+    } else if (a == null || b == null) {
+      // only one could be null
+      return false;
+    } else {
+      return a.equals(b);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <E> Iterator<E> emptyIterator() {
+    return (Iterator<E>) emptyIterator;
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <E> Iterable<E> emptyIterable() {
+    return (Iterable<E>) emptyIterable;
+  }
+
+  public static <E> Iterable<E> iterableFromIterator(Iterator<E> iter) {
+    return new Iteratorable<E>(iter);
+  }
+
+  public static final Object[] emptyObjectArray = new Object[] {};
+
+  public static final Iterator<?> emptyIterator = new Iterator<Object>() {
+    @Override
+       public boolean hasNext () { return false; }
+    @Override
+       public Object next () { throw new NoSuchElementException(); }
+    @Override
+       public void remove () { throw new NoSuchElementException(); }
+  };
+
+  public static final Iterable<?> emptyIterable = new Iterable<Object>() {
+    @Override
+       @SuppressWarnings("unchecked")
+    public Iterator<Object> iterator () {
+      return (Iterator<Object>) emptyIterator;
+    }
+  };
+
+  @SuppressWarnings("unchecked")
+  public static <B, E extends B> Iterable<B> asBaseIterable (Collection<E> col){
+    Collection<B> base = (Collection)col;
+    return base;
+  }
+
+  @SuppressWarnings("unchecked")
+  public static <B, E extends B> Iterator<B> getBaseIterator (Collection<E> col){
+    Collection<B> base = (Collection)col;
+    return base.iterator();
+  }
+  
+  public static <E> void addAll(Collection<E> target, Iterable<? extends E> src) {
+    for (E e : src) target.add(e);
+  }
+
+  public static <T> int indexOf (T[] array, Object elem){
+    for (int i=0; i<array.length; i++){
+      if (array[i].equals(elem)){
+        return i;
+      }
+    }
+    
+    return -1;
+  } 
+
+  public static <T> boolean contains (T[] array, Object elem){
+    for (int i=0; i<array.length; i++){
+      if (array[i].equals(elem)){
+        return true;
+      }
+    }
+    
+    return false;
+  } 
+  
+  @SuppressWarnings("unchecked")
+  public static <T> T[] stripNullElements(T[] oldArray){
+    int count = 0;
+    for (int i=0; i<oldArray.length; i++){
+      if (oldArray[i] != null){
+        count++;
+      }
+    }
+    
+    if (count == oldArray.length){ // nothing to strip
+      return oldArray;
+      
+    } else {
+      Class<?> ct = oldArray.getClass().getComponentType();
+      T[] newArray = (T[]) Array.newInstance(ct, count);
+
+      int j=0;
+      for (int i=0; i<oldArray.length; i++){
+        T e = oldArray[i];
+        if (e != null){
+          newArray[j++] = e;
+          if (j == count){
+            break;
+          }
+        }
+      }
+      
+      return newArray;
+    }
+  }
+  
+  @SuppressWarnings("unchecked")
+  public static <T> T[] getAddedElements (T[] oldArray, T[] newArray) {
+    
+    if (newArray == null || newArray.length == 0) {
+      return oldArray; 
+    }
+    if (oldArray == null || oldArray.length == 0) {
+      return newArray;
+    }
+    
+    T[] na = newArray.clone();
+    int n = na.length;
+    
+    for (int i=0; i<na.length; i++) {
+      Object eNew = na[i];
+      if (eNew != null) {
+        for (int j=0; j<oldArray.length; j++) {
+          if (eNew.equals(oldArray[j])) {
+            na[i] = null;
+            n--;
+            break;
+          }
+        }
+      } else {
+        n--;
+      }
+    }
+    
+    Class<?> ct = newArray.getClass().getComponentType();
+    T[] addedElements = (T[]) Array.newInstance(ct, n);
+    
+    for (int i=0, j=0; i<na.length; i++) {
+      if (na[i] != null) {
+        addedElements[j++] = na[i];
+      }
+    }
+    
+    return addedElements;
+  }
+  
+  public static <T> T[] getRemovedElements (T[] oldArray, T[] newArray) {
+
+    if (newArray == null || newArray.length == 0 || oldArray == null || oldArray.length == 0) {
+      return null;
+    }
+    
+    T[] oa = oldArray.clone();
+    int n = oa.length;
+    
+    for (int i=0; i<oa.length; i++) {
+      Object eOld = oa[i];
+      if (eOld != null) {
+        for (int j=0; j<newArray.length; j++) {
+          if (eOld.equals(newArray[j])) {
+            oa[i] = null;
+            n--;
+            break;
+          }
+        }
+      } else {
+        n--;
+      }
+    }
+    
+    Class<?> ct = oldArray.getClass().getComponentType();
+    T[] addedElements = (T[]) Array.newInstance(ct, n);
+    
+    for (int i=0, j=0; i<oa.length; i++) {
+      if (oa[i] != null) {
+        addedElements[j++] = oa[i];
+      }
+    }
+    
+    return addedElements;
+  }
+  
+  
+  public static <K,V> ArrayList<String> getSortedKeyStrings (HashMap<K,V> map){
+    ArrayList<String> list = new ArrayList<String>();
+
+    nextKey:
+    for (K key : map.keySet()){
+      String ks = key.toString();
+
+      for (int i=0; i<list.size(); i++){
+        String k = list.get(i);
+        if (ks.compareTo(k) > 0){
+          list.add(i, ks);
+          continue nextKey;
+        }
+      }
+
+      list.add(ks);
+    }
+
+    return list;
+  }
+
+  public static <K,V> ArrayList<Map.Entry<K,V>> createSortedEntryList (HashMap<K,V> map,
+                                                                       Comparator<Map.Entry<K,V>> comparer) {
+    ArrayList<Map.Entry<K,V>> list = new ArrayList<Map.Entry<K,V>>();
+
+    nextEntry:
+      for (Map.Entry<K,V> e : map.entrySet()){
+
+        for (int i=0; i<list.size(); i++){
+          if (comparer.compare(e,list.get(i)) > 0) {
+            list.add(i, e);
+            continue nextEntry;
+          }
+        }
+        list.add(e);
+      }
+
+
+    return list;
+  }
+
+  public static <K,V,E> ArrayList<E> createSortedList (HashMap<K,V> map,
+                                                    TwoTypeComparator<Map.Entry<K,V>,E> comparer,
+                                                    ElementCreator<Map.Entry<K,V>,E> creator) {
+    ArrayList<E> list = new ArrayList<E>();
+
+    nextEntry:
+      for (Map.Entry<K,V> e : map.entrySet()){
+
+        for (int i=0; i<list.size(); i++){
+          if (comparer.compare(e,list.get(i)) > 0) {
+            list.add(i, creator.create(e));
+            continue nextEntry;
+          }
+        }
+        list.add(creator.create(e));
+      }
+
+
+    return list;
+  }
+
+  public static int compare (Integer i1, Integer i2) {
+    return Integer.signum(i1.intValue() - i2.intValue());
+  }
+
+  public static <E,T> HashMap<E,Integer> createOccurrenceMap (Collection<T> collection,
+                                                            ElementCreator<T,E> creator) {
+    HashMap<E,Integer> map = new HashMap<E,Integer>();
+
+    for (T o : collection) {
+      E e = creator.create(o);
+      Integer nE = map.get(e);
+      if (nE == null){
+        map.put(e,1);
+      } else {
+        map.put(e,nE.intValue()+1);
+      }
+    }
+
+    return map;
+  }
+
+  public static <T> T createObject (Class<T> cls, Class<?>[] argTypes, Object[] args){
+    if (argTypes.length != args.length){
+      return null;
+    }
+
+    while (argTypes.length >= 0){
+      try {
+        Constructor<T> ctor = cls.getConstructor(argTypes);
+        return ctor.newInstance(args);
+
+      } catch (NoSuchMethodException nsmx){
+        Class<?>[] at = new Class<?>[argTypes.length-1];
+        System.arraycopy(argTypes, 1, at,0, at.length);
+        argTypes = at;
+
+        Object[] a = new Object[at.length];
+        System.arraycopy(args,1, a,0, a.length);
+        args = a;
+
+      } catch (Throwable t){
+        return null;
+      }
+    }
+
+    return null;
+  }
+
+  public static String toString (Object[] collection) {
+    StringBuilder sb = new StringBuilder();
+    
+    for (Object e : collection) {
+      sb.append(e);
+    }
+
+    return sb.toString();
+  }
+  
+  public static String toString (String[] collection) {
+    StringBuilder sb = new StringBuilder();
+    
+    for (Object e : collection) {
+      sb.append(e);
+    }
+
+    return sb.toString();
+  }
+  
+  public static String toString (Iterable<?> collection) {
+    StringBuilder sb = new StringBuilder();
+    
+    for (Object e : collection) {
+      sb.append(e);
+    }
+
+    return sb.toString();
+  }
+  
+  public static String toString( Iterable<?> collection,
+                                 String prefix, String separator, String postfix) {
+    StringBuilder sb = new StringBuilder();
+
+    if (prefix != null) {
+      sb.append(prefix);
+    }
+
+    int i=0;
+    for (Object e : collection) {
+      if (i++ > 0) {
+        sb.append(separator);
+      }
+      sb.append(e);
+    }
+
+    if (postfix != null) {
+      sb.append(postfix);
+    }
+
+    return sb.toString();
+  }
+
+  public static String toString( Object[] array,
+                                 String prefix, String separator, String postfix) {
+    StringBuilder sb = new StringBuilder();
+
+    if (prefix != null) {
+      sb.append(prefix);
+    }
+
+    for (int i=0; i<array.length; i++) {
+      if (i > 0) {
+        sb.append(separator);
+      }
+      sb.append(array[i]);
+    }
+
+    if (postfix != null) {
+      sb.append(postfix);
+    }
+
+    return sb.toString();
+  }
+
+  public static String toString( int[] array, int start, int end, String prefix, String separator, String postfix){
+    StringBuilder sb = new StringBuilder();
+
+    if (prefix != null) {
+      sb.append(prefix);
+    }
+
+    for (int i=start; i<array.length && i < end; i++) {
+      if (i > 0) {
+        sb.append(separator);
+      }
+      sb.append(array[i]);
+    }
+
+    if (postfix != null) {
+      sb.append(postfix);
+    }
+
+    return sb.toString();    
+  }
+
+  public static <T> T[] newArray (T... elements) {
+    return elements;
+  }
+
+  public static <T> T[] appendArray (T[] base, T... elements) {
+    if (base == null || base.length == 0){
+      return elements;
+    } else if (elements == null || elements.length == 0){
+      return base;
+    } else {
+      Class<?> componentType = base.getClass().getComponentType();
+      T[] a = (T[]) Array.newInstance(componentType, base.length + elements.length);
+      System.arraycopy(base, 0, a, 0, base.length);
+      System.arraycopy(elements, 0, a, base.length, elements.length);
+      return a;
+    }
+  }
+
+  public static <T> T[] prependArray (T[] base, T... elements) {
+    if (base == null || base.length == 0){
+      return elements;
+    } else if (elements == null || elements.length == 0){
+      return base;
+    } else {
+      Class<?> componentType = base.getClass().getComponentType();
+      T[] a = (T[]) Array.newInstance(componentType, base.length + elements.length);
+      System.arraycopy(base,0, a,elements.length, base.length);
+      System.arraycopy(elements,0, a,0, elements.length);
+      return a;
+    }
+  }
+
+  public static String[] prependArray (String[] base, String... elements){
+    if (base == null || base.length == 0){
+      return elements;
+    } else if (elements == null || elements.length == 0){
+      return base;
+    } else {
+      String[] a = new String[base.length + elements.length];
+      System.arraycopy(base,0, a,elements.length, base.length);
+      System.arraycopy(elements,0, a,0, elements.length);
+      return a;
+    }
+  }
+
+
+  public static <T> T[] arrayWithoutFirst( T[] base, int nElements){
+    if (base == null){
+      return null;
+    } else if (nElements >= base.length){
+      Class<?> componentType = base.getClass().getComponentType();
+      T[] a = (T[]) Array.newInstance(componentType, 0);
+      return a;
+    } else {
+      int n = base.length - nElements;
+      Class<?> componentType = base.getClass().getComponentType();
+      T[] a = (T[]) Array.newInstance(componentType, n);
+      System.arraycopy(base, nElements, a, 0, n);
+      return a;
+    }
+  }
+
+  public static <T> T[] appendElement (T[] base, T newElement){
+    int len = base.length;
+
+    Class<?> componentType = base.getClass().getComponentType();
+    T[] a = (T[]) Array.newInstance(componentType, len + 1);
+    System.arraycopy(base, 0, a, 0, len);
+    a[len] = newElement;
+
+    return a;
+  }
+
+  public static <T> T[] appendUniqueElement (T[] base, T newElement){
+    int len = base.length;
+
+    for (int i=0; i<len; i++){
+      if (base[i] == newElement){
+        return base;
+      }
+    }
+
+    return appendElement(base, newElement);
+  }
+
+  
+  public static <T> T[] removeElement (T[] base, T element) {
+    int len = base.length;
+
+    for (int i=0; i<len; i++){
+      if (base[i] == element){
+        Class<?> componentType = base.getClass().getComponentType();
+        T[] a = (T[]) Array.newInstance(componentType, len -1);
+        System.arraycopy(base, 0, a, 0, i);
+        System.arraycopy(base, i+1, a, i, len-i-1);
+        return a;
+      }
+    }
+
+    return base;
+  }
+
+  public static <T> boolean hasElementOfType (T[] base, Class<?> elemType){
+    int len = base.length;
+    for (int i=0; i<len; i++){
+      if (elemType.isInstance(base[i])){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public static <T,E> E getNextElementOfType (T[] base, Class<E> elemType, T prev){
+    boolean prevSeen = (prev == null);
+    int len = base.length;
+    for (int i=0; i<len; i++){
+      if (elemType.isInstance(base[i])){
+        if (prevSeen){
+          return (E)base[i];
+        } else {
+          prevSeen = (base[i] == prev);
+        }
+      }
+    }
+    
+    return null;
+  }
+
+  public static String[] arrayWithoutFirst(String[] base, int nElements){
+    String[] a = new String[base.length-1];
+    if (a.length > 0){
+      System.arraycopy(base,1,a,0,a.length);
+    }
+    return a;
+  }
+
+  public static boolean equals (Object[] a1, Object[] a2){
+    if (a1 == a2){
+      return true;
+    }
+
+    if (a1 == null){
+      if (a2 != null){
+        for (int i=0; i<a2.length; i++){
+          if (a2[i] != null){
+            return false;
+          }
+        }
+      }
+    } else if (a2 == null){
+      for (int i=0; i<a1.length; i++){
+        if (a1[i] != null){
+          return false;
+        }
+      }
+    } else {
+      if (a1.length != a2.length){
+        return false;
+      }
+
+      for (int i = 0; i < a1.length; i++) {
+        Object o1 = a1[i];
+        Object o2 = a2[i];
+
+        if (o1 != null) {
+          if (!o1.equals(o2)) {
+            return false;
+          }
+        } else if (o2 != null) {
+          return false;
+        }
+      }
+    }
+
+    return true;
+
+  }
+
+  /**
+   * equals first len objects of two reference arrays, which can contain null
+   * elements. If any of the reference arrays is null, this is treated as
+   * if all elements would be null
+   */
+  public static boolean compare (int len, Object[] a1, Object[] a2){
+    if (a1 == null && a2 == null){
+      return true;
+    }
+    
+    if (a1 == null){
+      if (a2 != null){
+        if (a2.length < len){
+          return false;
+        }
+        for (int i=0; i<len; i++){
+          if (a2[i] != null){
+            return false;
+          }
+        }
+      }
+    } else if (a2 == null){
+      if (a1.length < len){
+        return false;
+      }
+      for (int i=0; i<len; i++){
+        if (a1[i] != null){
+          return false;
+        }
+      }      
+    } else {
+      if (a1.length < len || a2.length < len){
+        return false;
+      }
+
+      for (int i = 0; i < len; i++) {
+        Object o1 = a1[i];
+        Object o2 = a2[i];
+
+        if (o1 != null) {
+          if (!o1.equals(o2)) {
+            return false;
+          }
+        } else if (o2 != null) {
+          return false;
+        }
+      }
+    }
+    
+    return true;
+  }
+
+  public static String stripToLastDot (String s){
+    int i = s.lastIndexOf('.');
+    if (i>=0){
+      return s.substring(i+1);
+    } else {
+      return s;
+    }
+  }
+
+  public static int setBit (int val, int idx){
+    return (val | (1<<idx));
+  }
+  
+  public static int clearBit (int val, int idx){
+    return (val & ~(1<<idx));
+  }
+
+  public static String upToFirst( String s, char c){
+    int i = s.indexOf(c);
+    if (i >= 0){
+      return s.substring(0, i);
+    } else {
+      return s;
+    }
+  }
+  
+  public static String fromFirst( String s, char c){
+    int i = s.indexOf(c);
+    if (i >= 0){
+      return s.substring(i);
+    } else {
+      return s;
+    }
+  }
+  
+  
+  /*=================== PRIVATE STUFF ===================*/
+
+  private static final class Iteratorable<E> implements Iterable<E> {
+    Iterator<E> iter;
+
+    public Iteratorable(Iterator<E> iter) {
+      this.iter = iter;
+    }
+
+    @Override
+       public Iterator<E> iterator () {
+      Iterator<E> ret = iter;
+      iter = null;
+      return ret;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/MutableInteger.java b/src/main/gov/nasa/jpf/util/MutableInteger.java
new file mode 100644 (file)
index 0000000..0c82f43
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * an object that holds a mutable int. Unfortunately, java.lang.Integer is
+ * final, but we can at least be a Number
+ */
+public class MutableInteger extends Number {
+
+  private int value;
+  
+  public MutableInteger (int val){
+    value = val;
+  }
+  
+  public void set (int val){
+    value = val;
+  }
+  
+  //--- arithmetic operations
+  public MutableInteger inc() {
+    value++;
+    return this;
+  }
+  
+  public MutableInteger dec() {
+    value--;
+    return this;
+  }
+  
+  public MutableInteger add (int n){
+    value += n;
+    return this;
+  }
+  
+  public MutableInteger subtract (int n){
+    value -= n;
+    return this;
+  }
+  
+  public MutableInteger multiply (int n){
+    value *= n;
+    return this;
+  }
+  
+  public MutableInteger divide (int n){
+    value /= n;
+    return this;
+  }
+  
+  //-- Hmm, we probably want to round correctly for these
+  public MutableInteger add (Number n){
+    value += n.intValue();
+    return this;
+  }
+  
+  public MutableInteger subtract (Number n){
+    value -= n.intValue();
+    return this;
+  }
+  
+  public MutableInteger multiply (Number n){
+    value *= n.intValue();
+    return this;
+  }
+  
+  public MutableInteger divide (Number n){
+    value /= n.intValue();
+    return this;
+  }
+  
+  //--- value accessors
+  
+  @Override
+  public double doubleValue() {
+    return value;
+  }
+
+  @Override
+  public float floatValue() {
+    return value;
+  }
+
+  @Override
+  public int intValue() {
+    return value;
+  }
+
+  @Override
+  public long longValue() {
+    return value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/MutableIntegerRestorer.java b/src/main/gov/nasa/jpf/util/MutableIntegerRestorer.java
new file mode 100644 (file)
index 0000000..0c90139
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClosedMemento;
+
+/**
+ * on-demand restorer for MutableInteger instances
+ */
+public class MutableIntegerRestorer implements ClosedMemento {
+
+  MutableInteger restoree;
+  int value;
+  
+  public MutableIntegerRestorer (MutableInteger restoree){
+    assert restoree != null : "restored object can't be null";
+    this.restoree = restoree;
+    this.value = restoree.intValue();
+  }
+  
+  @Override
+  public void restore (){
+    restoree.set(value);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/OATHash.java b/src/main/gov/nasa/jpf/util/OATHash.java
new file mode 100644 (file)
index 0000000..13b3e7f
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * Bob Jenkins One-at-a-Time Hash (http://www.burtleburtle.net/bob/hash/doobs.html),
+ * a simple yet sufficiently avalanching hash that doesn't require a-priori knowledge
+ * of the key length and is much faster than lookup3 
+ */
+public class OATHash {
+
+  //--- the hash primitives
+  
+  public static int hashMixin (int h, int key){
+    int k = key & 0xff;
+    h += k; h += (h <<10); h ^= (h >>> 6);
+    
+    key >>= 8;
+    k = key & 0xff;
+    h += k; h += (h <<10); h ^= (h >>> 6);
+
+    key >>= 8;
+    k = key & 0xff;
+    h += k; h += (h <<10); h ^= (h >>> 6);
+
+    key >>= 8;
+    k = key & 0xff;
+    h += k; h += (h <<10); h ^= (h >>> 6);
+    
+    return h;
+  }
+  
+  public static int hashMixin (int h, long key) {
+    h = hashMixin( h, (int)key);
+    h = hashMixin( h, (int)(key >> 32));
+    return h;
+  }
+  
+  public static int hashFinalize (int h){
+    h += (h << 3);
+    h ^= (h >>> 11);
+    h += (h << 15);
+    
+    return h;
+  }
+
+  //--- the one step public hashers
+  
+  public static int hash (int key){
+    return hashFinalize( hashMixin(0,key));
+  }
+  
+  public static int hash (int key1, int key2){
+    int h = hashMixin(0,key1);
+    h = hashMixin(h, key2);
+    return hashFinalize(h);
+  }
+  
+  public static int hash (long key) {
+    int h = hashMixin(0, (int)key);
+    h = hashMixin( h, (int)(key>>32));
+    return hashFinalize(h);
+  }
+  
+  public static int hash (int key1, int key2, int key3) {
+    int h = hashMixin( 0, key1);
+    h = hashMixin( h, key2);
+    h = hashMixin( h, key3);
+    
+    return hashFinalize(h);
+  }
+  
+  public static int hash (int[] keys){
+    int h = 0;
+    for (int i=0; i<keys.length; i++){
+      h = hashMixin( h, keys[i]);
+    }
+    return hashFinalize(h);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjArray.java b/src/main/gov/nasa/jpf/util/ObjArray.java
new file mode 100644 (file)
index 0000000..42c541e
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Wrapper for arrays of objects which provides proper equals() and hashCode()
+ * methods, and behaves nicely with Java 1.5 generics. 
+ */
+public final class ObjArray<E> implements ReadOnlyObjList<E>, Iterable<E>, Cloneable  {
+  final Object[] data;
+
+  public ObjArray(int size) {
+    data = new Object[size];
+  }
+  
+  public ObjArray(E[] data) {
+    this.data = data;
+  }
+
+  @Override
+  public ObjArray<E> clone() {
+    return new ObjArray( data.clone());
+  }
+
+
+  public E[] toArray (E[] a) {
+    if (a.length >= data.length) {
+      System.arraycopy(data,0,a,0,data.length);
+      return a;
+    } else {
+      return null;
+    }
+  }
+  
+  
+  @Override
+@SuppressWarnings("unchecked")
+  public E get(int idx) {
+    return (E) data[idx];
+  }
+  
+  public void set(int idx, E e) {
+    data[idx] = e;
+  }
+  
+  @Override
+  public int length() {
+    return data.length;
+  }
+  
+  @Override
+  public int hashCode() {
+    return Arrays.hashCode(data);
+  }
+  
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) return true;
+    if (! (o instanceof ObjArray)) return false;
+    Object[] thatData = ((ObjArray)o).data;
+    Object[] thisData = this.data;
+    
+    // could cause NullPointerException for non-robust .equals 
+    // return Arrays.equals(thisData, thatData);
+    
+    if (thisData == thatData) return true;
+    if (thisData.length != thatData.length) return false;
+    for (int i = 0; i < thisData.length; i++) {
+      if (!Misc.equals(thisData[i], thatData[i])) {
+        return false;
+      }
+    }
+    return true;
+  }
+
+  public void fill(E e) {
+    Arrays.fill(data, e);
+  }
+  
+  public void nullify () {
+    Arrays.fill(data, null);
+  }
+  
+  public static <T> void copy(ObjArray<? extends T> src, int srcPos,
+                              ObjArray<T> dst, int dstPos, int len) {
+    System.arraycopy(src.data, srcPos, dst.data, dstPos, len);
+  }
+
+  static final ObjArray<Object> zero = new ObjArray<Object>(0);
+  @SuppressWarnings("unchecked")
+  public static <T> ObjArray<T> zeroLength() {
+    return (ObjArray<T>) zero;
+  }
+  
+  @Override
+  public Iterator<E> iterator () {
+    return new Iterator<E>() {
+      int idx = 0;
+
+      @Override
+       public boolean hasNext () {
+        return idx < data.length;
+      }
+
+      @Override
+       @SuppressWarnings("unchecked")
+      public E next () {
+        if (idx >= data.length) throw new NoSuchElementException();
+        return (E) data[idx++];
+      }
+
+      @Override
+       public void remove () {
+        throw new UnsupportedOperationException();
+      }
+    };
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjVector.java b/src/main/gov/nasa/jpf/util/ObjVector.java
new file mode 100644 (file)
index 0000000..4fb85b6
--- /dev/null
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * more customizable alternative to java.util.Vector. Other than Vector, it
+ * supports dynamic growth on set() operations. While it supports list
+ * functions such as append, ObjVector resembles mostly an array, i.e.
+ * is meant to be a random-access collection
+ * 
+ * this collection does not keep a count of non-null elements, but does maintain the
+ * highest set index as its size through set/add and remove operations. Note that size
+ * only shrinks through remove operations, not by setting null values. This means there
+ * is no guarantee that data[size-1] is not null. The converse however is true - there is no
+ * non-null element at an index >= size.
+ * 
+ * @author pcd
+ */
+public class ObjVector<E> implements ReadOnlyObjList<E>, Cloneable {
+  public static final int defaultInitCap = 40;  
+
+  /** <i>size</i> as in a java.util.Vector. */
+  protected int size;
+  
+  /** the backing array. */
+  protected Object[] data;
+  
+  /** growth strategy. */
+  protected Growth growth;
+    
+  
+  //--- constructors
+  
+  public ObjVector(Growth initGrowth, int initCap) {
+    growth = initGrowth;
+    data = new Object[initCap];
+    size = 0;
+  }
+  
+  public ObjVector(Growth initGrowth) {
+    this(initGrowth,defaultInitCap);
+  }
+  
+  public ObjVector(int initCap) { 
+    this(Growth.defaultGrowth, initCap);
+  }
+  
+  public ObjVector() {
+    this(Growth.defaultGrowth,defaultInitCap);
+  }
+  
+  public <F extends E> ObjVector(F[] init) {
+    this(init.length);
+    append(init);
+  }
+  
+  public <F extends E> ObjVector(ObjVector<F> from) {
+    this.data = new Object[from.data.length];
+    this.size = from.size;
+    this.growth = from.growth;
+    System.arraycopy(from.data, 0, this.data, 0, size);
+  }
+  
+  //--- set/add/remove operations  
+  
+  public void add(E x) {
+    if (size >= data.length) {
+      ensureCapacity(size+1);
+    }
+    data[size] = x;
+    size++;
+  }
+  
+  public void addNulls (int length) {
+    int newSize = size + length;
+    if (newSize > data.length) {
+      ensureCapacity(size + length);
+    }
+    for (int i = size; i < newSize; i++) {
+      data[i] = null;
+    }
+    size = newSize;
+  }
+
+  public <F extends E> void append(F[] x) {
+    if (size + x.length > data.length) {
+      ensureCapacity(size + x.length);
+    }
+    System.arraycopy(x, 0, data, size, x.length);
+    size += x.length;
+  }
+
+  public <F extends E> void append(F[] x, int pos, int len) {
+    if (size + len > data.length) {
+      ensureCapacity(size + len);
+    }
+    System.arraycopy(x, pos, data, size, len);
+    size += len;
+  }
+  
+  public <F extends E> void append(ObjVector<F> x) {
+    if (size + x.size > data.length) {
+      ensureCapacity(size + x.size);
+    }
+    System.arraycopy(x.data, 0, data, size, x.size);
+    size += x.size;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public <F extends E> void append(ObjArray<F> x) {
+    append((F[])(x.data));
+  }
+
+  public <F extends E> void append(ArrayList<F> x){
+    int n = x.size();
+    int newSize = size + n;
+    if (newSize > data.length) {
+      ensureCapacity(newSize);
+    }
+    for (int i = size, j=0; i < newSize; i++,j++) {
+      data[i] = x.get(j);
+    }
+    size = newSize;
+  }
+
+  public <F extends E> void addAll(Iterable<F> x) {
+    if (x instanceof ObjVector) {
+      append((ObjVector<F>) x);
+      return;
+    }
+    // else
+    if (x instanceof ObjArray) {
+      append((ObjArray<F>) x);
+      return;
+    }
+    // else
+    if (x == null) return;
+    // else
+    for (F e : x) {
+      add(e);
+    }
+  }
+
+  public int nextNull (int fromIndex){
+    for (int i=fromIndex; i<size; i++){
+      if (data[i] == null){
+        return i;
+      }
+    }
+
+    ensureCapacity(size+1);
+    return size;
+  }
+
+  @Override
+@SuppressWarnings("unchecked")
+  public E get(int idx) {
+    if (idx >= size) {
+      return null;
+    } else {
+      return (E) data[idx];
+    }
+  }
+  
+  public void set(int idx, E v) {
+    ensureSize(idx+1);
+    data[idx] = v;
+  }
+
+  /**
+   * set range of values
+   * @param fromIndex first index (inclusive)
+   * @param toIndex last index (exclusive)
+   * @param val value to set
+   */
+  public void setRange (int fromIndex, int toIndex, E val) {
+    ensureSize(toIndex);
+    Arrays.fill(data, fromIndex, toIndex, val);
+  }
+  
+  public <F> F[] toArray (F[] dst) {
+    System.arraycopy(data,0,dst,0,size);
+    return dst;
+  }
+
+  public ObjArray<E> toObjArray () {
+    ObjArray<E> dst = new ObjArray<E>(size);
+    System.arraycopy(data,0,dst.data,0,size);
+    return dst;
+  }
+
+  public int dumpTo (Object[] dst, int pos) {
+    System.arraycopy(data,0,dst,pos,size);
+    return pos + size;
+  }
+
+  @Override
+  public ObjVector<E> clone() {
+    return new ObjVector<E>(this);
+  }
+  
+  public void squeeze() {
+    while (size > 0 && data[size - 1] == null) size--;
+  }
+  
+  public void setSize(int sz) {
+    if (sz > size) {
+      ensureCapacity(sz);
+      size = sz;
+    } else {
+      while (size > sz) {
+        size--;
+        data[size] = null;
+      }
+    }
+  }
+
+  public void clear() { 
+    // faster than iterating over the whole array
+    data = new Object[data.length];
+    size = 0;
+  }
+  
+  @SuppressWarnings("unchecked")
+  public void clearAllSatisfying (Predicate<E> pred) {
+    Object[] d = data;
+    int newSize = 0;
+    for (int i=size-1; i>=0; i--) {
+      E e = (E)d[i];
+      if (e != null) {
+        if (pred.isTrue(e)) {
+          d[i] = null;
+        } else {
+          if (newSize == 0) {
+            newSize = i+1;
+          }
+        }
+      }
+    }
+    
+    size = newSize;
+  }
+  
+  public int size() { 
+    return size; 
+  }
+  
+  @Override
+  public int length() {
+    return size;
+  }
+  
+  public void ensureSize(int sz) {
+    if (size < sz) {
+      ensureCapacity(sz);
+      size = sz;
+    }
+  }
+  
+  public void ensureCapacity(int desiredCap) {
+    if (data.length < desiredCap) {
+      Object[] newData = new Object[growth.grow(data.length, desiredCap)];
+      System.arraycopy(data, 0, newData, 0, size);
+      data = newData;
+    }
+  }
+  
+  @SuppressWarnings("unchecked")
+  public void sort(Comparator<? super E> comp) {
+    Arrays.sort(data, 0, size, (Comparator<Object>) comp);
+  }
+  
+  public static <E> void copy(ObjVector<? extends E> src, int srcPos,
+                              ObjVector<E> dst, int dstPos, int len) {
+    src.ensureCapacity(srcPos + len);
+    dst.ensureSize(dstPos+len);
+    System.arraycopy(src.data, srcPos, dst.data, dstPos, len);
+  }
+  
+  public static <E> void copy(ObjVector<? extends E> src, int srcPos,
+      E[] dst, int dstPos, int len) {
+    src.ensureCapacity(srcPos + len);
+    //dst.ensureSize(dstPos+len);
+    System.arraycopy(src.data, srcPos, dst, dstPos, len);
+  }
+
+  /**
+   * remove all non-null elements between 'fromIdx' (inclusive) and
+   * 'toIdx' (exclusive)
+   * throw IndexOutOfBoundsException if index values are out of range
+   */
+  public int removeRange(int fromIdx, int toIdx){
+    int n = 0;
+    Object[] data = this.data;
+
+    // it's the callers responsibility to ensure proper index ranges
+    //if (fromIdx < 0) fromIdx = 0;
+    //if (toIdx > size) toIdx = size;
+
+    for (int i=fromIdx; i<toIdx; i++){
+      if (data[i] != null){
+        data[i] = null;
+        n++;
+      }
+    }
+
+    if (toIdx >= size){
+      int i=fromIdx-1;
+      for (; i>=0 && (data[i] == null); i--);
+      size = i+1;
+    }
+
+    return n;
+  }
+
+  public int removeFrom(int fromIdx){
+    return removeRange(fromIdx,size);
+  }
+
+  public E remove (int i) {
+    E e = (E) data[i];
+    
+    if (e != null) {
+      data[i] = null;
+      if (i+1 == size) {
+        int j=i-1;
+        for (; j>=0 && (data[j] == null); j--); 
+        size = j+1;
+      }
+    }
+    
+    return e;
+  }
+
+  //--- store/restore snapshot operations
+    
+  static final int DEFAULT_MAX_GAP = 10;
+  
+  /**
+   * this is a block operation snapshot that stores chunks of original data with
+   * not more than DEFAULT_MAX_GAP consecutive null elements. Use this if 
+   * elements can be stored directly
+   */
+  public static class Snapshot<E> {
+    static class Block {
+      int baseIndex;
+      Object[] data;
+      Block next;
+      
+      Block (int baseIndex, Object[] data, Block next){
+        this.baseIndex = baseIndex;
+        this.data = data;
+        this.next = next;
+      }
+    }
+    
+    // the ObjVector state we directly store
+    int size;
+    Growth growth;
+    
+    // where we keep the data
+    Block head;
+    
+    int saveBlock (Object[] d, int start, int end) {
+      int len = end-start+1;
+      Object[] bd = new Object[len];
+      System.arraycopy(d, start, bd, 0, len);
+      head = new Block(start, bd, head);      
+      
+      return len;
+    }
+    
+    Snapshot (ObjVector<E> v, int maxGap){
+      int n = v.size;
+      size = n;
+      growth = v.growth;      
+      Object[] d = v.data;
+      
+      int end = -1, start = -1;
+      
+      for (int i=n-1; (i>=0) && (n>0); i--) {
+        if (d[i] != null) {
+          if (start > 0 && (start - i) > maxGap ) { // store prev block
+            n -= saveBlock( d, start, end);              
+            end = i;
+            start = i;
+            
+          } else {
+            if (end < 0) {
+              end = i;
+            }
+            start = i;
+          }
+        }
+      }
+      
+      if (end >=0 && end >= start) {
+        saveBlock( d, start, end);
+      }
+    }    
+    
+    public void restore (ObjVector<E> v) {
+      // this is faster than iterating through the array
+      Object[] d = new Object[size];
+      v.data = d;
+
+      for (Block block = head; block != null; block = block.next) {
+        Object[] bd = block.data;
+        System.arraycopy(bd, 0, d, block.baseIndex, bd.length);
+      }
+      
+      v.size = size;
+      v.growth = growth;
+    }
+  }
+
+  
+  public Snapshot<E> getSnapshot(){
+    return new Snapshot<E>(this, DEFAULT_MAX_GAP);
+  }
+  
+  /**
+   * create a snapshot that doesn't store more than maxGap consecutive null values
+   */
+  public Snapshot<E> getSnapshot( int maxGap){
+    return new Snapshot<E>(this, maxGap);
+  }
+  
+  public void restore (Snapshot<E> snap) {
+    snap.restore(this);
+  }
+
+  
+  /**
+   *  snapshot that can mutate element values, but therefore can't use block operations.
+   *  This is slower to store/restore, but can be more memory efficient if the elements
+   *  are fragmented (lots of small holes in data)
+   */
+  
+  public static class MutatingSnapshot<E,T>{
+    T[] values;
+    int[] indices;
+    
+    @SuppressWarnings("unchecked")
+    MutatingSnapshot (ObjVector<E> vec, Transformer<E,T> transformer){
+      E[] d = (E[])vec.data;
+      int size = vec.size;
+      int len = 0;
+      
+      //--- get number of non-null elements
+      for (int i=0; i<size; i++) {
+        if (d[i] != null) {
+          len++;
+        }
+      }
+      
+      //--- allocate data
+      T[] values = (T[])new Object[len];
+      int[] indices = new int[len];
+      
+      //--- fill it
+      int j=0;
+      for (int i=0; j < len; i++) {
+        if (d[i] != null) {
+          indices[j] = i;
+          values[j] = transformer.transform(d[i]);
+          j++;
+        }
+      }
+      
+      this.values = values;
+      this.indices = indices;
+    }
+    
+    @SuppressWarnings("unchecked")
+    protected void restore (ObjVector<E> vec, Transformer<T,E> transformer) {
+      T[] values = this.values;
+      int[] indices = this.indices;
+      int len = indices.length;
+      int size = indices[len-1] +1;
+
+      vec.clear();
+      vec.ensureSize(size);
+      E[] d = (E[])vec.data;
+
+      for (int i=0; i<len; i++){
+        E obj = transformer.transform(values[i]);
+        int index = indices[i];
+        d[index] = obj;
+      }
+      
+      vec.size = size;
+    }
+  }
+  
+  public <T> MutatingSnapshot<E,T> getSnapshot( Transformer<E,T> transformer){
+    return new MutatingSnapshot<E,T>(this, transformer);
+  }
+  
+  public <T> void restore (MutatingSnapshot<E,T> snap, Transformer<T,E> transformer) {
+    snap.restore(this, transformer);
+  }
+  
+
+  //--- iterators
+  
+  /**
+   * iterator that goes over all elements regardless of value (i.e. also includes null values)
+   */
+  protected class OVIterator implements Iterator<E> {
+    int idx = 0;
+    
+    @Override
+       public boolean hasNext () {
+      return idx < size;
+    }
+
+    @Override
+       @SuppressWarnings("unchecked")
+    public E next () {
+      if (idx >= data.length) throw new NoSuchElementException();
+      E e = (E) data[idx];
+      idx++;
+      return e;
+    }
+
+    @Override
+       public void remove () {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  @Override
+  public Iterator<E> iterator () {
+    return new OVIterator();
+  }
+  
+  /**
+   * iterator that only includes element values that are not null
+   */
+  protected class NonNullIterator implements Iterator<E>, Iterable<E> {
+    int idx = 0;
+    //int count = 0;
+
+    @Override
+       public boolean hasNext() {
+      return (idx < size); // size is max set index
+    }
+
+    @Override
+       @SuppressWarnings("unchecked")
+    public E next () {
+      int len = data.length;
+      for (int i=idx; i<len; i++){
+        Object o = data[i];
+        if (o != null){
+          //count++;
+          idx = i+1;
+          return (E)o;
+        }
+      }
+
+      throw new NoSuchElementException();
+    }
+
+    @Override
+       public void remove () {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+       public Iterator<E> iterator() {
+      return this;
+    }
+  }
+  
+
+
+  public Iterator<E> nonNullIterator() {
+    return new NonNullIterator();
+  }
+
+  public Iterable<E> elements() {
+    return new NonNullIterator();
+  }
+
+  public void process (Processor<E> processor) {
+    for (int i=0; i<data.length; i++) {
+      Object o = data[i];
+      if (o != null) {
+        processor.process( (E)o);
+      }
+    }
+  }
+
+  //--- misc (debugging etc.)
+  
+  public void printOn (PrintStream ps) {
+    ps.println("ObjVector = [");
+    for (int i=0; i<size; i++) {
+      ps.print("  ");
+      ps.print(i);
+      ps.print(": ");
+      ps.println(get(i));
+    }
+    ps.println(']');
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjectConverter.java b/src/main/gov/nasa/jpf/util/ObjectConverter.java
new file mode 100644 (file)
index 0000000..89dda10
--- /dev/null
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClinitRequired;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.MJIEnv;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.Field;
+
+/**
+ * Object transformer from Java objects to JPF objects
+ * @author Ivan Mushketik
+ */
+public class ObjectConverter {
+  /**
+   * Create JPF object from Java object
+   * @param env - MJI environment
+   * @param javaObject - java object that is used to created JPF object from
+   * @return reference to new JPF object
+   */
+  public static int JPFObjectFromJavaObject(MJIEnv env, Object javaObject) throws ClinitRequired {
+      Class<?> javaClass = javaObject.getClass();
+      String typeName = javaClass.getName();
+      int newObjRef = env.newObject(typeName);
+      ElementInfo newObjEI = env.getModifiableElementInfo(newObjRef);
+
+      ClassInfo ci = env.getClassInfo(newObjRef);
+
+      while (ci != null) {
+        for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
+          if (!fi.isReference()) {
+            setJPFPrimitive(newObjEI, fi, javaObject);
+          }
+          else {
+            try {
+              Field arrField = getField(fi.getName(), javaClass);
+              arrField.setAccessible(true);
+              Object fieldJavaObj = arrField.get(javaObject);
+
+              int fieldJPFObjRef;
+              if (isArrayField(fi)) {
+                fieldJPFObjRef = getJPFArrayRef(env, fieldJavaObj);
+              } else {
+                fieldJPFObjRef = JPFObjectFromJavaObject(env, fieldJavaObj);
+              }
+
+              newObjEI.setReferenceField(fi, fieldJPFObjRef);
+
+            } catch (NoSuchFieldException nsfx){
+              throw new JPFException("JPF object creation failed, no such field: " + fi.getFullName(), nsfx);
+            } catch (IllegalAccessException iax){
+              throw new JPFException("JPF object creation failed, illegal access: " + fi.getFullName(), iax);
+            }
+          }
+        }
+
+        ci = ci.getSuperClass();
+      }
+
+      return newObjRef;
+  }
+
+  private Object createObject(String className) {
+    return null;
+  }
+
+  private static void setJPFPrimitive(ElementInfo newObjEI, FieldInfo fi, Object javaObject) {
+    try {
+
+      String jpfTypeName = fi.getType();
+      Class javaClass = javaObject.getClass();
+      Field javaField = getField(fi.getName(), javaClass);
+      javaField.setAccessible(true);
+
+      if (jpfTypeName.equals("char")) {
+        newObjEI.setCharField(fi, javaField.getChar(javaObject));
+      }
+      else if (jpfTypeName.equals("byte")) {
+        newObjEI.setByteField(fi, javaField.getByte(javaObject));
+      }
+      else if (jpfTypeName.equals("short")) {
+        newObjEI.setShortField(fi, javaField.getShort(javaObject));
+      }
+      else if (jpfTypeName.equals("int")) {
+        newObjEI.setIntField(fi, javaField.getInt(javaObject));
+      }
+      else if (jpfTypeName.equals("long")) {
+        newObjEI.setLongField(fi, javaField.getLong(javaObject));
+      }
+      else if (jpfTypeName.equals("float")) {
+        newObjEI.setFloatField(fi, javaField.getFloat(javaObject));
+      }
+      else if (jpfTypeName.equals("double")) {
+        newObjEI.setDoubleField(fi, javaField.getDouble(javaObject));
+      }
+    }
+    catch (Exception ex) {
+      throw new JPFException(ex);
+    }
+  }
+
+  private static Field getField(String fieldName, Class javaClass) throws NoSuchFieldException {
+    while (true) {
+      try {
+        Field field = javaClass.getDeclaredField(fieldName);
+        return field;
+      } catch (NoSuchFieldException ex) {
+        // Try to search this field in a super class
+        javaClass = javaClass.getSuperclass();
+
+        // No more super class. Wrong field
+        if (javaClass == null) {
+          throw ex;
+        }
+      }
+    }
+
+  }
+
+  private static int getJPFArrayRef(MJIEnv env, Object javaArr) throws NoSuchFieldException, IllegalAccessException {
+        
+    Class arrayElementClass = javaArr.getClass().getComponentType();
+
+    int javaArrLength = Array.getLength(javaArr);
+    int arrRef;
+
+    if (arrayElementClass == Character.TYPE) {
+      arrRef = env.newCharArray(javaArrLength);
+      ElementInfo charArrRef = env.getModifiableElementInfo(arrRef);
+      char[] charArr = charArrRef.asCharArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        charArr[i] = Array.getChar(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Byte.TYPE) {
+      arrRef = env.newByteArray(javaArrLength);
+      ElementInfo byteArrRef = env.getModifiableElementInfo(arrRef);
+      byte[] byteArr = byteArrRef.asByteArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        byteArr[i] = Array.getByte(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Short.TYPE) {
+      arrRef = env.newShortArray(javaArrLength);
+      ElementInfo shortArrRef = env.getModifiableElementInfo(arrRef);
+      short[] shortArr = shortArrRef.asShortArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        shortArr[i] = Array.getShort(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Integer.TYPE) {
+      arrRef = env.newIntArray(javaArrLength);
+      ElementInfo intArrRef = env.getModifiableElementInfo(arrRef);
+      int[] intArr = intArrRef.asIntArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        intArr[i] = Array.getInt(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Long.TYPE) {
+      arrRef = env.newLongArray(javaArrLength);
+      ElementInfo longArrRef = env.getModifiableElementInfo(arrRef);
+      long[] longArr = longArrRef.asLongArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        longArr[i] = Array.getLong(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Float.TYPE) {
+      arrRef = env.newFloatArray(javaArrLength);
+      ElementInfo floatArrRef = env.getModifiableElementInfo(arrRef);
+      float[] floatArr = floatArrRef.asFloatArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        floatArr[i] = Array.getFloat(javaArr, i);
+      }
+    }
+    else if (arrayElementClass == Double.TYPE) {
+      arrRef = env.newDoubleArray(javaArrLength);
+      ElementInfo floatArrRef = env.getModifiableElementInfo(arrRef);
+      double[] doubleArr = floatArrRef.asDoubleArray();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        doubleArr[i] = Array.getDouble(javaArr, i);
+      }
+    }
+    else {
+      arrRef = env.newObjectArray(arrayElementClass.getCanonicalName(), javaArrLength);
+      ElementInfo arrayEI = env.getModifiableElementInfo(arrRef);
+
+      Fields fields = arrayEI.getFields();
+
+      for (int i = 0; i < javaArrLength; i++) {
+        int newArrElRef;
+        Object javaArrEl = Array.get(javaArr, i);        
+        
+        if (javaArrEl != null) {
+          if (javaArrEl.getClass().isArray()) {
+            newArrElRef = getJPFArrayRef(env, javaArrEl);
+          }
+          else {
+            newArrElRef = JPFObjectFromJavaObject(env, javaArrEl);
+          }
+        }
+        else {
+          newArrElRef = MJIEnv.NULL;
+        }
+
+        fields.setReferenceValue(i, newArrElRef);
+      }
+    }
+
+    return arrRef;
+  }
+
+  // Do we need this??
+  public static Object javaObjectFromJPFObject(ElementInfo ei) {
+    try {
+      String typeName = ei.getType();
+      Class clazz = ClassLoader.getSystemClassLoader().loadClass(typeName);
+
+      Object javaObject = clazz.newInstance();
+      ClassInfo ci = ei.getClassInfo();
+      while (ci != null) {
+
+        for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
+          if (!fi.isReference()) {
+            setJavaPrimitive(javaObject, ei, fi);
+          }
+        }
+
+        ci = ci.getSuperClass();
+      }
+
+      return javaObject;
+    } catch (Exception ex) {
+      throw new JPFException(ex);
+    }
+  }
+
+  private static void setJavaPrimitive(Object javaObject, ElementInfo ei, FieldInfo fi) throws NoSuchFieldException, IllegalAccessException {
+    String primitiveType = fi.getName();
+    String fieldName = fi.getName();
+
+    Class javaClass = javaObject.getClass();
+    Field javaField = javaClass.getDeclaredField(fieldName);
+    javaField.setAccessible(true);
+
+    if (primitiveType.equals("char")) {
+      javaField.setChar(javaObject, ei.getCharField(fi));
+    }
+    else if (primitiveType.equals("byte")) {
+      javaField.setByte(javaObject, ei.getByteField(fi));
+    }
+    else if (primitiveType.equals("short")) {
+      javaField.setShort(javaObject, ei.getShortField(fi));
+    }
+    else if (primitiveType.equals("int")) {
+      javaField.setInt(javaObject, ei.getIntField(fi));
+    }
+    else if (primitiveType.equals("long")) {
+      javaField.setLong(javaObject, ei.getLongField(fi));
+    }
+    else if (primitiveType.equals("float")) {
+      javaField.setFloat(javaObject, ei.getFloatField(fi));
+    }
+    else if (primitiveType.equals("double")) {
+      javaField.setDouble(javaObject, ei.getDoubleField(fi));
+    }
+    else {
+      throw new JPFException("Can't convert " + primitiveType + " to " +
+              javaField.getClass().getCanonicalName());
+    }
+  }
+
+  private static boolean isArrayField(FieldInfo fi) {
+    return fi.getType().lastIndexOf('[') >= 0;
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjectList.java b/src/main/gov/nasa/jpf/util/ObjectList.java
new file mode 100644 (file)
index 0000000..c9b7a8a
--- /dev/null
@@ -0,0 +1,647 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.SystemAttribute;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.NoSuchElementException;
+
+/**
+ * a minimal container API that transparently handles Object lists which can
+ * degenerate to single values that are stored directly. Value lists are
+ * implemented by means of a private Node type, which is transparently handled
+ * through the (static) ObjectList API 
+ * 
+ * No null objects can be stored in the list. No list can only contain a single
+ * Node object
+ * 
+ * Conversion between single objects and lists is done transparently if you
+ * follow a pattern like:
+ * 
+ *  Object attr; // either a single value or a list
+ *  ..
+ *  attr = ObjectList.remove(attr, v);
+ * 
+ * If there is only one remaining value in a list, the corresponding Node will
+ * be replaced with this value. 
+ * 
+ * iterators are LIFO.
+ * 
+ * We assume that attribute collections are small, otherwise retrieval and
+ * deletion with this API becomes rather inefficient
+ * 
+ * NOTE: while ObjectList heads are stored in simple Object fields within the
+ * user (and therefore could be just overwritten by simple assignments)
+ * YOU SHOULD NOT DO THIS! Other extensions or JPF itself could rely on
+ * current attributes. In case you have to change the whole list, use
+ * set(oldAttrs,newAttr), which checks if there currently is a SystemAttribute
+ * instance in the list, in which case it throws a JPFException unless the
+ * new attibute value is also a gov.nasa.jpf.SystemAttribute instance. Use
+ * forceSet(null) if you really have to remove lists with SystemAttributes  
+ * 
+ * 
+ * usage:
+ *  Object attr;
+ *  ...
+ *    attr = AttrContainer.add( newAttr, attr);
+ * 
+ *    MyAttr a = AttrContainer.getNext( MyAttr.class, attr);
+ * 
+ *    attr = AttrContainer.remove( a, attr);
+ * 
+ *    for (MyAttr a = ObjectList.getFirst(MyAttr.class, attr); a != null;
+ *                a = ObjectList.getNext(MyAttr.class, attr, a)) {..}
+ * 
+ */
+public abstract class ObjectList {
+  
+  // there are no instances, this class is only a static API
+  private ObjectList(){}
+  
+  private static class Node implements Cloneable {
+    Object data;
+    Node next;
+
+    Node(Object data, Node next) {
+      this.data = data;
+      this.next = next;
+    }
+    
+    @Override
+       public boolean equals(Object o){
+      if (o instanceof Node){        
+        Node n = this;
+        Node no = (Node)o;
+        for (; n != null && no != null; n=n.next, no=no.next){
+          if (!n.data.equals(no.data)){
+            return false;
+          }
+        }
+        return (n == null) && (no == null);
+      } else {
+        return false;
+      }
+    }
+    
+    @Override
+       protected Node clone(){
+      try {
+        return (Node)super.clone();
+      } catch (CloneNotSupportedException cnsx){
+        throw new RuntimeException("Node clone failed");
+      }
+    }
+    
+    // recursively clone up to the node with the specified data
+    public Node cloneWithReplacedData (Object oldData, Object newData){
+      Node newThis = clone();
+      
+      if (data.equals(oldData)){
+        newThis.data = newData;
+        
+      } else if (next != null) {
+        newThis.next = next.cloneWithReplacedData(oldData, newData);
+      }
+      
+      return newThis;
+    }
+    
+    public Node cloneWithRemovedData (Object oldData){
+      Node newThis = clone();
+      
+      if (next != null){
+        if (next.data.equals(oldData)){
+          newThis.next = next.next;
+        } else {
+          newThis.next = next.cloneWithRemovedData( oldData);
+        }
+      }
+      
+      return newThis;      
+    }
+  }
+
+  public static class Iterator implements java.util.Iterator<Object>, Iterable<Object> {
+    Object cur;
+    
+    Iterator (Object head){
+      cur = head;
+    }
+    
+    @Override
+       public boolean hasNext() {
+      return cur != null;      
+    }
+
+    @Override
+       public Object next() {
+      if (cur != null){
+        if (cur instanceof Node){
+          Node n = (Node)cur;
+          cur = n.next;
+          return n.data;
+          
+        } else { // single attr value
+          Object n = cur;
+          cur = null;
+          return n;
+        }
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      // we can't remove since that would have to change the head field in the client
+      throw new UnsupportedOperationException();
+    }
+    
+    @Override
+       public java.util.Iterator<Object> iterator(){
+      return this;
+    }
+  }
+  
+  static final Iterator emptyIterator = new Iterator(null);
+  
+  public static Iterator iterator (Object head){
+    if (head == null){
+      return emptyIterator;
+    } else {
+      return new Iterator(head);
+    }
+  }
+  
+  public static class TypedIterator<A> implements java.util.Iterator<A>, Iterable<A> {
+    Object cur;
+    Class<A> type;
+    
+    TypedIterator (Object head, Class<A> type){
+      this.type = type;
+      this.cur = null;
+      
+      if (head instanceof Node){
+        for (Node n = (Node)head; n != null; n = n.next){
+          if (type.isAssignableFrom(n.data.getClass())) {
+            cur = n;
+            break;
+          }
+        }
+      } else if (head != null) {
+        if (type.isAssignableFrom(head.getClass())) {
+          cur = head;
+        }
+      }
+    }
+    
+    @Override
+       public boolean hasNext() {
+      return cur != null;      
+    }
+
+    @Override
+       public A next() {
+      
+      if (cur != null){
+        if (cur instanceof Node){
+          Node nCur = (Node)cur;
+          cur = null;
+          A d = (A)nCur.data;
+          
+          for (Node n=nCur.next; n != null; n=n.next){
+            if (type.isAssignableFrom(n.data.getClass())){
+              cur = n;
+              break;
+            }
+          }
+          
+          return d;
+          
+        } else { // single attr value
+          A d = (A)cur;
+          cur = null;
+          return d;
+        }
+        
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      // we can't remove since that would have to change the head field in the client
+      throw new UnsupportedOperationException();
+    }
+    
+    @Override
+       public java.util.Iterator<A> iterator(){
+      return this;
+    }
+  }
+  
+  static final TypedIterator<Object> emptyTypedIterator = new TypedIterator<Object>(null,Object.class);
+  
+  public static <A> TypedIterator<A> typedIterator (Object head, Class<A> type){
+    if (head == null){
+      return (TypedIterator<A>) emptyTypedIterator;
+    } else {
+      return new TypedIterator<A>(head, type);
+    }
+  }
+  
+  /**
+   * this returns either the first value if there is only one element, or
+   * a Node list containing all the values in the order they are provided 
+   * 
+   * note - elements in the list occur in order of arguments, whereas normal
+   * add() always adds at the head of the list
+   */
+  public static Object createList(Object... values){
+    if (values.length == 0){
+      return null;
+      
+    } else if (values.length == 1){
+      return values[0];
+      
+    } else {
+      Node node = null, next = null;
+
+      for (int i=values.length-1; i>=0; i--){
+        node = new Node(values[i], next);
+        next = node;
+      }
+      return node;
+    }
+  }
+    
+  public static Object valueOf (Object o){
+    return (o instanceof Node) ? ((Node)o).data : o;
+  }
+    
+  public static Object set (Object head, Object newHead){
+    if (head == null || newHead instanceof SystemAttribute){
+      return newHead; // Ok to overwrite
+      
+    } else {
+      if (head instanceof Node){
+        // check if there is any SystemAttribute in the list
+        for (Node n = (Node)head; n != null; n = n.next){
+          if (n.data instanceof SystemAttribute){
+            throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
+          }
+        }
+        
+        return newHead; // Ok to overwrite
+        
+      } else { // single data attribute
+        if (head instanceof SystemAttribute){
+          throw new JPFException("attempt to overwrite SystemAttribute with " + newHead);
+        } else {
+          return newHead; // Ok to overwrite
+        }
+      }
+    }
+  }
+  
+  /**
+   * just to provide a way to overwrite SystemAttributes (e.g. with null)
+   */
+  public static Object forceSet (Object head, Object newHead){
+    return newHead;
+  }
+  
+  
+  public static Object add (Object head, Object data){
+    if (head == null){
+      return data;
+      
+    } else if (data == null){
+      return head;
+      
+    } else {
+      if (head instanceof Node){
+        return new Node(data, (Node)head);
+        
+      } else { // was single value -> turn into list
+        Node p = new Node(head,null);
+        return new Node(data, p);
+      }
+    }
+  }
+  
+  public static Object replace (Object head, Object oldData, Object newData){
+    if (oldData == null){
+      return head;
+    }
+    if (newData == null){
+      return remove(head, oldData); // no null data, remove oldData
+    }
+    
+    if (head instanceof Node){
+      // <2do> perhaps we should first check if it is there
+      return ((Node)head).cloneWithReplacedData(oldData, newData);
+      
+    } else { // single object
+      if (oldData.equals(head)){
+        return newData;
+      } else {
+        return head;
+      }
+    }
+  }
+  
+  public static Object remove (Object head, Object data){
+    if (head == null || data == null){
+      return head;  
+    }
+
+    if (head instanceof Node) {
+      Node nh = (Node) head;
+      
+      Node nhn = nh.next;
+      if (nhn != null && nhn.next == null) { // 2 node case - reduce if found
+        if (nh.data.equals(data)) {
+          return nhn.data;
+        } else if (nhn.data.equals(data)) {
+          return nh.data;
+        } else { // not there
+          return head;
+        }
+      }
+      
+      return nh.cloneWithRemovedData(data);
+      
+    } else { // single object
+      if (head.equals(data)){
+        return null;
+      } else {
+        return head;
+      }
+    }
+  }
+  
+  public static boolean contains (Object head, Object o){
+    if (head == null || o == null){
+      return false;
+      
+    } else if (head instanceof Node){
+      for (Node n = (Node)head; n != null; n = n.next){
+        if (o.equals(n.data)){
+          return true;
+        }
+      }
+      return false;
+            
+    } else {
+      return o.equals(head);
+    }
+  }
+  
+  public static boolean containsType (Object head, Class<?> type){
+    if (head == null || type == null){
+      return false;
+      
+    } else if (head instanceof Node){
+      for (Node n = (Node)head; n != null; n = n.next){
+        if (type.isAssignableFrom(n.data.getClass())){
+          return true;
+        }
+      }
+      return false;
+            
+    } else {
+      return type.isAssignableFrom(head.getClass());
+    }
+  }
+  
+  //--- various qualifiers
+
+  public static boolean isList (Object head){
+    return (head instanceof Node);
+  }
+  
+  public static boolean isEmpty(Object head){
+    return head == null;
+  }
+  
+  public static int size(Object head){
+    int len = 0;
+    
+    if (head instanceof Node){
+      for (Node n = (Node) head; n != null; n = n.next) {
+        len++;
+      }    
+    } else {
+      if (head != null){
+        len = 1;
+      }
+    }
+    
+    return len;
+  }
+  
+  public static int numberOfInstances (Object head, Class<?> type){
+    int len = 0;
+    
+    if (head instanceof Node){
+      for (Node n = (Node) head; n != null; n = n.next) {
+        if (type.isInstance(n.data)){
+          len++;
+        }
+      }    
+    } else {
+      if (head != null){
+        if (type.isInstance(head)){
+          len = 1;
+        }
+      }
+    }
+    
+    return len;
+    
+  }
+  
+  public static Object get (Object head, int idx){
+    if (head instanceof Node){
+      int i=0;
+      for (Node n = (Node) head; n != null; n = n.next) {
+        if (i++ == idx){
+          return n.data;
+        }
+      }    
+    } else {
+      if (idx == 0){
+        return head;
+      }
+    }
+    
+    return null;
+  }
+  
+  public static Object getFirst(Object head){
+    if (head instanceof Node){
+      return ((Node)head).data;
+    } else {
+      return head;
+    }
+  }
+  
+  public static Object getNext(Object head, Object prevData){
+    if (head instanceof Node){
+      Node n = (Node)head;
+      if (prevData != null){
+        for (; n != null && n.data != prevData; n=n.next);
+        if (n == null){
+          return null;
+        } else {
+          n = n.next;
+        }
+      }
+      
+      return n.data;
+      
+    } else {
+      if (prevData == null){
+        return head;
+      }
+    }
+    
+    return null;    
+  }
+  
+  public static <A> A getFirst (Object head, Class<A> type){
+    if (head != null){
+      if (type.isAssignableFrom(head.getClass())) {
+        return (A) head;
+      }
+
+      if (head instanceof Node) {
+        for (Node n = (Node) head; n != null; n = n.next) {
+          if (type.isAssignableFrom(n.data.getClass())) {
+            return (A) n.data;
+          }
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  public static <A> A getNext (Object head, Class<A> type, Object prevData){
+    if (head instanceof Node){
+      Node n = (Node)head;
+      if (prevData != null){
+        for (; n != null && n.data != prevData; n=n.next);
+        if (n == null){
+          return null;
+        } else {
+          n = n.next;
+        }
+      }
+      
+      for (; n != null; n = n.next) {
+        if (type.isAssignableFrom(n.data.getClass())) {
+          return (A) n.data;
+        }
+      }
+      
+    } else if (head != null) {
+      if (prevData == null){
+        if (type.isAssignableFrom(head.getClass())){
+          return (A)head;
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  public static void hash (Object head, HashData hd){
+    if (head instanceof Node){
+      for (Node n = (Node) head; n != null; n = n.next) {
+        hd.add(n.data);
+      }
+            
+    } else if (head != null){
+      hd.add(head);
+    }    
+  }
+  
+  public static boolean equals( Object head1, Object head2){
+    if (head1 != null){
+      return head1.equals(head2);
+    } else {
+      return head2 == null; // both null is treated as equal      
+    }
+  }
+  
+  static Object cloneData (Object o) throws CloneNotSupportedException {
+    if (o instanceof CloneableObject) {
+      CloneableObject co = (CloneableObject) o;
+      return co.clone();
+      
+    } else if (o != null) {
+      Class<?> cls = o.getClass();
+      try {
+        Method m = cls.getMethod("clone");
+        // it can't be static because this would mask Object.clone()
+        // since Class.getMethod() only returns publics, we don't have to set accessible
+        return m.invoke(o);
+        
+      } catch (NoSuchMethodException nsmx){
+        // since Object.clone() would throw it (unless this is a Cloneable, in which
+        // case there most probably is a public clone() and we would not have
+        // gotten here), there is no use trying to call it
+        throw new CloneNotSupportedException("no public clone(): " + o);
+      } catch (InvocationTargetException ix){
+        throw new RuntimeException( "generic clone failed: " + o, ix.getCause());
+      } catch (IllegalAccessException iax){
+        throw new RuntimeException("clone() not accessible: " + o);
+      }
+      
+    } else {
+      return null;
+    }
+  }
+  
+  static Node cloneNode (Node n) throws CloneNotSupportedException {
+    if (n == null){
+      return null;
+    } else {
+      return new Node( cloneData(n.data), cloneNode(n.next));
+    }
+  }
+    
+  public static Object clone (Object head) throws CloneNotSupportedException {
+    if (head instanceof Node){
+      return cloneNode( (Node)head);
+            
+    } else if (head != null){
+      return cloneData( head);
+      
+    } else {
+      return null;
+    }
+    
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjectQueue.java b/src/main/gov/nasa/jpf/util/ObjectQueue.java
new file mode 100644 (file)
index 0000000..a5d4d91
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * generic interface for object queues
+ */
+public interface ObjectQueue<E> extends Iterable<E> {
+
+  boolean isEmpty();
+  
+  void clear();
+  
+  int size();
+  
+  boolean offer (E e);
+  boolean add (E e);
+  
+  E peek();
+  
+  E poll();
+  E remove() throws NoSuchElementException;
+  
+  void process (Processor<E> processor);
+}
diff --git a/src/main/gov/nasa/jpf/util/ObjectSet.java b/src/main/gov/nasa/jpf/util/ObjectSet.java
new file mode 100644 (file)
index 0000000..3a20c11
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * generic interface for object sets
+ * this one makes no guarantees about identity or value comparison
+ */
+public interface ObjectSet<E> extends Cloneable {
+
+  boolean add (E e);
+  boolean contains (E e);
+  boolean remove (E e);
+  
+  int size();
+  
+  ObjectSet<E> clone();
+}
diff --git a/src/main/gov/nasa/jpf/util/PSIntMap.java b/src/main/gov/nasa/jpf/util/PSIntMap.java
new file mode 100644 (file)
index 0000000..8f80b16
--- /dev/null
@@ -0,0 +1,1799 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Persistent (immutable) associative array that maps integer keys to generic reference values.
+ * <p>
+ * PSIntMap is implemented as a bitwise trie which processes key bits in msb order
+ * (from left to right) and has the same depth along all paths (i.e. values are only kept at the
+ * terminal node level, which corresponds to the rightmost bit block in the key).
+ * 
+ * This particular implementation was chosen to optimize performance for dense key value domains,
+ * e.g. keys that are computed from counters. More specifically, PSIntMap was designed to be a
+ * suitable basis for JPF Heap implementations with their characteristic usage pattern:
+ *   ..
+ *   transition{ ..alloc( n),..alloc(n+1),..alloc(n+2), ..}, garbage-collection{ remove(x),remove(y),..}
+ *   ..
+ * 
+ * The 32bit keys are broken up into 5bit blocks that represent the trie levels, each 5bit block
+ * (0..31) being the index for the respective child node or value.
+ * For instance, a key/value pair of 12345->'x' is stored as
+ * <blockquote><pre>
+ *   level:     6    5     4     3     2     1     0
+ *   key:       00.00000.00000.00000.01100.00001.11001  = 12345
+ *   block-val:  0     0     0     0    12     1    25
+ * 
+ *       Node0 (level 2 : nodes)
+ *         ... 
+ *         [12] -> Node1 (level 1 : nodes)
+ *                   ...
+ *                   [1] -> Node2 (level 0 : values)
+ *                            ...
+ *                           [25] -> 'x'
+ *</pre></blockquote>
+ * The main benefit of using this representation is that existing maps are never modified (are
+ * persistent) and hence a previous state can be restored by simply keeping the reference of
+ * the respective map. The main drawback is that not only the changed value has to be stored
+ * upon add/remove, but everything from the node that contains this value up to the root node.
+ * 
+ * This implementation partitions keys from left (msb) to right, which has the major property that
+ * consecutive keys are stored in the same node, which in turn allows for efficient caching of
+ * the last modified node. Keeping track of this 'stagingNode' avoids copying anything
+ * but the affected node until the next staging node miss, at which point the old stagingNode
+ * has to be merged. This merge only requires copying of old stagingNode parents up to the
+ * level that already has been copied due to the new key insertion that caused the stagingNode miss).
+ * 
+ * The internal trie representation uses a protected Node type, which uses the bit block values (0..31)
+ * as index into an array that stores either child node references (in case this is not a
+ * terminal block), or value objects (if this is the terminal level). There are three Node
+ * subtypes that get promoted upon population in the following order:
+ * <ul>
+ *  <li>OneNode - store only a single value/child element. Every node starts as a OneNode
+ *  <li>BitmapNode - stores up to 31 elements (compressed)
+ *  <li>FullNode - stores 32 elements
+ * </ul>
+ * Removal of keys leads to symmetric demotion of node types.
+ * 
+ * The five major public operations for PersistentIntMaps are
+ * 
+ * <ol>
+ *  <li>set(int key, V value) -> PersistentIntMap : return a new map with an additional value 
+ *  <li>get(int key) -> V : retrieve value
+ *  <li>remove(int key) -> PersistentIntMap : return a new map without the specified key/value
+ *  <li>removeAllSatisfying(Predicate<V> predicate) -> PersistentIntMap : return a new map
+ *                             without all values satisfying the specified predicate
+ *  <li>process(Processor<V> processor) : iterate over all values with specified processor
+ * </ol>
+ *  
+ * Being a persistent data structure, the main property of PersistentIntMaps is that all
+ * add/remove operations (set,remove,removeAllSatisfying) have to return new PersistenIntMap
+ * instances, no destructive update is allowed. Normal usage patterns therefore look like this:
+ * 
+ * <blockquote><pre>
+ *   PSIntMap<String> map = PSIntMap<String>();
+ *   ..
+ *   map = map.set(42, "fortytwo"); // returns a new map
+ *   ..
+ *   map = map.remove(42); // returns a new map
+ *   ..
+ *   map = map.removeAllSatisfying( new Predicate<String>(){ // returns a new map
+ *     public boolean isTrue (String val){ 
+ *       return val.endsWith("two");
+ *     });
+ *     
+ *   map.process( new Processor<String>(){
+ *     public void process (String val){
+ *       System.out.println(val);
+ *     });
+ * </pre></blockquote>
+ * 
+ * NOTE: bitwise tries are inherently recursive data structures, which would naturally lend
+ * itself to implementations using recursive methods (over nodes). However, the recursion
+ * is always bounded (finite number of key bits), and we need to keep track of the terminal
+ * (value) node that was modified, which means we would have to return two values from
+ * every recursion level (new current level node and new (terminal) stagingNode), thus
+ * requiring additional allocation per map operation ( e.g. 'result' object to keep track
+ * of transient state, as in "node = node.assoc(..key, value, result)") or per recursive call
+ * ( result: {node,stagingNode}, as in "result = node.assoc( ..key, value)"). The first solution
+ * would allow to create/store a result object on the caller site, but this could compromise
+ * map consistency in case of concurrent map operations. Both solutions are counter-productive
+ * in a sense that PSIntMap is optimized to minimize allocation count, which is the crux of
+ * persistent data structures.
+ * 
+ * The approach that is taken here is to manually unroll the recursion by means of explicit
+ * operand stacks, which leads to methods with large number of local variables (to avoid
+ * array allocation) and large switch statements to set respective fields. The resulting
+ * programming style should only be acceptable for critical runtime optimizations.
+ */ 
+
+public class PSIntMap <V> implements Iterable<V> {
+
+  //--- auxiliary types
+  
+  /**
+   * Abstract root class for all node types. This type needs to be internal, no instances
+   * are allowed to be visible outside the PersistentIntMap class hierarchy in order to guarantee
+   * invariant data.
+   * 
+   * NOTE - since this is an internal type, we forego a lot of argument range checks in
+   * the Node subclasses, assuming that all internal use has been tested and bugs will not
+   * cause silent corruption of node data but will lead to follow-on exceptions such as
+   * ArrayIndexOutOfBounds etc.
+   */
+  protected abstract static class Node<E> {
+    
+    abstract E getElementAtLevelIndex (int i);
+    
+    abstract int getNumberOfElements();
+    abstract E getElementAtStorageIndex (int i);
+    
+    abstract int storageToLevelIndex (int i);
+    
+    //--- those clone
+    abstract Node cloneWithAdded (int idx, E e);
+    abstract Node cloneWithReplaced (int idx, E e);
+    abstract Node cloneWithRemoved (int idx);
+    abstract Node removeAllSatisfying (Predicate<E> pred);
+    
+    //--- no clone
+    abstract void set (int idx, E e);
+    abstract void process (int level, Node<E> targetNode, Node<E> stagingNode, Processor<E> p);
+    
+    boolean isEmptyNode(){
+      return false;
+    }
+    
+    //--- debugging
+    void printIndentOn (PrintStream ps, int level) {
+      for (int i=0; i<level; i++) {
+        ps.print("    ");
+      }
+    }
+    
+    void printNodeInfoOn (PrintStream ps, Node targetNode, Node stagingNode) {
+      String clsName = getClass().getSimpleName();
+      int idx = clsName.indexOf('$');
+      if (idx > 0) {
+        clsName = clsName.substring(idx+1);
+      }
+      ps.print(clsName);
+      
+      if (this == targetNode){
+        ps.print( " (target)");
+      }
+    }
+    
+    abstract void printOn(PrintStream ps, int level, Node targetNode, Node stagingNode);
+  }
+  
+  /**
+   * Node that has only one element and hence does not need an array.
+   * If a new element is added, this OneNode gets promoted into a BitmapNode
+   */
+  protected static class OneNode<E> extends Node<E> {
+    E e;
+    int idx;
+    
+    OneNode (int idx, E e){
+      this.idx = idx;
+      this.e = e;
+    }
+
+    @Override
+    int getNumberOfElements(){
+      return 1;
+    }
+    
+    @Override
+    E getElementAtStorageIndex (int i){
+      assert i == 0;
+      return e;
+    }
+    
+    @Override
+    E getElementAtLevelIndex(int i) {
+      if (i == idx){
+        return e;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+    int storageToLevelIndex (int i){
+      if (i == 0){
+        return idx;
+      }
+      return -1;
+    }
+    
+    /**
+     * this assumes the index is not set 
+     */
+    @Override
+    Node cloneWithAdded(int i, E newElement) {
+      assert i != idx;
+      
+      Object[] a = new Object[2];
+      
+      if (i < idx){
+        a[0] = newElement;
+        a[1] = e;
+      } else {
+        a[0] = e;
+        a[1] = newElement;
+      }
+      int bitmap = (1 << idx) | (1 << i);
+      
+      return new BitmapNode(bitmap, a);
+    }
+
+    /**
+     * this assumes the index is set 
+     */
+    @Override
+    Node cloneWithReplaced(int i, E e) {
+      assert i == idx;
+      return new OneNode( i, e);
+    }
+
+    /**
+     * this assumes the index is set 
+     */
+    @Override
+    Node cloneWithRemoved(int i){
+      assert (i == idx);
+      return null;
+    }
+    
+    @Override
+    Node removeAllSatisfying (Predicate<E> pred){
+      if (pred.isTrue(e)){
+        return null;
+      } else {
+        return this;
+      }
+    }
+    
+    @Override
+    void set (int i, E e){
+      assert i == idx;
+      this.e = e;
+    }
+    
+    @Override
+    boolean isEmptyNode(){
+      return idx == 0;
+    }
+    
+    @Override
+    void process (int level, Node<E> targetNode, Node<E> stagingNode, Processor<E> p){
+      if (level == 0){
+        if (this == targetNode){
+          stagingNode.process( 0, null, null, p);
+        } else {
+          p.process(e);
+        }
+      } else {
+        ((Node)e).process( level-1, targetNode, stagingNode, p);
+      }
+    }
+    
+    @Override
+       public void printOn (PrintStream ps, int depth, Node targetNode, Node stagingNode) {
+      printIndentOn(ps, depth);
+      ps.printf("%2d: ", idx);
+
+      if (e instanceof Node) {
+        Node<E> n = (Node<E>) e;
+        n.printNodeInfoOn(ps, targetNode, stagingNode);
+        ps.println();
+        n.printOn(ps, depth+1, targetNode, stagingNode);
+      } else {
+        ps.print("value=");
+        ps.println(e);
+      }
+    }
+
+  }
+  
+  /**
+   * A node that holds between 2 and 31 elements.
+   * 
+   * We use bitmap based element array compaction - the corresponding bit block of the key
+   * [0..31] is used as an index into a bitmap. The elements are stored in a dense
+   * array at indices corresponding to the number of set bitmap bits to the right of the
+   * respective index in the bitmap, e.g. for
+   * 
+   * <blockquote><pre> 
+   *   key = 289 =  0b01001.00001, shift = 5, assuming node already contains key 97 = 0b00011.00001 =>
+   *     idx = (key >>> shift) & 0x1f = 0b01001 = 9
+   *     bitmap =  1000001000  : bit 9 from key 289 (0b01001.), bit 3 from key 97 (0b00011.)
+   *     node element index for key 289 (level index 9) = 1 (one set bit to the right of bit 9)
+   * </pre></blockquote>
+   * 
+   * While storage index computation seems complicated and expensive, there are efficient algorithms to
+   * count leading/trailing bits by means of binary operations and minimal branching, which is
+   * suitable for JIT compilation (see http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup)
+   * 
+   * <p>
+   * If the bit count of a BitmapNode is 2 and an element is removed, this gets demoted into q OneNode.
+   * If the bit count of a BitmapNode is 31 and an element is added, this gets promoted into a FullNode
+   */
+  protected static class BitmapNode<E> extends Node<E> {
+    final E[] elements;
+    final int bitmap;
+    
+    BitmapNode (int idx, E e, E e0){
+      bitmap = (1 << idx) | 1;
+      
+      elements = (E[]) new Object[2];
+      elements[0] = e0;
+      elements[1] = e;
+    }
+    
+    BitmapNode (int bitmap, E[] elements){
+      this.bitmap = bitmap;
+      this.elements = elements;
+    }
+    
+    @Override
+    int getNumberOfElements(){
+      return elements.length;
+    }
+    
+    @Override
+    E getElementAtStorageIndex (int i){
+      return elements[i];
+    }
+    
+    @Override
+    E getElementAtLevelIndex (int i) {
+      int bit = 1 << i;
+      if ((bitmap & bit) != 0) {
+        int idx = Integer.bitCount( bitmap & (bit-1));
+        return elements[idx];
+      } else {
+        return null;
+      }
+    }
+
+    /**
+     * get the position of the (n+1)'th set bit in bitmap
+     */
+    @Override
+    int storageToLevelIndex (int n){
+      int v = bitmap;
+      /**/
+      switch (n){
+        case 30: v &= v-1;
+        case 29: v &= v-1;
+        case 28: v &= v-1;
+        case 27: v &= v-1;
+        case 26: v &= v-1;
+        case 25: v &= v-1;
+        case 24: v &= v-1;
+        case 23: v &= v-1;
+        case 22: v &= v-1;
+        case 21: v &= v-1;
+        case 20: v &= v-1;
+        case 19: v &= v-1;
+        case 18: v &= v-1;
+        case 17: v &= v-1;
+        case 16: v &= v-1;
+        case 15: v &= v-1;
+        case 14: v &= v-1;
+        case 13: v &= v-1;
+        case 12: v &= v-1;
+        case 11: v &= v-1;
+        case 10: v &= v-1;
+        case 9: v &= v-1;
+        case 8: v &= v-1;
+        case 7: v &= v-1;
+        case 6: v &= v-1;
+        case 5: v &= v-1;
+        case 4: v &= v-1;
+        case 3: v &= v-1;
+        case 2: v &= v-1;
+        case 1: v &= v-1;
+      }
+      /**/
+      
+      /**
+      for (int i=n; i>0; i--){
+        v &= v-1; // remove n-1 least significant bits
+      }
+      **/
+      
+      v = v & ~(v-1); // reduce to the least significant bit
+      return TrailingMultiplyDeBruijnBitPosition[((v & -v) * 0x077CB531) >>> 27];
+    }
+    
+    @Override
+    Node cloneWithAdded(int i, E e) {
+      int bit = 1 << i;
+      int idx = Integer.bitCount( bitmap & (bit -1));
+      
+      if (elements.length == 31){
+        Object[] a = new Object[32];
+
+        if (idx > 0) {
+          System.arraycopy(elements, 0, a, 0, idx);
+        }
+        if (idx < 31) {
+          System.arraycopy(elements, idx, a, idx + 1, 31 - idx);
+        }
+        a[idx] = e;
+        return new FullNode(a);
+        
+      } else {
+        int n = elements.length;
+        Object[] a = new Object[n + 1];
+
+        if (idx > 0) {
+          System.arraycopy(elements, 0, a, 0, idx);
+        }
+
+        a[idx] = e;
+
+        if (n > idx) {
+          System.arraycopy(elements, idx, a, idx + 1, (n - idx));
+        }
+      
+        return new BitmapNode( bitmap | bit, a);
+      }
+    }
+
+    @Override
+    Node cloneWithReplaced(int i, E e) {
+      int idx = Integer.bitCount( bitmap & ((1<<i) -1));
+      
+      E[] a = elements.clone();
+      a[idx] = e;
+      
+      return new BitmapNode( bitmap, a);
+    }
+    
+    @Override
+    Node cloneWithRemoved(int i){
+      int bit = (1<<i);
+      int idx = Integer.bitCount( bitmap & (bit-1));
+      int n = elements.length;
+      
+      if (n == 2){
+        E e = (idx == 0) ? elements[1] : elements[0]; // the remaining value
+        int i0 = Integer.numberOfTrailingZeros(bitmap ^ bit);
+        return new OneNode( i0, e);
+        
+      } else {
+        Object[] a = new Object[n - 1];
+        if (idx > 0) {
+          System.arraycopy(elements, 0, a, 0, idx);
+        }
+        n--;
+        if (n > idx) {
+          System.arraycopy(elements, idx + 1, a, idx, (n - idx));
+        }
+        return new BitmapNode(bitmap ^ bit, a);
+      }
+    }
+    
+    @Override
+    Node removeAllSatisfying (Predicate<E> pred){
+      int newBitmap = bitmap;
+      int len = elements.length;
+      int newLen = len;
+      E[] elem = elements;
+      int removed = 0;
+      
+      for (int i=0, bit=1; i<len; i++, bit<<=1){
+        while ((newBitmap & bit) == 0){
+          bit <<= 1;
+        }
+        
+        if (pred.isTrue(elem[i])){
+          newBitmap ^= bit;
+          newLen--;
+          removed |= (1 << i);
+        } 
+      }
+      
+      if (newLen == 0){ // nothing left
+        return null;
+        
+      } else if (newLen == len){ // nothing removed
+        return this;
+        
+      } else if (newLen == 1) { // just one value left - reduce to OneNode
+        int i = Integer.bitCount( bitmap & (newBitmap -1));
+        int idx = Integer.numberOfTrailingZeros(newBitmap);
+        return new OneNode<E>( idx, elem[i]);
+        
+      } else { // some values removed - reduced BitmapNode
+        E[] newElements = (E[]) new Object[newLen];
+        for (int i=0, j=0; j<newLen; i++){
+          if ((removed & (1<<i)) == 0){
+            newElements[j++] = elem[i];
+          }
+        }
+        return new BitmapNode( newBitmap, newElements);
+      }
+    }
+
+    
+    @Override
+    void set (int i, E e){
+      int idx = Integer.bitCount( bitmap & ((1<<i) -1));
+      elements[idx] = e;
+    }
+    
+    @Override
+    void process (int level, Node<E> targetNode, Node<E> stagingNode, Processor<E> p){
+      if (level == 0){
+        if (this == targetNode){
+          stagingNode.process(0, null, null, p);
+        } else {
+          for (int i = 0; i < elements.length; i++) {
+            p.process(elements[i]);
+          }
+        }
+      } else {
+        for (int i=0; i<elements.length; i++){
+          ((Node)elements[i]).process(level-1, targetNode, stagingNode, p);
+        }        
+      }
+    }
+    
+    @Override
+       void printOn (PrintStream ps, int depth, Node targetNode, Node stagingNode) {
+      int j=0;
+      for (int i=0; i<32; i++) {
+        if ((bitmap & (1<<i)) != 0) {
+          printIndentOn(ps, depth);
+          ps.printf("%2d: ", i);
+          
+          E e = elements[j++];
+          if (e instanceof Node) {
+            Node<E> n = (Node<E>)e;
+            n.printNodeInfoOn(ps, targetNode, stagingNode);
+            ps.println();
+            n.printOn(ps, depth+1, targetNode, stagingNode);
+          } else {
+            ps.print("value=");
+            ps.println(e);
+          }
+        }
+      }
+    }
+
+  }
+
+  /**
+   * newElements node with 32 elements, for which we don't need newElements bitmap.
+   * No element can be added since this means we just promote an existing element
+   * If an element is removed, this FullNode gets demoted int newElements BitmapNode
+   */
+  protected static class FullNode<E> extends Node<E> {
+    final E[] elements;
+
+    FullNode (E[] elements){
+      this.elements = elements;
+    }
+    
+    @Override
+    int getNumberOfElements(){
+      return 32;
+    }
+    
+    @Override
+    E getElementAtStorageIndex (int i){
+      return elements[i];
+    }
+    
+    @Override
+    E getElementAtLevelIndex (int i) {
+      return elements[i];
+    }
+
+    @Override
+    int storageToLevelIndex (int i){
+      return i;
+    }
+
+    
+    @Override
+    Node cloneWithAdded (int idx, E e){
+      throw new RuntimeException("can't add a new element to a FullNode");
+    }
+    
+    @Override
+    Node cloneWithReplaced (int idx, E e){
+      E[] newElements = elements.clone();
+      newElements[idx] = e;
+      return new FullNode(newElements);
+    }
+    
+    @Override
+    Node cloneWithRemoved(int idx){
+      Object[] a = new Object[31];
+      int bitmap = 0xffffffff ^ (1 << idx);
+      
+      if (idx > 0){
+        System.arraycopy(elements, 0, a, 0, idx);
+      }
+      if (idx < 31){
+        System.arraycopy(elements, idx+1, a, idx, 31-idx);
+      }
+      
+      return new BitmapNode( bitmap, a);
+    }
+    
+    @Override
+    Node removeAllSatisfying (Predicate<E> pred){
+      int newBitmap = 0xffffffff;
+      int newLen = 32;
+      E[] elem = elements;
+      int removed = 0;
+      
+      for (int i=0, bit=1; i<32; i++, bit<<=1){
+        if (pred.isTrue(elem[i])){
+          newBitmap ^= bit;
+          newLen--;
+          removed |= (1 << i);
+        } 
+      }
+      
+      if (newLen == 0){ // nothing left
+        return null;
+        
+      } else if (newLen == 32){ // nothing removed
+        return this;
+        
+      } else if (newLen == 1) { // just one value left - reduce to OneNode
+        int idx = Integer.numberOfTrailingZeros(newBitmap);  
+        return new OneNode<E>( idx, elem[idx]);
+        
+      } else { // some values removed - reduced BitmapNode
+        E[] newElements = (E[]) new Object[newLen];
+        for (int i=0, j=0; j<newLen; i++){
+          if ((removed & (1<<i)) == 0){
+            newElements[j++] = elem[i];
+          }
+        }
+        return new BitmapNode( newBitmap, newElements);
+      }
+    }
+    
+    @Override
+    void set (int i, E e){
+      elements[i] = e;
+    }
+    
+    @Override
+    void process (int level, Node<E> targetNode, Node<E> stagingNode, Processor<E> p){
+      if (level == 0){
+        if (this == targetNode){
+          stagingNode.process(0, null, null, p);
+        } else {
+          for (int i = 0; i < elements.length; i++) {
+            p.process(elements[i]);
+          }
+        }
+      } else {
+        for (int i=0; i<elements.length; i++){
+          ((Node)elements[i]).process(level-1, targetNode, stagingNode, p);
+        }        
+      }
+    }
+    
+    @Override
+       void printOn (PrintStream ps, int depth, Node targetNode, Node stagingNode) {    
+      for (int i=0; i<32; i++) {
+        printIndentOn(ps, depth);
+        ps.printf("%2d: ", i);
+
+        E e = elements[i];
+        if (e instanceof Node) {
+          Node<E> n = (Node<E>) e;
+          n.printNodeInfoOn(ps, targetNode, stagingNode);
+          ps.println();
+          n.printOn(ps, depth+1, targetNode, stagingNode);
+        } else {
+          ps.print("value=");
+          ps.println(e);
+        }
+      }
+    }
+  }
+
+  @Override
+@SuppressWarnings({ "rawtypes", "unchecked" })
+  public Iterator<V> iterator(){
+    return new ValueIterator();
+  }
+  
+  /**
+   * this is less efficient than using map.process(processor), but required to use PSIntMaps in lieu of ordinary containers
+   * Since PSIntMaps are bounded recursive data structures, we have to model newElements stack explicitly, but at least we know it is
+   * not exceeding newElements depth of 6 (5 bit index blocks)
+   * 
+   * Note - there are no empty nodes. Each one has at least newElements single child node or value
+   */
+  protected class ValueIterator implements Iterator<V> {
+
+    Node node;
+    int nodeIdx, maxNodeIdx;
+    
+    Node[] parentNodeStack;
+    int[] parentIdxStack;
+    int top;
+    int nVisited, nTotal;
+    
+    
+    @SuppressWarnings("unchecked")
+    public ValueIterator (){
+      node = PSIntMap.this.rootNode;
+      if (node != null) {
+        if (node == PSIntMap.this.targetNode){
+          node = PSIntMap.this.stagingNode;
+        }
+        
+        maxNodeIdx = node.getNumberOfElements();
+        
+        // nodeIdx = 0;
+        // nVisited = 0;
+        // top = 0;
+        
+        int depth = PSIntMap.this.rootLevel;
+        parentNodeStack = new Node[depth];
+        parentIdxStack = new int[depth];
+            
+        nTotal = PSIntMap.this.size;
+      }
+    }
+    
+    @Override
+    public boolean hasNext() {
+      return nVisited < nTotal;
+    }
+
+    @Override
+    @SuppressWarnings("unchecked")
+    public V next() {
+      if (nVisited >= nTotal) {
+        throw new NoSuchElementException();
+      }
+      
+      int idx = nodeIdx;
+      Object nv = node.getElementAtStorageIndex( idx);
+      
+      //--- descend
+      while (top < PSIntMap.this.rootLevel) {
+        parentNodeStack[top] = node; // push current node on stack
+        parentIdxStack[top] = idx;
+        top++;
+        
+        if (nv == PSIntMap.this.targetNode){
+          node = PSIntMap.this.stagingNode;
+        } else {        
+          node = (Node)nv;
+        }
+        
+        idx = nodeIdx = 0;
+        maxNodeIdx = node.getNumberOfElements();
+        
+        nv = node.getElementAtStorageIndex(0);
+      }
+      
+      //--- newElements value, finally
+      nVisited++;
+      idx++;
+
+      if (idx == maxNodeIdx) { // done, no more child nodes/values for this node
+        while (top > 0) { // go up
+          top--;
+          node = parentNodeStack[top];
+          nodeIdx = parentIdxStack[top] + 1;
+          maxNodeIdx = node.getNumberOfElements();
+          if (nodeIdx < maxNodeIdx) break;
+        }
+      } else {
+        nodeIdx = idx;
+      }
+
+      //assert (nVisited == nTotal) || (nodeIdx < maxNodeIdx);
+      return (V) nv;
+    }
+
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException("PersistentIntMap iterators don't support removal");
+    }
+    
+  }
+
+  
+  //--- auxiliary data and functions
+  
+  static final int BASE_MASK = ~0x1f;
+  
+  static final int TrailingMultiplyDeBruijnBitPosition[] = {
+    0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, 
+    31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
+  };
+  
+  static int getNumberOfTrailingZeros (int v){
+    return TrailingMultiplyDeBruijnBitPosition[((v & -v) * 0x077CB531) >>> 27];
+  }
+  
+  
+  // the values are the respective block levels
+  static final int LeadingMultiplyDeBruijnBitPosition[] = {
+    0, 1, 0, 2, 2, 4, 0, 5, 2, 2, 3, 3, 4, 5, 0, 6,
+    1, 2, 4, 5, 3, 3, 4, 1, 3, 5, 4, 1, 5, 1, 0, 6
+  };
+  
+  /**
+   * get the start level [0..7] for the highest bit index (bit block). This is essentially counting the number of leading zero bits,
+   * which we can derive from http://graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup
+   */
+  static int getStartLevel (int v){
+    v |= v >>> 1;
+    v |= v >>> 2;
+    v |= v >>> 4;
+    v |= v >>> 8;
+    v |= v >>> 16;
+
+    return LeadingMultiplyDeBruijnBitPosition[(v * 0x07C4ACDD) >>> 27];
+  }
+  
+  //--- instance data
+  
+  final protected int size;       // number of values in this map
+  final protected int rootLevel;  // bit block level of the root node (highest non-0 bit block of all keys in map)
+  final protected Node rootNode;  // topmost node of trie
+  
+  /*
+   * the following fields are used to cache consecutive key operations with the goal of avoiding
+   * path copies from the modified value node all the way up to the root node. As long as the same value
+   * node is modified (hence msb key block traversal) we just need to keep track which position in the
+   * trie the stagingNode refers to (stagingNodeMask), and only have to create a new stagingNode with the
+   * updated values. Once we have a key operation that refers to a different value node position (staging miss),
+   * we merge the old stagingNode back into the trie. If we do this after inserting the new key, only
+   * nodes from the old stagingNode parent up to the first node that is on the new key path have to be copied,
+   * the merge node (on the new stagingNode path) can be safely modified since it has only been created during
+   * the ongoing map operation. Example:
+   *                                          key    value
+   * last mod key/value (old stagingNode) : a.c.e -> Y    => stagingNodeMask = a.c.FF
+   * new key/value (new stagingNode)      : a.b.d -> X
+   * 
+   *                            a
+   *                    n0: [...n1...]            root node (level 2)
+   *                           /
+   *                 b    c   /
+   *          n1:  [.n2...n3.]                    
+   *                /       \
+   *           d   /         \                                                  e
+   *    n2:  [.X..]      n3:  [.....]             value nodes (level 0)     [...Y...]
+   *      new stagingNode       old targetNode  <-------------------------- old stagingNode
+   *    (= new targetNode)
+   * 
+   * In this case, the sequence of operations is as follows:
+   * <ol>
+   *   <li> insert new key/value pair (a.b.d)->X into the trie, which is a stagingNode miss since
+   *        stagingNodeMasks are different (a.b.FF != a.c.FF). This leads to copied/new nodes n2,n1,n0
+   *   <li> check if old stagingNode differs from targetNode (had several consecutive modifications), if
+   *        targetNode != stagingNode then merge old stagingNode <em>after</em> n2,n1,n0 creation
+   *   <li> since n1 is already a new node that is not shared with any prior version of this map,
+   *        its [c] element can be simply set to the old stagingNode, i.e. the merge does not require
+   *        any additional allocation. Note that n1 has to contain a [c] element since we always link
+   *        new stagingNodes into the trie upon creation. This means the number of elements in n1
+   *        (and hence the node type) does not change, i.e. setting the new [c] element involves
+   *        just a single AASTORE instruction
+   *   <li> set stagingNode = targetNode = n2
+   * </ol>
+   */
+  
+  final protected Node<V> stagingNode; // last modified value node (not linked into the trie upon subsequent modification)
+  final protected int stagingNodeMask; // key mask for stagingNode (key | 0x1f)
+  final protected Node targetNode;     // original stagingNode state that is linked into the trie
+  
+  /**
+   * the only public constructor
+   */
+  public PSIntMap(){
+    this.size = 0;
+    this.rootLevel = 0;
+    this.rootNode = null;
+    this.targetNode = null;
+    this.stagingNode = null;
+    this.stagingNodeMask = 0;
+  }
+  
+  protected PSIntMap (int size, int rootLevel, Node rootNode, Node<V> stagingNode, Node<V> targetNode, int stagingNodeMask){
+    this.size = size;
+    this.rootLevel = rootLevel;
+    this.rootNode = rootNode;
+    this.stagingNode = stagingNode;
+    this.targetNode = targetNode;
+    this.stagingNodeMask = stagingNodeMask;
+  }
+  
+  //--- public API
+  
+  public int size(){
+    return size;
+  }
+  
+  public V get (int key){
+    if (stagingNodeMask == (key | 0x1f)){
+      int idx = key & 0x1f;
+      return stagingNode.getElementAtLevelIndex(idx);
+      
+    } else {
+      if (rootNode == null) return null;
+      
+      int l = getStartLevel(key);
+      if (l > rootLevel) return null;
+      
+      Node<Node> n = rootNode;
+      
+      switch (rootLevel){
+        case 6: 
+          n = n.getElementAtLevelIndex( key >>> 30);
+          if (n == null) return null;
+        case 5:
+          n = n.getElementAtLevelIndex( (key >>> 25) & 0x1f); 
+          if (n == null) return null;
+        case 4:
+          n = n.getElementAtLevelIndex( (key >>> 20) & 0x1f); 
+          if (n == null) return null;
+        case 3:
+          n = n.getElementAtLevelIndex( (key >>> 15) & 0x1f);
+          if (n == null) return null;
+        case 2:
+          n = n.getElementAtLevelIndex( (key >>> 10) & 0x1f);
+          if (n == null) return null;
+        case 1:
+          n = n.getElementAtLevelIndex( (key >>>  5) & 0x1f);
+          if (n == null) return null;
+        case 0: 
+          return ((Node<V>)n).getElementAtLevelIndex(key & 0x1f);
+      }
+      
+      return null; // can't get here
+    }
+  }
+  
+  protected Node mergeStagingNode (){
+    Node<Node> n2=null, n3=null, n4=null, n5=null, n6=null;
+    int i1, i2=0, i3=0, i4=0, i5=0, i6=0;
+    
+    int k = stagingNodeMask;
+    Node<Node> n = rootNode;
+    
+    switch (rootLevel){
+      case 6: 
+        i6 = (k >>> 30);
+        n6 = n; 
+        n = n.getElementAtLevelIndex(i6);
+      case 5:
+        i5 = (k >>> 25) & 0x1f;
+        n5 = n;
+        n = n.getElementAtLevelIndex(i5);
+      case 4:
+        i4 = (k >>> 20) & 0x1f;
+        n4 = n;
+        n = n.getElementAtLevelIndex(i4);
+      case 3:
+        i3 = (k >>> 15) & 0x1f;
+        n3 = n; 
+        n = n.getElementAtLevelIndex(i3);
+      case 2:
+        i2 = (k >>> 10) & 0x1f;
+        n2 = n; 
+        n = n.getElementAtLevelIndex(i2);
+      case 1: 
+        i1 = (k >>> 5) & 0x1f;
+        n = n.cloneWithReplaced(i1, stagingNode);
+        if (n2 != null){
+          n = n2.cloneWithReplaced(i2, n);
+          if (n3 != null){
+            n = n3.cloneWithReplaced(i3, n);
+            if (n4 != null){
+              n = n4.cloneWithReplaced(i4, n);
+              if (n5 != null){
+                n = n5.cloneWithReplaced(i5, n);
+                if (n6 != null){
+                  n = n6.cloneWithReplaced(i6, n);
+                }
+              }
+            }
+          }
+        }
+        return n;
+        
+      case 0:
+        // special case - only node in the trie is the targetNode
+        return stagingNode;
+    }
+    
+    return null; //  can't get here
+  }
+  
+  /**
+   * this relies on that all nodes from the new staging node to the newRootNode have been copied
+   * and can be modified without cloning.
+   * The modification does not change the node type since the old staging/target node was in the trie
+   * The first node where new and old staging indices differ is the mergeNode that needs to be
+   * modified (old staging path node replaced). This has to be level 1..6
+   * Everything above the mergeNode is not modified (the newRootNode does not have to be copied
+   * as it is new)
+   * All nodes between the old stagingNode and the mergeNode have to be copied
+   * The old stagingNode itself does not need to be cloned.
+   */
+  protected void mergeStagingNode (int key, int newRootLevel, Node newRootNode){
+    int k = stagingNodeMask;
+    int mergeLevel = getStartLevel( key ^ k); // block of first differing bit
+    Node<Node> mergeNode = newRootNode;
+    int shift = newRootLevel*5;
+    
+    //--- get the mergeNode
+    for (int l=newRootLevel; l>mergeLevel; l--){
+      int idx = (k >>> shift) & 0x1f;
+      mergeNode = mergeNode.getElementAtLevelIndex(idx);
+      shift -= 5;
+    }
+    int mergeIdx = (k >>> shift) & 0x1f;
+    
+    //--- copy from old staging up to mergeNode
+    Node<Node> n5=null, n4=null, n3=null, n2=null, n1=null;
+    int i5=0, i4=0, i3=0, i2=0, i1=0;
+    Node<Node> n = mergeNode.getElementAtLevelIndex(mergeIdx);
+    
+    switch (mergeLevel-1){ 
+      case 5:        
+        i5 = (k >>> 25) & 0x1f;
+        n5 = n;
+        n = n.getElementAtLevelIndex(i5);
+      case 4:
+        i4 = (k >>> 20) & 0x1f;
+        n4 = n;
+        n = n.getElementAtLevelIndex(i4);
+      case 3:
+        i3 = (k >>> 15) & 0x1f;
+        n3 = n;
+        n = n.getElementAtLevelIndex(i3);
+      case 2:
+        i2 = (k >>> 10) & 0x1f;
+        n2 = n;
+        n = n.getElementAtLevelIndex(i2);
+      case 1:
+        i1 = (k >>> 5) & 0x1f;
+        n1 = n;
+      case 0:
+        n = (Node)stagingNode;
+      
+        if (n1 != null){
+          n = n1.cloneWithReplaced(i1, n);
+          if (n2 != null) {
+            n = n2.cloneWithReplaced(i2, n);
+            if (n3 != null) {
+              n = n3.cloneWithReplaced(i3, n);
+              if (n4 != null) {
+                n = n4.cloneWithReplaced(i4, n);
+                if (n5 != null) {
+                  n = n5.cloneWithReplaced(i5, n);
+                }
+              }
+            }
+          }          
+        }
+    }
+    
+    //--- modify mergeNode
+    mergeNode.set(mergeIdx, n);
+  }
+
+  PSIntMap<V> remove (int key, boolean isTargetNode){
+    Node<Node> n6=null, n5=null, n4=null, n3=null, n2=null, n1=null;
+    Node<V> n0;
+    int i6=0, i5=0, i4=0, i3=0, i2=0, i1=0, i0;
+    
+    Node<Node> n = rootNode;
+    switch (rootLevel){
+      case 6:
+        i6 = (key >>> 30);
+        n5 = n.getElementAtLevelIndex(i6);
+        if (n5 == null){
+          return this; // key not in map
+        } else {
+          n6 = n;
+          n = n5;
+        }
+      case 5:
+        i5 = (key >>> 25) & 0x1f;
+        n4 = n.getElementAtLevelIndex(i5);
+        if (n4 == null){
+          return this; // key not in map
+        } else {
+          n5 = n;
+          n = n4;
+        }
+      case 4:
+        i4 = (key >>> 20) & 0x1f;
+        n3 = n.getElementAtLevelIndex(i4);
+        if (n3 == null){
+          return this; // key not in map
+        } else {
+          n4 = n;
+          n = n3;
+        }
+      case 3:
+        i3 = (key >>> 15) & 0x1f;
+        n2 = n.getElementAtLevelIndex(i3);
+        if (n2 == null){
+          return this; // key not in map
+        } else {
+          n3 = n;
+          n = n2;
+        }
+      case 2:
+        i2 = (key >>> 10) & 0x1f;
+        n1 = n.getElementAtLevelIndex(i2);
+        if (n1 == null){
+          return this; // key not in map
+        } else {
+          n2 = n;
+          n = n1;
+        }
+      case 1:
+        i1 = (key >>> 5) & 0x1f;
+        n0 = n.getElementAtLevelIndex(i1);
+        if (n0 == null){
+          return null;
+        } else {
+          n1 = n;
+          n = (Node)n0;
+        }
+        
+      case 0:
+        n0 = (Node<V>)n;
+        if (isTargetNode){
+          n0 = null;
+        } else {
+          i0 = key & 0x1f;
+          if (n0 == null || n0.getElementAtLevelIndex(i0) == null){
+            return this; // key not in map
+          } else {
+            n0 = n0.cloneWithRemoved(i0);
+          }
+        }
+        n = (Node)n0;
+        if (n1 != null){
+          n = (n == null) ? n1.cloneWithRemoved(i1) : n1.cloneWithReplaced(i1, n);
+          if (n2 != null){
+            n = (n == null) ? n2.cloneWithRemoved(i2) : n2.cloneWithReplaced(i2, n);
+            if (n3 != null){
+              n = (n == null) ? n3.cloneWithRemoved(i3) : n3.cloneWithReplaced(i3, n);
+              if (n4 != null){
+                n = (n == null) ? n4.cloneWithRemoved(i4) : n4.cloneWithReplaced(i4, n);
+                if (n5 != null){
+                  n = (n == null) ? n5.cloneWithRemoved(i5) : n5.cloneWithReplaced(i5, n);
+                  if (n6 != null){
+                    n = (n == null) ? n6.cloneWithRemoved(i6) : n6.cloneWithReplaced(i6, n);
+                  }
+                }
+              }
+            }
+          }
+        }
+        
+        if (n == null){
+          return new PSIntMap<V>();
+          
+        } else {
+          int newRootLevel = rootLevel;
+          int newSb = (n0 == null) ? 0 : (key | 0x1f);
+          
+          while ((newRootLevel > 0) && n.isEmptyNode()){
+            newRootLevel--;
+            n = n.getElementAtLevelIndex(0);
+          }
+          
+          if (!isTargetNode && (stagingNode != targetNode)){
+            mergeStagingNode(key, newRootLevel, n);
+          }
+          
+          return new PSIntMap<V>( size-1, newRootLevel, n, n0, n0, newSb);
+        }
+    }
+    
+    return null; // can't get here
+  }
+  
+  public PSIntMap<V> remove (int key){
+    int newSm = key | 0x1f;
+
+    if (newSm == stagingNodeMask){ // staging node hit - this should be the dominant case
+      int i = key & 0x1f;
+      Node<V> n = stagingNode;
+      if ((n.getElementAtLevelIndex(i)) != null) { // key is in the stagingNode
+        n = n.cloneWithRemoved(i);
+        if (n == null){ // staging node is empty, remove target node
+          return remove(newSm, true);
+        } else { // non-empty staging node, just replace it
+          return new PSIntMap<V>( size-1, rootLevel, rootNode, n, targetNode, newSm);
+        }
+        
+      } else { // key wasn't in the stagingNode
+        return this;
+      }
+      
+    } else { // staging node miss
+      return remove( key, false);
+    }
+  }
+  
+  /**
+   * this either replaces or adds newElements new value 
+   */
+  public PSIntMap<V> set (int key, V value){
+  
+    if (value == null){
+      // we don't store null values, this is a remove in disguise
+      return remove(key);
+    }
+    
+    int newSm = key | 0x1f;
+    
+    if (newSm == stagingNodeMask){ // staging node hit - this should be the dominant case
+      int i = key & 0x1f;
+      Node<V> n = stagingNode;
+      int newSize = size;
+      if ((n.getElementAtLevelIndex(i)) == null) {
+        n = n.cloneWithAdded(i, value);
+        newSize = size+1;
+      } else {
+        n = n.cloneWithReplaced(i, value);
+      }
+      return new PSIntMap<V>( newSize, rootLevel, rootNode, n, targetNode, newSm);
+      
+    } else { // staging node miss
+      int newRootLevel = getStartLevel(key);
+      
+      if (newRootLevel > rootLevel){ // old trie has to be merged in
+        return setInNewRootLevel( newRootLevel, key, value);
+        
+      } else {     // new value can be added to old trie (stagingNode change)
+        return setInCurrentRootLevel( key, value);
+      }
+    }
+  }
+  
+  protected PSIntMap<V> setInNewRootLevel (int newRootLevel, int key, V value){
+    int newSm = key | 0x1f;
+
+    Node<Node> nOld;
+    if (stagingNode != targetNode){
+      nOld = mergeStagingNode();
+    } else {
+      nOld = rootNode;
+    }
+    
+    //--- expand old root upwards
+    if (nOld != null){
+      for (int l = rootLevel + 1; l < newRootLevel; l++) {
+        nOld = new OneNode(0, nOld);
+      }
+    }
+
+    //--- create chain of new value nodes
+    int i = key & 0x1f;
+    Node nNew = new OneNode(i, value);
+    int shift = 5;
+    Node newStagingNode = nNew;
+    for (int l = 1; l < newRootLevel; l++) {
+      i = (key >>> shift) & 0x1f;
+      nNew = new OneNode(i, nNew);
+      shift += 5;
+    }
+
+    //--- create new root
+    i = (key >>> shift); // no remainBmp needed, top level
+    Node<Node> newRootNode = (nOld == null) ? new OneNode( i, nNew) : new BitmapNode<Node>(i, nNew, nOld);
+
+    return new PSIntMap<V>(size + 1, newRootLevel, newRootNode, newStagingNode, newStagingNode, newSm);
+  }  
+  
+  /**
+   * that's ugly, but if we use recursion we need newElements result object to obtain the new stagingNode and
+   * the size change, which means there would be an additional allocation per set() or newElements non-persistent,
+   * transient object that would need synchronization
+   */
+  protected PSIntMap<V> setInCurrentRootLevel (int key, V value){
+    Node<Node> n6=null, n5=null, n4=null, n3=null, n2=null, n1=null;
+    Node<V> n0;
+    int i6=0, i5=0, i4=0, i3=0, i2=0, i1=0, i0;
+    int newSb = key | 0x1f;
+    boolean needsMerge = (targetNode != stagingNode);
+    int newSize = size+1;
+    
+    //--- new stagingNode
+    Node<Node> n = rootNode;
+
+    switch(rootLevel){
+      case 6:
+        i6 = key >>> 30;
+        n5 = n.getElementAtLevelIndex(i6);
+        if (n5 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n1 = new OneNode( (key >>> 5) & 0x1f, n0);
+          n2 = new OneNode( (key >>> 10) & 0x1f, n1);
+          n3 = new OneNode( (key >>> 15) & 0x1f, n2);
+          n4 = new OneNode( (key >>> 20) & 0x1f, n3);
+          n5 = new OneNode( (key >>> 25) & 0x1f, n4);
+          n = n.cloneWithAdded( i6, n5);
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+          
+        } else {
+          n6 = n;
+          n = n5;
+        }
+
+      case 5:
+        i5 = (key >>> 25) & 0x1f;
+        n4 = n.getElementAtLevelIndex(i5);
+        if (n4 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n1 = new OneNode( (key >>> 5) & 0x1f, n0);
+          n2 = new OneNode( (key >>> 10) & 0x1f, n1);
+          n3 = new OneNode( (key >>> 15) & 0x1f, n2);
+          n4 = new OneNode( (key >>> 20) & 0x1f, n3);
+          n = n.cloneWithAdded( i5, n4);
+
+          if (n6 != null){
+            n = n6.cloneWithReplaced( i6, n);
+          }
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+
+        } else {
+          n5 = n;
+          n = n4;
+        }
+
+      case 4:
+        i4 = (key >>> 20) & 0x1f;
+        n3 = n.getElementAtLevelIndex(i4);
+        if (n3 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n1 = new OneNode( (key >>> 5) & 0x1f, n0);
+          n2 = new OneNode( (key >>> 10) & 0x1f, n1);
+          n3 = new OneNode( (key >>> 15) & 0x1f, n2);
+          n = n.cloneWithAdded( i4, n3);
+
+          if (n5 != null){
+            n = n5.cloneWithReplaced( i5, n);
+            if (n6 != null){ 
+              n = n6.cloneWithReplaced( i6, n);
+            }
+          }
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+
+        } else {
+          n4 = n;
+          n = n3;
+        }
+        
+      case 3:
+        i3 = (key >>> 15) & 0x1f;
+        n2 = n.getElementAtLevelIndex(i3);
+        if (n2 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n1 = new OneNode( (key >>> 5) & 0x1f, n0);
+          n2 = new OneNode( (key >>> 10) & 0x1f, n1);
+          n = n.cloneWithAdded( i3, n2);
+
+          if (n4 != null){
+            n = n4.cloneWithReplaced( i4, n);
+            if (n5 != null){
+              n = n5.cloneWithReplaced( i5, n);
+              if (n6 != null){ 
+                n = n6.cloneWithReplaced( i6, n);
+              }
+            }
+          }
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+
+        } else {
+          n3 = n;
+          n = n2;
+        }
+        
+      case 2:
+        i2 = (key >>> 10) & 0x1f;
+        n1 = n.getElementAtLevelIndex(i2);
+        if (n1 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n1 = new OneNode( (key >>> 5) & 0x1f, n0);
+          n = n.cloneWithAdded( i2, n1);
+
+          if (n3 != null){
+            n = n3.cloneWithReplaced( i3, n);
+            if (n4 != null){
+              n = n4.cloneWithReplaced( i4, n);
+              if (n5 != null){
+                n = n5.cloneWithReplaced( i5, n);
+                if (n6 != null){ 
+                  n = n6.cloneWithReplaced( i6, n);
+                }
+              }
+            }
+          }
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+
+        } else {
+          n2 = n;
+          n = n1;
+        }
+      case 1:
+        i1 = (key >>> 5) & 0x1f;
+        n0 = n.getElementAtLevelIndex(i1);
+        if (n0 == null) {
+          n0 = new OneNode( (key & 0x1f), value);
+          n = n.cloneWithAdded( i1, n0);
+
+          if (n2 != null){
+            n = n2.cloneWithReplaced( i2, n);
+            if (n3 != null){
+              n = n3.cloneWithReplaced( i3, n);
+              if (n4 != null){
+                n = n4.cloneWithReplaced( i4, n);
+                if (n5 != null){
+                  n = n5.cloneWithReplaced( i5, n);
+                  if (n6 != null){ 
+                    n = n6.cloneWithReplaced( i6, n);
+                  }
+                }
+              }
+            }
+          }
+          if (needsMerge) mergeStagingNode(key, rootLevel, n);
+          return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+
+        } else {
+          n1 = n;
+          n = (Node)n0;
+        }
+      case 0: // finally the value level
+        i0 = key & 0x1f;
+        n0 = (Node<V>)n;
+        if (n0 != null){
+          if (n0.getElementAtLevelIndex(i0) == null) {
+            n0 = n0.cloneWithAdded(i0, value);
+          } else {
+            n0 = n0.cloneWithReplaced(i0, value);
+            newSize = size;
+          }
+        } else { // first node
+          n0 = new OneNode( i0, value);
+          newSize = 1;
+        }
+        
+        n = (Node)n0;
+        if (n1 != null){
+          n = n1.cloneWithReplaced( i1, n);
+          if (n2 != null){
+            n = n2.cloneWithReplaced( i2, n);
+            if (n3 != null){
+              n = n3.cloneWithReplaced( i3, n);
+              if (n4 != null){
+                n = n4.cloneWithReplaced( i4, n);
+                if (n5 != null){
+                  n = n5.cloneWithReplaced( i5, n);
+                  if (n6 != null){
+                    n = n6.cloneWithReplaced( i6, n);
+                  }
+                }
+              }
+            }
+          } 
+        }
+        if (needsMerge) mergeStagingNode( key, rootLevel, n);
+        return new PSIntMap<V>( newSize, rootLevel, n, n0, n0, newSb);
+    }
+    
+    return null; // can't get here
+  }
+
+  
+  public void process (Processor<V> p){
+    if (rootNode != null){
+      if (targetNode == stagingNode){
+        rootNode.process( rootLevel, null, null, p);
+      } else {
+        rootNode.process( rootLevel, targetNode, stagingNode, p);
+      }
+    }
+  }
+    
+  final protected Node removeAllSatisfying (int level, Node node, Predicate<V> pred){
+    if (level == 0){ // value level
+      return ((Node<V>)node).removeAllSatisfying(pred);
+      
+    } else { // node level
+      // it sucks not having stack arrays but we don't want to allocate for temporary results
+      Node n0=null,n1=null,n2=null,n3=null,n4=null,n5=null,n6=null,n7=null,n8=null,n9=null,n10=null,
+           n11=null,n12=null,n13=null,n14=null,n15=null,n16=null,n17=null,n18=null,n19=null,n20=null,
+           n21=null,n22=null,n23=null,n24=null,n25=null,n26=null,n27=null,n28=null,n29=null,n30=null,n31=null;
+      int nRemaining = 0, nChanged = 0;
+      int len = node.getNumberOfElements();
+      
+      //--- collect the remaining nodes
+      for (int i=0; i<len; i++){
+        Node e = (Node)node.getElementAtStorageIndex(i);
+        Node n = removeAllSatisfying( level-1, e, pred);
+        if (n != null){
+          nRemaining++;
+          if (n != e){
+            nChanged++;
+          }
+          switch (i){
+            case  0:  n0=n; break;
+            case  1:  n1=n; break;
+            case  2:  n2=n; break;
+            case  3:  n3=n; break;
+            case  4:  n4=n; break;
+            case  5:  n5=n; break;
+            case  6:  n6=n; break;
+            case  7:  n7=n; break;
+            case  8:  n8=n; break;
+            case  9:  n9=n; break;
+            case 10: n10=n; break;
+            case 11: n11=n; break;
+            case 12: n12=n; break;
+            case 13: n13=n; break;
+            case 14: n14=n; break;
+            case 15: n15=n; break;
+            case 16: n16=n; break;
+            case 17: n17=n; break;
+            case 18: n18=n; break;
+            case 19: n19=n; break;
+            case 20: n20=n; break;
+            case 21: n21=n; break;
+            case 22: n22=n; break;
+            case 23: n23=n; break;
+            case 24: n24=n; break;
+            case 25: n25=n; break;
+            case 26: n26=n; break;
+            case 27: n27=n; break;
+            case 28: n28=n; break;
+            case 29: n29=n; break;
+            case 30: n30=n; break;
+            case 31: n31=n; break;
+          }
+        }
+      }
+      
+      //--- construct the returned node
+      if (nRemaining == 0){
+        return null;
+        
+      } else if ((nRemaining == len) && (nChanged == 0)){
+        return node;
+        
+      } else {
+        if (nRemaining == 1){ // becomes a OneNode
+          for (int i=0; i<32; i++){
+            switch (i){
+              case  0:  if  (n0!=null) return new OneNode( node.storageToLevelIndex(0), n0); break;
+              case  1:  if  (n1!=null) return new OneNode( node.storageToLevelIndex(1), n1); break;
+              case  2:  if  (n2!=null) return new OneNode( node.storageToLevelIndex(2), n2); break;
+              case  3:  if  (n3!=null) return new OneNode( node.storageToLevelIndex(3), n3); break;
+              case  4:  if  (n4!=null) return new OneNode( node.storageToLevelIndex(4), n4); break;
+              case  5:  if  (n5!=null) return new OneNode( node.storageToLevelIndex(5), n5); break;
+              case  6:  if  (n6!=null) return new OneNode( node.storageToLevelIndex(6), n6); break;
+              case  7:  if  (n7!=null) return new OneNode( node.storageToLevelIndex(7), n7); break;
+              case  8:  if  (n8!=null) return new OneNode( node.storageToLevelIndex(8), n8); break;
+              case  9:  if  (n9!=null) return new OneNode( node.storageToLevelIndex(9), n9); break;
+              case 10:  if (n10!=null) return new OneNode( node.storageToLevelIndex(10),n10); break;
+              case 11:  if (n11!=null) return new OneNode( node.storageToLevelIndex(11),n11); break;
+              case 12:  if (n12!=null) return new OneNode( node.storageToLevelIndex(12),n12); break;
+              case 13:  if (n13!=null) return new OneNode( node.storageToLevelIndex(13),n13); break;
+              case 14:  if (n14!=null) return new OneNode( node.storageToLevelIndex(14),n14); break;
+              case 15:  if (n15!=null) return new OneNode( node.storageToLevelIndex(15),n15); break;
+              case 16:  if (n16!=null) return new OneNode( node.storageToLevelIndex(16),n16); break;
+              case 17:  if (n17!=null) return new OneNode( node.storageToLevelIndex(17),n17); break;
+              case 18:  if (n18!=null) return new OneNode( node.storageToLevelIndex(18),n18); break;
+              case 19:  if (n19!=null) return new OneNode( node.storageToLevelIndex(19),n19); break;
+              case 20:  if (n20!=null) return new OneNode( node.storageToLevelIndex(20),n20); break;
+              case 21:  if (n21!=null) return new OneNode( node.storageToLevelIndex(21),n21); break;
+              case 22:  if (n22!=null) return new OneNode( node.storageToLevelIndex(22),n22); break;
+              case 23:  if (n23!=null) return new OneNode( node.storageToLevelIndex(23),n23); break;
+              case 24:  if (n24!=null) return new OneNode( node.storageToLevelIndex(24),n24); break;
+              case 25:  if (n25!=null) return new OneNode( node.storageToLevelIndex(25),n25); break;
+              case 26:  if (n26!=null) return new OneNode( node.storageToLevelIndex(26),n26); break;
+              case 27:  if (n27!=null) return new OneNode( node.storageToLevelIndex(27),n27); break;
+              case 28:  if (n28!=null) return new OneNode( node.storageToLevelIndex(28),n28); break;
+              case 29:  if (n29!=null) return new OneNode( node.storageToLevelIndex(29),n29); break;
+              case 30:  if (n30!=null) return new OneNode( node.storageToLevelIndex(30),n30); break;
+              case 31:  if (n31!=null) return new OneNode( node.storageToLevelIndex(31),n31); break;
+            }
+          }
+          
+        } else if (nRemaining == 32) { // still a FullNode, but elements might have changed
+          Node[] a = {n0,n1,n2,n3,n4,n5,n6,n7,n8,n9,n10,
+                      n11,n12,n13,n14,n15,n16,n17,n18,n19,n20,
+                      n21,n22,n23,n24,n25,n26,n27,n28,n29,n30,n31};
+          
+          return new FullNode(a);
+          
+        } else {
+          int bitmap = 0;
+          Node[] a = new Node[nRemaining];
+          int j=0;
+
+          // <2do> this is bad - there has to be a more efficient way to generate the bitmap
+          for (int i=0; j < nRemaining; i++){
+            switch (i){
+              case  0:  if  (n0!=null) { a[j++] =  n0; bitmap |= (1<<node.storageToLevelIndex(0)); } break;
+              case  1:  if  (n1!=null) { a[j++] =  n1; bitmap |= (1<<node.storageToLevelIndex(1)); } break;
+              case  2:  if  (n2!=null) { a[j++] =  n2; bitmap |= (1<<node.storageToLevelIndex(2)); } break;
+              case  3:  if  (n3!=null) { a[j++] =  n3; bitmap |= (1<<node.storageToLevelIndex(3)); } break;
+              case  4:  if  (n4!=null) { a[j++] =  n4; bitmap |= (1<<node.storageToLevelIndex(4)); } break;
+              case  5:  if  (n5!=null) { a[j++] =  n5; bitmap |= (1<<node.storageToLevelIndex(5)); } break;
+              case  6:  if  (n6!=null) { a[j++] =  n6; bitmap |= (1<<node.storageToLevelIndex(6)); } break;
+              case  7:  if  (n7!=null) { a[j++] =  n7; bitmap |= (1<<node.storageToLevelIndex(7)); } break;
+              case  8:  if  (n8!=null) { a[j++] =  n8; bitmap |= (1<<node.storageToLevelIndex(8)); } break;
+              case  9:  if  (n9!=null) { a[j++] =  n9; bitmap |= (1<<node.storageToLevelIndex(9)); } break;
+              case 10:  if (n10!=null) { a[j++] = n10; bitmap |= (1<<node.storageToLevelIndex(10)); } break;
+              case 11:  if (n11!=null) { a[j++] = n11; bitmap |= (1<<node.storageToLevelIndex(11)); } break;
+              case 12:  if (n12!=null) { a[j++] = n12; bitmap |= (1<<node.storageToLevelIndex(12)); } break;
+              case 13:  if (n13!=null) { a[j++] = n13; bitmap |= (1<<node.storageToLevelIndex(13)); } break;
+              case 14:  if (n14!=null) { a[j++] = n14; bitmap |= (1<<node.storageToLevelIndex(14)); } break;
+              case 15:  if (n15!=null) { a[j++] = n15; bitmap |= (1<<node.storageToLevelIndex(15)); } break;
+              case 16:  if (n16!=null) { a[j++] = n16; bitmap |= (1<<node.storageToLevelIndex(16)); } break;
+              case 17:  if (n17!=null) { a[j++] = n17; bitmap |= (1<<node.storageToLevelIndex(17)); } break;
+              case 18:  if (n18!=null) { a[j++] = n18; bitmap |= (1<<node.storageToLevelIndex(18)); } break;
+              case 19:  if (n19!=null) { a[j++] = n19; bitmap |= (1<<node.storageToLevelIndex(19)); } break;
+              case 20:  if (n20!=null) { a[j++] = n20; bitmap |= (1<<node.storageToLevelIndex(20)); } break;
+              case 21:  if (n21!=null) { a[j++] = n21; bitmap |= (1<<node.storageToLevelIndex(21)); } break;
+              case 22:  if (n22!=null) { a[j++] = n22; bitmap |= (1<<node.storageToLevelIndex(22)); } break;
+              case 23:  if (n23!=null) { a[j++] = n23; bitmap |= (1<<node.storageToLevelIndex(23)); } break;
+              case 24:  if (n24!=null) { a[j++] = n24; bitmap |= (1<<node.storageToLevelIndex(24)); } break;
+              case 25:  if (n25!=null) { a[j++] = n25; bitmap |= (1<<node.storageToLevelIndex(25)); } break;
+              case 26:  if (n26!=null) { a[j++] = n26; bitmap |= (1<<node.storageToLevelIndex(26)); } break;
+              case 27:  if (n27!=null) { a[j++] = n27; bitmap |= (1<<node.storageToLevelIndex(27)); } break;
+              case 28:  if (n28!=null) { a[j++] = n28; bitmap |= (1<<node.storageToLevelIndex(28)); } break;
+              case 29:  if (n29!=null) { a[j++] = n29; bitmap |= (1<<node.storageToLevelIndex(29)); } break;
+              case 30:  if (n30!=null) { a[j++] = n30; bitmap |= (1<<node.storageToLevelIndex(30)); } break;
+              case 31:  if (n31!=null) { a[j++] = n31; bitmap |= (1<<node.storageToLevelIndex(31)); } break;
+            }
+          }
+          
+          return new BitmapNode( bitmap, a);
+        }
+      }
+    }
+    
+    throw new RuntimeException("can't get here");
+  }
+  
+  public PSIntMap<V> removeAllSatisfying( Predicate<V> pred){
+    Node<Node> node = rootNode;
+    
+    if (stagingNode != targetNode){
+      // we need to merge first since the target node might be gone after bulk removal
+      node = mergeStagingNode();
+    }
+    node = removeAllSatisfying( rootLevel, node, pred);
+    
+    // reduce depth
+    
+    int newRootLevel = rootLevel;
+    int newSize = countSize( newRootLevel, node);
+    
+    return new PSIntMap<V>( newSize, newRootLevel, node, null, null, 0);    
+  }
+  
+  protected final int countSize (int level, Node node){
+    if (node == null){
+      return 0;
+      
+    } else {
+      if (level == 0) {
+        return node.getNumberOfElements();
+
+      } else {
+        int nValues = 0;
+        int len = node.getNumberOfElements();
+        for (int i = 0; i < len; i++) {
+          nValues += countSize(level - 1, (Node) node.getElementAtStorageIndex(i));
+        }
+        return nValues;
+      }
+    }
+  }
+  
+  public V[] values (){
+    final Object[] values = new Object[size];
+    Processor<V> flattener = new Processor<V>(){
+      int i=0;
+      @Override
+       public void process (V v){
+        values[i] = v;
+      }
+    };
+    
+    process(flattener);
+    
+    return (V[])values;
+  }
+  
+  //--- debugging
+  
+  public void printOn(PrintStream ps) {
+    if (rootNode != null) {
+      rootNode.printNodeInfoOn(ps, targetNode, stagingNode);
+      ps.println();
+      rootNode.printOn(ps, rootLevel, targetNode, stagingNode);
+    } else {
+      ps.println( "empty");
+    }
+
+    if (stagingNode != null) {
+      ps.println("--------------- staging");
+      stagingNode.printNodeInfoOn(ps, targetNode, stagingNode);
+      ps.println();
+      stagingNode.printOn(ps, 0, targetNode, stagingNode);
+    }
+  }
+  
+  public String keyDescription (int key) {
+    StringBuilder sb = new StringBuilder();
+    int ish = getStartLevel(key);
+    
+    sb.append(key);
+    sb.append(" (0x");
+    sb.append(Integer.toHexString(key));
+    sb.append(") => ");
+    
+    for (int shift=ish*5; shift>=0; shift-=5) {
+      sb.append((key>>shift) & 0x1f);
+      if (shift > 0) {
+        sb.append('.');
+      }
+    }
+    
+    return sb.toString();
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/Pair.java b/src/main/gov/nasa/jpf/util/Pair.java
new file mode 100644 (file)
index 0000000..b66bf3d
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * simple const wrapper utility class for 2-tuple 
+ */
+public class Pair<A,B> {
+  
+  // final so that we don't need getters
+  public final A _1;
+  public final B _2;
+  
+  public Pair(A a, B b) { 
+    this._1 = a; 
+    this._2 = b; 
+  }
+  
+  @Override
+  public final boolean equals(Object o) {
+    if (o instanceof Pair){
+      Pair p = (Pair)o;
+      if ((_1 == null) && (p._1 != null)){
+        return false;
+      }
+      if ((_2 == null) && (p._2 != null)){
+        return false;
+      }
+      if (!_1.equals(p._1)){
+        return false;
+      }
+      if (!_2.equals(p._2)){
+        return false;
+      }
+      
+      return true;
+      
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public final int hashCode() {
+    int h1 = (_1 == null) ? 0 : _1.hashCode();
+    int h2 = (_2 == null) ? 0 : _2.hashCode();
+    
+    return h1 ^ h2;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/PairPermutationGenerator.java b/src/main/gov/nasa/jpf/util/PairPermutationGenerator.java
new file mode 100644 (file)
index 0000000..01d7de0
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * a generator for pair-wise permutations, which only considers permutations
+ * for each pair of elements, regardless of position. This reduces the
+ * number of generated permutations from N! to sum(i=1..N){N-i} + 1.
+ * This can be used to test order dependencies between two concurrent
+ * entities (threads, state machines etc), based on the same assumptions
+ * that are used in pair-wise testing
+ */
+public class PairPermutationGenerator extends PermutationGenerator {
+
+  protected int i, j;
+
+  public PairPermutationGenerator (int nElements){
+    super(nElements);
+  }
+
+  @Override
+  public void reset(){
+    initPermutations();
+    i = 0;
+    j = 0;
+  }
+  
+  public static long computeNumberOfPermutations (int n){
+    long v = 1;
+    for (int l=1; l<n; l++){
+      v += (n - l);
+    }
+    return v;
+  }
+  
+  @Override
+  protected long computeNumberOfPermutations(){
+    return computeNumberOfPermutations(nElements);
+  }
+          
+  @Override
+  public int[] next (){
+    int n = permutation.length;
+
+    if (nGenerated == 0){ // the initial order
+      nGenerated = 1;
+      return permutation;
+      
+    } else if (nGenerated > 1){
+      if (nGenerated == nPermutations){
+        throw new NoSuchElementException();
+      }
+      swap(permutation, i, j); // revert last permutation
+    }
+
+
+    if (++j == n){
+      if (++i == n){
+        throw new NoSuchElementException();
+      } else {
+        j = i+1;
+      }
+    }
+
+    swap(permutation, i, j);
+    nGenerated++;
+    return permutation;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/PathnameExpander.java b/src/main/gov/nasa/jpf/util/PathnameExpander.java
new file mode 100644 (file)
index 0000000..09f3b47
--- /dev/null
@@ -0,0 +1,239 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * utility to perform pathname expansion
+ * the following patterns are supported so far:
+ *
+ * (1) brace expansion ala bash: foo{Boo,Shoo} => fooBoo, fooShoo
+ *     (this doesn't check for existence, its simply lexical)
+ *
+ * (2) '*' wildcard pathname expansion ala bash: "*.java" | "*\Main*.java"
+ *     (supports wildcards in mutiple path elements and within file/dir name)
+ *
+ * (3) recursive dir expansion ala Ant: "**\*.jar"
+ *
+ */
+public class PathnameExpander {
+
+  public String[] expandPath (String s) {
+    if (s == null || s.length() == 0) {
+      return null;
+    }
+
+    boolean hasWildcards = (s.indexOf('*') >= 0);
+
+    int i = s.indexOf('{');
+    if (i >= 0){
+      ArrayList<String> list = new ArrayList<String>();
+
+      int j=0, jLast = s.length();
+      for (; (i = s.indexOf('{', j)) >= 0;) {
+        if ((j = s.indexOf('}', i)) > 0) {
+          String[] choices = s.substring(i + 1, j).split(",");
+
+          if (list.isEmpty()) {
+            String prefix = s.substring(0, i);
+            for (String c : choices) {
+              list.add(prefix + c);
+            }
+          } else {
+            String prefix = s.substring(jLast, i);
+            ArrayList<String> newList = new ArrayList<String>();
+            for (String e : list) {
+              for (String c : choices) {
+                newList.add(e + prefix + c);
+              }
+            }
+            list = newList;
+          }
+          jLast = j+1;
+        } else {
+          throw new IllegalArgumentException("illegal path spec (missing '}'): " + s);
+        }
+      }
+
+      if (jLast < s.length()) {
+        String postfix = s.substring(jLast);
+        ArrayList<String> newList = new ArrayList<String>();
+        for (String e : list) {
+          newList.add(e + postfix);
+        }
+        list = newList;
+      }
+
+      if (hasWildcards){
+        ArrayList<String> newList = new ArrayList<String>();
+        for (String p : list) {
+          for (String c : expandWildcards(p)) {
+            newList.add(c);
+          }
+        }
+        list = newList;
+      }
+
+      return list.toArray(new String[list.size()]);
+
+    } else {  // no bracket expansion required
+
+      if (hasWildcards){
+        return expandWildcards(s);
+
+      } else { // nothing to expand at all
+        return (new String[] {s});
+      }
+    }
+  }
+
+  protected String[] expandWildcards (String s){
+    int i = s.indexOf('*');
+
+    if (i >= 0){ // Ok, we have at least one wildcard
+      String[] a = s.split("\\/");
+      ArrayList<File> list = new ArrayList<File>();
+
+      int j= initializeMatchList(list, a[0]);
+      for (; j<a.length; j++){
+        ArrayList<File> newList = new ArrayList<File>();
+
+        String e = a[j];
+        if (e.indexOf('*') >= 0){
+
+          if (e.equals("**")){ // matches all subdirs recursively
+            collectDirs(list, newList);
+
+          } else { // file/dir name match
+            collectMatchingNames(list, newList, getPattern(e));
+          }
+
+        } else { // no wildcard
+          collectExistingFile(list, newList, e);
+        }
+
+        if (newList.isEmpty()){  // shortcut, nothing more to match
+          return new String[0];
+        }
+        list = newList;
+      }
+
+      return getPaths(list);
+
+    } else { // no wildcards, nothing to expand
+      return new String[] {s};
+    }
+  }
+
+  private int initializeMatchList (ArrayList<File> list, String path){
+    if (path.isEmpty()){ // absolute pathname (ignoring drive letters for now)
+      list.add(new File(File.separator));
+      return 1;
+    } else if (path.equals("..") || path.equals(".")){
+      list.add(new File(path));
+      return 1;
+    } else {
+      list.add(new File("."));
+      return 0;
+    }
+  }
+
+  private void collectMatchingNames(ArrayList<File> list, ArrayList<File> newList, Pattern pattern){
+    for (File dir : list) {
+      if (dir.isDirectory()){
+        for (String c : dir.list()){
+          Matcher m = pattern.matcher(c);
+          if (m.matches()){
+            newList.add(new File(dir,c));
+          }
+        }
+      }
+    }
+  }
+
+  private void collectExistingFile(ArrayList<File> list, ArrayList<File> newList, String fname) {
+    for (File dir : list) {
+      if (dir.isDirectory()){
+        File nf = new File(dir, fname);
+        if (nf.exists()) {
+          newList.add(nf);
+        }
+      }
+    }
+  }
+
+  private void collectDirs(ArrayList<File> list, ArrayList<File> newList){
+    for (File dir : list) {
+      if (dir.isDirectory()){
+        newList.add(dir); // this includes the dir itself!
+        collectSubdirs(newList,dir);
+      }
+    }
+  }
+  private void collectSubdirs(ArrayList<File> newList, File dir) {
+    for (File f : dir.listFiles()){
+      if (f.isDirectory()){
+        newList.add(f);
+        collectSubdirs(newList, f);
+      }
+    }
+  }
+
+  protected String[] getPaths(ArrayList<File> list) {
+    String[] result = new String[list.size()];
+    int k=0;
+    for (File f : list){
+      String p = f.getPath();
+      if ((p.length() > 1) && (p.charAt(0) == '.')){ // remove leading "./"
+        char c = p.charAt(1);
+        if (c == '\\' || c == '/'){
+          p = p.substring(2);
+        }
+      }
+      result[k++] = p;
+    }
+    return result;
+  }
+
+  protected Pattern getPattern(String s){
+    Pattern p;
+
+    StringBuilder sb = new StringBuilder();
+
+    int len = s.length();
+    for (int j=0; j<len; j++){
+      char c = s.charAt(j);
+      switch (c){
+      case '.' : sb.append("\\."); break;
+      case '$' : sb.append("\\$"); break;
+      case '[' : sb.append("\\["); break;
+      case ']' : sb.append("\\]"); break;
+      case '*' : sb.append(".*"); break;
+      // <2do> and probably more..
+      default:   sb.append(c);
+      }
+    }
+
+    p = Pattern.compile(sb.toString());
+    return p;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/PermutationGenerator.java b/src/main/gov/nasa/jpf/util/PermutationGenerator.java
new file mode 100644 (file)
index 0000000..d0edda6
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintStream;
+
+/**
+ * base type for permutation generators
+ */
+public abstract class PermutationGenerator {
+  
+  protected final int nElements;
+  
+  protected int[] permutation;   // array containing permutation
+
+  protected long nPermutations;
+  protected long nGenerated;
+
+  protected PermutationGenerator (int nElements){
+    this.nElements = nElements;
+    nPermutations = computeNumberOfPermutations();
+    
+    initPermutations();
+  }
+  
+  protected void initPermutations (){
+    permutation = new int[nElements];
+    
+    // initialize element array in order, starting with firstIdx
+    for (int i=0; i<nElements; i++){
+      permutation[i] = i;
+    }
+    
+    nGenerated = 0;
+  }
+  
+  protected abstract long computeNumberOfPermutations();
+  public abstract void reset();
+  
+  public long getNumberOfPermutations(){
+    return nPermutations;
+  }
+  
+  public long getNumberOfGeneratedPermutations(){
+    return nGenerated;
+  }
+  static void swap(int[] a, int i, int j){
+    int tmp = a[j];
+    a[j] = a[i];
+    a[i] = tmp;
+  }
+
+  /**
+   * for debugging purposes 
+   */
+  public void printOn (PrintStream ps){
+    printOn( ps, nGenerated, permutation);
+  }
+
+  public static void printOn (PrintStream ps, long nGenerated, int[] perm){
+    ps.printf("%2d: [", nGenerated);
+    for (int k=0; k<perm.length; k++){
+      if (k > 0) ps.print(',');
+      ps.print(perm[k]);
+    }
+    ps.println(']');    
+  }
+  
+  
+  //--- the public iteration interface, following Iterator
+  public boolean hasNext(){
+    return (nGenerated < nPermutations);
+  }
+  
+  /**
+   * return the next permutation or throw a NoSuchElementException if there is none.
+   * 
+   * NOTE - this does not guarantee to return a different object on each call,
+   * i.e. the caller has to clone if the result is stored directly
+   */
+  public abstract int[] next(); // the work horse, throws NoSuchElementException
+}
diff --git a/src/main/gov/nasa/jpf/util/Predicate.java b/src/main/gov/nasa/jpf/util/Predicate.java
new file mode 100644 (file)
index 0000000..2b817db
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public interface Predicate<T> {
+
+  boolean isTrue (T subject);
+}
diff --git a/src/main/gov/nasa/jpf/util/PrintStreamable.java b/src/main/gov/nasa/jpf/util/PrintStreamable.java
new file mode 100644 (file)
index 0000000..eca6cb2
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintStream;
+
+/**
+ * convenience interface to mix in PrintStream interface
+ */
+public interface PrintStreamable {
+
+  // the primitive method used by the defaults
+  PrintStream getPrintStream();
+
+  default void println() {
+    getPrintStream().println();
+  }
+
+  default void print(boolean a){
+    getPrintStream().print(a);
+  }
+  default void print(int a){
+    getPrintStream().print(a);
+  }
+  default void print(double a){
+    getPrintStream().print(a);
+  }
+  default void print(String s) {
+    getPrintStream().print(s);
+  }
+  default void print(Object o) {
+    getPrintStream().print(o.toString());
+  }
+
+  default void println(boolean a){
+    getPrintStream().println(a);
+  }
+  default void println(int a){
+    getPrintStream().println(a);
+  }
+  default void println(double a){
+    getPrintStream().println(a);
+  }
+  default void println(String s) {
+    getPrintStream().println(s);
+  }
+  default void println(Object o) {
+    getPrintStream().println(o.toString());
+  }
+
+  default void printf (String format, Object... args) {
+    getPrintStream().printf(format, args);
+  }
+
+  //... and many more
+}
diff --git a/src/main/gov/nasa/jpf/util/PrintUtils.java b/src/main/gov/nasa/jpf/util/PrintUtils.java
new file mode 100644 (file)
index 0000000..e07a0af
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+import java.io.PrintStream;
+
+public class PrintUtils {
+
+  public static void printChar (PrintStream ps, char c){
+    switch (c) {
+    case '\n': ps.print("\\n"); break;
+    case '\r': ps.print("\\r"); break;
+    case '\t': ps.print("\\t"); break;
+    case '\b': ps.print("\\b"); break;
+    case '\f': ps.print("\\f"); break;
+    case '\'': ps.print("\\'"); break;
+    case '\"': ps.print("\\\""); break;
+    case '\\': ps.print("\\"); break;
+    default: ps.print(c);
+    }
+  }
+  
+  public static void printCharLiteral (PrintStream ps, char c){
+    ps.print('\'');
+    printChar(ps, c);
+    ps.print('\'');
+  }
+  
+  public static void printStringLiteral (PrintStream ps, char[] data, int max){
+    int i;
+    if (max < 0){
+      max = data.length;
+    }
+    
+    ps.print('"');
+    for (i=0; i<max; i++){
+      printChar(ps, data[i]);
+    }
+    
+    if (i< data.length){
+      ps.print("...");
+    }
+    
+    ps.print('"');
+  }
+  
+  // this is mostly here so that we use the same convention
+  public static void printReference (PrintStream ps, int ref){
+    if (ref == MJIEnv.NULL){
+      ps.print("null");
+    } else {
+      ps.print('@');
+      ps.printf("%x", ref);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Printable.java b/src/main/gov/nasa/jpf/util/Printable.java
new file mode 100644 (file)
index 0000000..a93a6ce
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintWriter;
+
+
+/**
+ * Printable is mainly used to avoid having to create potentially huge
+ * String(via StringBuilders or Writers) which are just used to be
+ * written to std streams
+ */
+public interface Printable {
+  void printOn (PrintWriter ps);
+}
diff --git a/src/main/gov/nasa/jpf/util/Processor.java b/src/main/gov/nasa/jpf/util/Processor.java
new file mode 100644 (file)
index 0000000..ae55cac
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public interface Processor<T> {
+  void process(T obj);
+}
diff --git a/src/main/gov/nasa/jpf/util/RandomPermutationGenerator.java b/src/main/gov/nasa/jpf/util/RandomPermutationGenerator.java
new file mode 100644 (file)
index 0000000..2de9ccd
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+import java.util.Random;
+
+/**
+ * a permutation generator that uses the Fisher-Yates shuffle
+ * (Durstenfeld, R. (July 1964). "Algorithm 235: Random permutation". 
+ * Communications of the ACM 7 (7): 420)
+ * 
+ * use this if TotalPermutations would be too large and PairPermutations
+ * not enough
+ */
+public class RandomPermutationGenerator extends PermutationGenerator {
+
+  protected int seed;
+  protected Random rand;
+  
+  protected int[] orig;
+    
+  public RandomPermutationGenerator (int nElements, int nPermutations, int seed){
+    super(nElements);
+    this.nPermutations = nPermutations;
+    rand = new Random(seed);
+    orig = permutation.clone();
+  }
+  
+  @Override
+  protected long computeNumberOfPermutations() {
+    return nPermutations; // it's input (set)
+  }
+
+  @Override
+  public void reset() {
+    initPermutations();
+    rand = new Random(seed);
+    nGenerated = 0;
+  }
+
+  @Override
+  public int[] next() {
+    if (nGenerated == 0){
+      nGenerated = 1;
+      return permutation;
+      
+    } else if (nGenerated < nPermutations){
+      permutation = orig.clone();
+      for (int i=0; i<nElements; i++){
+        int r = i + rand.nextInt( nElements-i);  // i <= r < nElements-1
+        swap(permutation, r, i);
+      }        
+      nGenerated++;
+      return permutation;
+        
+    } else {
+      throw new NoSuchElementException();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/ReadOnlyObjList.java b/src/main/gov/nasa/jpf/util/ReadOnlyObjList.java
new file mode 100644 (file)
index 0000000..72ad1f4
--- /dev/null
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public interface ReadOnlyObjList<E> extends Iterable<E> {
+  E get(int idx);
+  int length();
+}
diff --git a/src/main/gov/nasa/jpf/util/Reflection.java b/src/main/gov/nasa/jpf/util/Reflection.java
new file mode 100644 (file)
index 0000000..1a02984
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPFException;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+/**
+ * reflection utilities
+ */
+public class Reflection {
+
+  /**
+   * find callers class
+   *
+   * @param up levels upwards from our caller (NOT counting ourselves)
+   * @return caller class, null if illegal 'up' value
+   */
+  public static Class<?> getCallerClass(int up) {
+    int idx = up + 1; // don't count this stackframe
+
+    StackTraceElement[] st = (new Throwable()).getStackTrace();
+    if ((up < 0) || (idx >= st.length)) {
+      return null;
+    } else {
+      try {
+        return Class.forName(st[idx].getClassName());
+      } catch (Throwable t) {
+        return null;
+      }
+    }
+  }
+
+  public static Class<?> getCallerClass () {
+    return getCallerClass(2);
+  }
+
+  public static <T> Class<? extends T>  getCallerClass (Class<T> type){
+    Class<?> cls = getCallerClass(2);
+
+    if (cls != null) {
+      if (type.isAssignableFrom(cls)) {
+        return cls.asSubclass(type);
+      } else {
+        throw new JPFException("caller class: " + cls.getName() + " not of type: " + type.getName());
+      }
+    }
+    return null;
+  }
+
+  public static StackTraceElement getCallerElement (int up){
+    int idx = up + 1; // don't count this stackframe
+
+    StackTraceElement[] st = (new Throwable()).getStackTrace();
+    if ((up < 0) || (idx >= st.length)) {
+      return null;
+    } else {
+      return st[idx];
+    }
+  }
+  public static StackTraceElement getCallerElement () {
+    StackTraceElement[] st = (new Throwable()).getStackTrace();
+    if (st.length > 2){
+      return st[2]; // '0' is this method itself
+    } else {
+      return null;
+    }
+  }
+
+  public static boolean tryCallMain(Class<?> cls, String[] args) throws InvocationTargetException {
+    try {
+      Method method = cls.getDeclaredMethod("main", String[].class);
+      int modifiers = method.getModifiers();
+
+      if ((modifiers & (Modifier.PUBLIC | Modifier.STATIC | Modifier.ABSTRACT)) == (Modifier.PUBLIC | Modifier.STATIC)) {
+        method.invoke(null, (Object)args);
+        return true;
+      }
+
+    } catch (NoSuchMethodException nsmx) {
+      //System.out.println(nsmx);
+      // just return false
+    } catch (IllegalAccessException iax){
+      //System.out.println(iax);
+      // can't happen, we checked for it before invoking
+    } catch (IllegalArgumentException iargx){
+      //System.out.println(iargx);
+      // can't happen, we checked for it before invoking
+    }
+
+    return false;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/RepositoryEntry.java b/src/main/gov/nasa/jpf/util/RepositoryEntry.java
new file mode 100644 (file)
index 0000000..e69f2af
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * simple generic structure to hold repository info for source files
+ * 
+ * <2do> extend this to find out what the status of the repository is, i.e. if
+ * there are any local modifications, update revision etc.
+ */
+public class RepositoryEntry {
+  
+  static HashMap<String,RepositoryEntry> dict = new HashMap<String,RepositoryEntry>();
+  
+  String fileName;
+  String repositoryType;
+  String repository;
+  String revision;
+
+  static RepositoryEntryFactory searchers[] = {
+    new SvnRepositoryEntryFactory(),
+    new HgRepositoryEntryFactory(),
+    new GitRepositoryEntryFactory()
+  };
+
+  public static RepositoryEntry getRepositoryEntry (String fullFileName) {
+    RepositoryEntry e = dict.get(fullFileName);
+    
+    if (e == null) {
+      for (RepositoryEntryFactory factory : searchers) {
+        if ((e = factory.getRepositoryEntry(fullFileName)) != null)
+          break;
+      }
+    }
+    
+    dict.put(fullFileName, e); // no need to look this up more than once
+    return e;
+  }
+  
+  public RepositoryEntry (String fileName, String repositoryType, String repository, String revision) {
+    this.fileName = fileName;
+    this.repositoryType = repositoryType;
+    this.repository = repository;
+    this.revision = revision;
+  }
+  
+  public String getFileName() {
+    return fileName;
+  }
+  
+  public String getRepositoryType() {
+    return repositoryType;
+  }
+  
+  public String getRepository() {
+    return repository;
+  }
+  
+  public String getRevision() {
+    return revision;
+  }
+
+}
+
+interface RepositoryEntryFactory {
+  RepositoryEntry getRepositoryEntry(String fullFileName);
+}
+
+class SvnRepositoryEntryFactory implements RepositoryEntryFactory {
+
+  /*
+   * <2do> doesn't work on Windows, where the .svn/entries is apparently
+   * not stored as an XML file
+   */
+  @Override
+  public RepositoryEntry getRepositoryEntry(String fullFileName) {
+    File f = new File(fullFileName);
+    String fname = f.getName();
+    String dName = f.getParent();
+    
+    File fEntries = new File(dName + File.separatorChar + ".svn" + File.separatorChar + "entries");
+    if (fEntries.exists()) {
+      String repository = "?";
+      String revision = "?";
+      
+      Pattern pName = Pattern.compile(" *name=\"([a-zA-Z0-9.]+)\"");
+      Pattern pRep = Pattern.compile(" *url=\"([a-zA-Z0-9.:/\\-]+)\"");
+      Pattern pRev = Pattern.compile(" *committed-rev=\"([0-9]+)\"");
+      try {
+        BufferedReader r = new BufferedReader(new FileReader(fEntries));
+        for (String line=r.readLine(); line != null; line = r.readLine()) {
+          Matcher mRep = pRep.matcher(line);
+          if (mRep.matches()) {
+            repository = mRep.group(1);
+          } else {
+            Matcher mRev = pRev.matcher(line);
+            if (mRev.matches()) {
+              revision = mRev.group(1);
+            } else {
+              Matcher mName = pName.matcher(line);
+              if (mName.matches() && mName.group(1).equals(fname)) {
+                // Ok, got everything
+                return new RepositoryEntry(fname, "svn", repository, revision);
+              }
+            }
+          }
+        }
+      } catch (Throwable t) {}
+    }
+    
+    return null;
+  }
+}
+
+class HgRepositoryEntryFactory implements RepositoryEntryFactory {
+
+  @Override
+  public RepositoryEntry getRepositoryEntry(String fullFileName) {
+    File file = new File(fullFileName);
+
+    if (!file.exists())
+      return null;
+
+    File currentDir = file.getParentFile();
+
+    searchForHg:
+    while (currentDir != null) {
+      for (String childName : currentDir.list())
+        if (childName.equals(".hg"))
+          break searchForHg;
+
+      currentDir = currentDir.getParentFile();
+    }
+
+    if (currentDir != null) {
+      try {
+        File hgrcFile = new File(currentDir, ".hg/hgrc");
+        
+        String repoURL = "";
+        BufferedReader r = new BufferedReader(new FileReader(hgrcFile));
+        for (String line=r.readLine(); line != null; line = r.readLine()) {
+          String keyVal[] = line.split("=");
+          if (keyVal[0].trim().equals("default")) {
+            repoURL = keyVal[1].trim();
+            break;
+          }
+
+        }
+
+        File branchHeads = new File(currentDir, ".hg/branchheads.cache");
+        r = new BufferedReader(new FileReader(branchHeads));
+        String revision = r.readLine().split(" ")[1];
+
+        return new RepositoryEntry(fullFileName, "hg", repoURL, revision);
+      }
+      catch (Exception ex) {
+        return null;
+      }
+    }
+
+    return null;
+  }
+
+}
+
+class GitRepositoryEntryFactory implements RepositoryEntryFactory {
+
+  @Override
+  public RepositoryEntry getRepositoryEntry(String fullFileName) {
+    File file = new File(fullFileName);
+
+    if (!file.exists())
+      return null;
+
+    File currentDir = file.getParentFile();
+
+    searchForHg:
+    while (currentDir != null) {
+      for (String childName : currentDir.list()) {
+        if (childName.equals(".git")) {
+          break searchForHg;
+        }
+      }
+
+      currentDir = currentDir.getParentFile();
+    }
+
+    if (currentDir != null) {
+      try {
+        File hgrcFile = new File(currentDir, ".git/config");
+
+        String repoURL = "";
+        BufferedReader r = new BufferedReader(new FileReader(hgrcFile));
+        for (String line = r.readLine(); line != null; line = r.readLine()) {
+          String keyVal[] = line.split("=");
+          if (keyVal[0].trim().equals("url")) {
+            repoURL = keyVal[1].trim();
+            break;
+          }
+
+        }
+
+        // git doesn't has revision numbers so we will read last revision's hash instead
+        File gitHeadHash = new File(currentDir, ".git/refs/heads/master");
+        r = new BufferedReader(new FileReader(gitHeadHash));
+        String revision = r.readLine();
+
+        return new RepositoryEntry(fullFileName, "git", repoURL, revision);
+
+      } catch (Exception ex) {
+        return null;
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Result.java b/src/main/gov/nasa/jpf/util/Result.java
new file mode 100644 (file)
index 0000000..8f3d9a0
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * simple result wrapper that can store a boolean value and a String, to
+ * be used as method return value if we have to keep exceptions in the callee
+ */
+public class Result {
+  
+  public static final Result OK = new Result();
+  
+  // final so that we don't need getters
+  public final String error;
+  
+  // if you need a positive result, use OK
+  private Result (){
+    error = null;
+  }
+  
+  private Result (String errorMsg){
+    error = errorMsg;
+  }
+  
+  @Override
+  public boolean equals(Object o){
+    // we only compare if there was an error, not which one
+    if (o instanceof Boolean){
+      return (error == null) == (Boolean)o;
+    } else if (o instanceof Result){
+      return (error == null) == (((Result)o).error == null);
+    }
+    return false;
+  }
+
+  public static Result failure (String errorMsg){
+    return new Result(errorMsg);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Right.java b/src/main/gov/nasa/jpf/util/Right.java
new file mode 100644 (file)
index 0000000..2888cb4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Arrays;
+
+/**
+ * right justified output
+ * <2do> this is not worth a class! use a general TextFormatter
+ */
+
+public class Right {
+  public static String format (String value, int spaces, char ch) {
+    int vlen = value.length();
+    int newLen = Math.max(spaces, vlen);
+    char[] result = new char[newLen];
+    int pivot = newLen - vlen;
+    value.getChars(0, vlen, result, pivot);
+    Arrays.fill(result, 0, pivot, ch);
+    return new String(result);
+  }
+
+  public static String format (String value, int spaces) {
+    return format(value, spaces, ' ');
+  }
+
+  public static String format (int value, int digits) {
+    return format(Integer.toString(value),digits,' ');
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/RunListener.java b/src/main/gov/nasa/jpf/util/RunListener.java
new file mode 100644 (file)
index 0000000..9614195
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * little helper interface to facilitate resetting classes and objects
+ * between JPF runs, mostly to avoid memory leaks
+ */
+public interface RunListener {
+
+  void reset (RunRegistry reg);
+}
diff --git a/src/main/gov/nasa/jpf/util/RunRegistry.java b/src/main/gov/nasa/jpf/util/RunRegistry.java
new file mode 100644 (file)
index 0000000..4be3a1a
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.ArrayList;
+
+/**
+ * little helper to enable resetting classes and objects between JPF runs,
+ * mostly to avoid memory leaks
+ * 
+ * reset() has to be called at the beginning of a new run, causing all
+ * still registered listeners to be notified. Listeners have to implement
+ * their own logic to check for re-initialization, but can use the
+ * 'run' timestamp to do so
+ */
+public class RunRegistry {
+  static RunRegistry singleton = new RunRegistry();
+  
+  ArrayList<RunListener> listeners = new ArrayList<RunListener>();
+  long run;
+  
+  public static RunRegistry getDefaultRegistry() {
+    return singleton;
+  }
+  
+  public void addListener (RunListener r) {
+    if (!listeners.contains(r)){
+      listeners.add(r);
+    }
+  }
+  
+  public boolean isRegistered (RunListener r){
+    return listeners.contains(r);
+  }
+  
+  public void reset() {
+    run = System.currentTimeMillis();
+    
+    for (RunListener r : listeners){
+      r.reset(this);
+    }
+    
+    listeners.clear();
+  }
+  
+  public long getRun() {
+    return run;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SimplePool.java b/src/main/gov/nasa/jpf/util/SimplePool.java
new file mode 100644 (file)
index 0000000..52ad32a
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * This is a simplified hash pool that does not support removal or
+ * numbering of elements.
+ */
+@SuppressWarnings("unchecked")
+public class SimplePool<E> {
+  static final double MAX_LOAD = 0.7;
+  static final int DEFAULT_POW = 10;
+
+  Object[] table;
+  
+  int count;
+  int pow;
+  int mask;
+  int nextRehash;
+  
+  /**
+   * Creates a SimplePool that holds about 716 elements before first
+   * rehash.
+   */
+  public SimplePool() {
+    this(DEFAULT_POW);
+  }
+  
+  /**
+   * Creates a SimplePool that holds about 0.7 * 2**pow elements before
+   * first rehash.
+   */
+  public SimplePool(int pow) {
+    table = new Object[1 << pow];
+    count = 0;
+    this.pow = pow;
+    mask = table.length - 1;
+    nextRehash = (int)(MAX_LOAD * mask);
+  }
+
+  // ********************** API as simple hash pool ******************* //
+  
+  /**
+   * Asks whether a particular element is already pooled.  NOT A TYPICAL
+   * OPERATION.
+   */
+  public boolean isPooled(E e) {
+    return e == null || query(e) != null;
+  }
+  
+  /**
+   * Returns the matching element if there is one, null if not.
+   */
+  public E query(E e) {
+    if (e == null) return null;
+    int code = e.hashCode();
+    int idx = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = idx;
+
+    for(;;) {
+      Object o = table[idx];
+      if (o == null) break;
+      if (e.equals(o)) {
+        return (E) o; // seen before!
+      }
+      idx = (idx + delta) & mask;
+      assert (idx != oidx); // should never wrap around
+    }
+    return null;
+  }
+
+  /**
+   * Returns a pooled element matching e, which will be e if no match
+   * has been previously pooled.
+   */
+  public E pool(E e) {
+    if (e == null) return null;
+    int code = e.hashCode();
+    int idx = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = idx;
+
+    for(;;) {
+      Object o = table[idx];
+      if (o == null) break;
+      if (e.equals(o)) {
+        return (E) o; // seen before!
+      }
+      idx = (idx + delta) & mask;
+      assert (idx != oidx); // should never wrap around
+    }
+    assert (table[idx] == null); // should never fill up
+    // not seen before; add it
+    
+    count++;
+    if (count >= nextRehash) { // too full
+      Object[] oldTable = table;
+      pow++;
+      table = new Object[1 << pow];
+      mask = table.length - 1;
+      nextRehash = (int)(MAX_LOAD * mask);
+
+      int oldLen = oldTable.length;
+      for (int i = 0; i < oldLen; i++) {
+        Object o = oldTable[i];
+        if (o != null) {
+          code = o.hashCode();
+          idx = code & mask;
+          delta = (code >> (pow - 1)) | 1; // must be odd!
+          while (table[idx] != null) { // we know enough slots exist
+            idx = (idx + delta) & mask;
+          }
+          table[idx] = o;
+        }
+      }
+      // done with rehash; now get idx to empty slot
+      code = e.hashCode();
+      idx = code & mask;
+      delta = (code >> (pow - 1)) | 1; // must be odd!
+      while (table[idx] != null) { // we know enough slots exist & new element
+        idx = (idx + delta) & mask;
+      }
+    } else {
+      // idx already pointing to empty slot
+    }
+
+    table[idx] = e;
+    return e;
+  }
+  
+  
+  // ******************* API as add-only hash set *************** //
+  
+  public boolean isMember(E e) {
+    return query(e) != null;
+  }
+  
+  public void add(E e) {
+    /*(void)*/ pool(e);
+  }
+  
+  
+  // ************************** Test main ************************ //
+  
+  /**
+   * Test main.
+   */
+  public static void main(String[] args) {
+    SimplePool<Integer> pool = new SimplePool<Integer>(4);
+    for (int i = 0; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o != p) throw new RuntimeException();
+      Integer q = pool.pool(p);
+      if (q != p) throw new RuntimeException();
+    }
+    for (int i = 0; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o == p) throw new RuntimeException();
+      if (!o.equals(p)) throw new RuntimeException();
+    }
+    for (int i = 1; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o != p) throw new RuntimeException();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SingleElementList.java b/src/main/gov/nasa/jpf/util/SingleElementList.java
new file mode 100644 (file)
index 0000000..d35d628
--- /dev/null
@@ -0,0 +1,248 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.NoSuchElementException;
+
+/**
+ * a immutable list that just contains a single element
+ * 
+ * This is just an optimization for constructs such as CGs that inherently can
+ * contain lists, but frequently don't have more than a single element
+ * 
+ * While java.util.Collections provides optimizations for empty lists, there is
+ * no optimization for single element lists
+ */
+public class SingleElementList<E> implements List<E> {
+  
+  protected E elem;
+
+  class SingleElemIterator implements ListIterator<E>{
+    boolean done;
+
+    @Override
+    public boolean hasNext() {
+      return !done;
+    }
+
+    @Override
+    public E next() {
+      if (!done){
+        done = true;
+        return elem;
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+    public boolean hasPrevious() {
+      return false;
+    }
+
+    @Override
+    public E previous() {
+      throw new NoSuchElementException();
+    }
+
+    @Override
+    public int nextIndex() {
+      if (!done){
+        return 0;
+      } else {
+        return 1;
+      }
+    }
+
+    @Override
+    public int previousIndex() {
+      if (done){
+        return 0;
+      } else {
+        return -1;
+      }
+    }
+
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException("list is immutable");
+    }
+
+    @Override
+    public void set(E e) {
+      throw new UnsupportedOperationException("list is immutable");
+    }
+
+    @Override
+    public void add(E e) {
+      throw new UnsupportedOperationException("list is immutable");
+    }
+  }
+  
+  public SingleElementList (E e){
+    elem = e;
+  }
+
+  @Override
+  public int size() {
+    return 1;
+  }
+
+  @Override
+  public boolean isEmpty() {
+    return false;
+  }
+
+  @Override
+  public boolean contains(Object o) {
+    if (elem != null){
+      return elem.equals(o);
+    } else {
+      return o == null;
+    }
+  }
+
+  @Override
+  public Iterator<E> iterator() {
+    return new SingleElemIterator();
+  }
+
+  @Override
+  public Object[] toArray() {
+    Object[] a = { elem };
+    return a;
+  }
+
+  @Override
+  public <T> T[] toArray(T[] a) {
+    a[0] = (T)elem;
+    return a;
+  }
+
+  @Override
+  public boolean add(E e) {
+    return false;
+  }
+
+  @Override
+  public boolean remove(Object o) {
+    return false;
+  }
+
+  @Override
+  public boolean containsAll(Collection<?> c) {
+    for (Object o : c){
+      if (!contains(o)){
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public boolean addAll(Collection<? extends E> c) {
+    return false;
+  }
+
+  @Override
+  public boolean addAll(int index, Collection<? extends E> c) {
+    return false;
+  }
+
+  @Override
+  public boolean removeAll(Collection<?> c) {
+    return false;
+  }
+
+  @Override
+  public boolean retainAll(Collection<?> c) {
+    for (Object o : c){
+      if (!contains(o)){
+        return false;
+      }
+    }
+    return true;
+  }
+
+  @Override
+  public void clear() {
+    throw new UnsupportedOperationException("list is immutable");
+  }
+
+  @Override
+  public E get(int index) {
+    if (index == 0){
+      return elem;
+    } else {
+      throw new IndexOutOfBoundsException(Integer.toString(index));
+    }
+  }
+
+  @Override
+  public E set(int index, E element) {
+    throw new UnsupportedOperationException("list is immutable");
+  }
+
+  @Override
+  public void add(int index, E element) {
+    throw new UnsupportedOperationException("list is immutable");
+  }
+
+  @Override
+  public E remove(int index) {
+    throw new UnsupportedOperationException("list is immutable");
+  }
+
+  @Override
+  public int indexOf(Object o) {
+    if (elem.equals(o)){
+      return 0;
+    } else {
+      return -1;
+    }
+  }
+
+  @Override
+  public int lastIndexOf(Object o) {
+    return indexOf(o);
+  }
+
+  @Override
+  public ListIterator<E> listIterator() {
+    return new SingleElemIterator();
+  }
+
+  @Override
+  public ListIterator<E> listIterator(int index) {
+    if (index == 0){
+      return new SingleElemIterator();
+    } else {
+      throw new IndexOutOfBoundsException(Integer.toString(index));      
+    }
+  }
+
+  @Override
+  public List<E> subList(int fromIndex, int toIndex) {
+    throw new UnsupportedOperationException("single element list");
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/SortedArrayIntSet.java b/src/main/gov/nasa/jpf/util/SortedArrayIntSet.java
new file mode 100644 (file)
index 0000000..7b65363
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+/**
+ * a set of integers that uses a simple sorted int array and binary search
+ * 
+ * To be used in a context where
+ * 
+ *  - the number of elements does not have a hard limit
+ *  - the number of elements is assumed to be small, but potentially sparse
+ *  - the following operations are time critical
+ *     + inclusion check
+ *     + size check
+ *     + cloning
+ *     + iteration over elements
+ *  - adding/removing should be better than O(N)
+ *  
+ */
+public class SortedArrayIntSet extends ArrayIntSet {
+  
+  static final int DEFAULT_CAPACITY = 8;
+  static final int GROWTH = 8;
+      
+  //--- private methods
+  
+  // returns the index where the match should be
+  // caller has to make sure size > 0
+  protected final int bisect (int val){
+    int min = 0;
+    int max = size-1;
+    int[] a = elements;
+    
+    while (max > min) {
+      int mid = (min + max) / 2;
+      
+      if (a[mid] < val) {
+        min = mid + 1;
+      } else {
+        max = mid;
+      }
+    }
+    
+    return min;
+  }
+  
+  
+  // if we already have elements, idx has to be within range
+  protected final void insertElement (int idx){
+    if (elements == null){
+      elements = new int[DEFAULT_CAPACITY];
+     
+    } else {
+      int[] a = elements;      
+      
+      if (size == a.length){
+        int newLength = a.length + GROWTH;
+        int[] newElements = new int[newLength];
+        if (idx > 0){
+          System.arraycopy(a, 0, newElements, 0, idx);
+        }
+        if (idx < size){
+          System.arraycopy(a, idx, newElements, idx+1, size-idx);
+        }
+        elements = newElements;
+        
+      } else {
+        System.arraycopy(a, idx, a, idx+1, size-idx);
+      }
+    }
+  }
+  
+  
+  //--- public methods
+  
+  public SortedArrayIntSet (){
+    // nothing
+  }
+  
+  public SortedArrayIntSet (int initialCapacity){
+    super(initialCapacity);
+  }
+  
+  @Override
+  public boolean contains (int v) {
+    return ((size > 0) && elements[bisect(v)] == v);      
+  }
+  
+  @Override
+  public boolean add (int v){
+    if (size == 0){
+      elements = new int[DEFAULT_CAPACITY];
+      elements[0] = v;
+      size++;
+      return true;
+      
+    } else {
+      int i = bisect(v);
+      int e = elements[i];
+      if (e != v){
+        if (e < v) {
+          i++;
+        }
+        
+        insertElement(i);
+        elements[i] = v;
+        size++;
+        return true;
+        
+      } else {
+        return false; // was already there
+      }
+    }
+  }
+    
+  @Override
+  public boolean remove (int v) {
+    int len = size;
+    
+    if (len > 0){
+      int[] a = elements;
+      int i = bisect(v);
+      if (a[i] == v) {
+        len--;
+        if (len == 0){
+          elements = null;
+          size = 0;
+          
+        } else {
+          if (i < len){
+            System.arraycopy(a, i + 1, a, i, (len - i));          
+          }
+          size = len;
+        }
+        
+        return true;
+      }
+    }
+    
+    return false; // wasn't there
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/SortedArrayObjectSet.java b/src/main/gov/nasa/jpf/util/SortedArrayObjectSet.java
new file mode 100644 (file)
index 0000000..7e0a3f6
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * a generic set of comparable objects with value based inclusion check
+ * (i.e. using equals())
+ * objects that return compareTo() == 0 are entered LIFO
+ */
+public class SortedArrayObjectSet<T extends Comparable<T>> implements Iterable<T> {
+
+  static final int DEFAULT_CAPACITY = 8;
+  static final int GROWTH = 8;
+      
+  class StorageIterator implements Iterator<T>{
+    int next;
+    
+    @Override
+       public boolean hasNext() {
+      return (next < size);
+    }
+
+    @Override
+       public T next() {
+      if (next < size){
+        return (T)elements[next++];
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      int idx = next-1;
+      if (idx >=0){
+        if (idx < size-1){
+          System.arraycopy(elements, next, elements, idx, size-idx);
+        }
+        size--;
+        next = idx;
+      }
+    }    
+  }
+  
+  
+  int size;
+  Comparable<T>[] elements;
+  
+  //--- private methods
+  
+  // returns the index where the match should be
+  // caller has to make sure size > 0
+  protected final int bisect (T val){
+    int min = 0;
+    int max = size-1;
+    Comparable<T>[] a = elements;
+    
+    while (max > min) {
+      int mid = (min + max) / 2;
+      
+      if (a[mid].compareTo(val) < 0) {
+        min = mid + 1;
+      } else {
+        max = mid;
+      }
+    }
+    
+    return min;
+  }
+  
+  
+  // if we already have elements, idx has to be within range
+  protected final void insertElement (int idx){
+    if (elements == null){
+      elements = new Comparable[DEFAULT_CAPACITY];
+     
+    } else {
+      Comparable[] a = elements;      
+      
+      if (size == a.length){
+        int newLength = a.length + GROWTH;
+        Comparable[] newElements = new Comparable[newLength];
+        if (idx > 0){
+          System.arraycopy(a, 0, newElements, 0, idx);
+        }
+        if (idx < size){
+          System.arraycopy(a, idx, newElements, idx+1, size-idx);
+        }
+        elements = newElements;
+        
+      } else {
+        System.arraycopy(a, idx, a, idx+1, size-idx);
+      }
+    }
+  }
+  
+  
+  //--- public methods
+  
+  public SortedArrayObjectSet (){
+    // nothing
+  }
+  
+  public SortedArrayObjectSet (int initialCapacity){
+    elements = new Comparable[initialCapacity];
+  }
+  
+  public int size(){
+    return size;
+  }
+  
+  public boolean isEmpty(){
+    return (size == 0);
+  }
+  
+  public boolean contains (T v) {
+    return ((size > 0) && elements[bisect(v)].equals(v));      
+  }
+  
+  public void add (T v){
+    if (size == 0){
+      elements = new Comparable[DEFAULT_CAPACITY];
+      elements[0] = v;
+      size++;
+      
+    } else {
+      int i = bisect(v);
+      Comparable<T> e = elements[i];
+      if (!e.equals(v)){
+        if (e.compareTo(v) < 0){
+          i++;
+        }
+        
+        insertElement(i);
+        elements[i] = v;
+        size++;
+      }
+    }
+  }
+    
+  public void remove (T v) {
+    int len = size;
+    
+    if (len > 0){
+      Comparable<T>[] a = elements;
+      
+      for (int i = bisect(v); i<size && a[i].compareTo(v) == 0; i++) {
+        if (v.equals(a[i])){
+          len--;
+          if (len == 0) {
+            elements = null;
+            size = 0;
+
+          } else {
+            if (i < len) {
+              System.arraycopy(a, i + 1, a, i, (len - i));
+            }
+            size = len;
+          }
+          
+          return;
+        }
+      }
+    }    
+  }
+
+  @Override
+  public Iterator<T> iterator(){
+    return new StorageIterator();
+  }
+  
+  @Override
+  public String toString (){
+    
+    StringBuilder sb = new StringBuilder("{");
+    
+    for (int i=0; i<size; i++){
+      if (i > 0){
+        sb.append(',');
+      }
+      sb.append(elements[i]);
+    }
+    
+    sb.append('}');
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Source.java b/src/main/gov/nasa/jpf/util/Source.java
new file mode 100644 (file)
index 0000000..15ee49e
--- /dev/null
@@ -0,0 +1,335 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Hashtable;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+import java.util.logging.Logger;
+
+
+/**
+ * utility class to access arbitrary source files by line number
+ * sources can be files inside of root directories, or
+ * can be entries in jars
+ */
+public class Source {
+
+  static Logger logger = JPF.getLogger("gov.nasa.jpf.util.Source");
+
+  static List<SourceRoot> sourceRoots;
+  static Hashtable<String,Source> sources = new Hashtable<String,Source>();
+  static Source noSource = new Source(null, null);
+
+  static abstract class SourceRoot { // common base
+    abstract InputStream getInputStream (String fname);
+  }
+
+  static class DirRoot extends SourceRoot {
+    String path;
+
+    DirRoot (String path){
+      this.path = path;
+    }
+
+    @Override
+       InputStream getInputStream (String fname) {
+      if (File.separatorChar != '/'){
+        fname = fname.replace('/', File.separatorChar);
+      }
+
+      File f = new File(path, fname);
+      if (f.exists()) {
+        try {
+          return new FileInputStream(f);
+        } catch (FileNotFoundException fnfx) {
+          return null;
+        }
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+       public boolean equals (Object other){
+      return (other != null) && (other instanceof DirRoot) &&
+              path.equals(((DirRoot)other).path);
+    }
+
+    @Override
+       public String toString() {
+      return path;
+    }
+  }
+
+  static class JarRoot extends SourceRoot {
+    JarFile jar;
+    String  entryPrefix;
+
+    JarRoot (String path, String ep) throws IOException {
+      jar = new JarFile(path);
+
+      if (ep == null) {
+        entryPrefix = null;
+      } else {
+        entryPrefix = ep;
+        if (ep.charAt(ep.length()-1) != '/') {
+          entryPrefix += '/';
+        }
+      }
+    }
+
+    @Override
+       InputStream getInputStream (String fname) {
+      String en = (entryPrefix != null) ? entryPrefix + fname : fname;
+      JarEntry entry = jar.getJarEntry(en);
+      if (entry != null) {
+        try {
+          return jar.getInputStream(entry);
+        } catch (IOException e) {
+          return null;
+        }
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+       public boolean equals (Object other){
+      if ( (other != null) && (other instanceof JarRoot)){
+
+        // just how hard can it be to check if two JarFiles instances refer to
+        // the same file?
+        JarRoot o = (JarRoot)other;
+        File f = new File(jar.getName());
+        File fOther = new File(o.jar.getName());
+        if (f.getAbsolutePath().equals(fOther.getAbsolutePath())){
+          if (entryPrefix == null){
+            return o.entryPrefix == null;
+          } else {
+            entryPrefix.equals(o.entryPrefix);
+          }
+        }
+      }
+
+      return false;
+    }
+
+    @Override
+       public String toString() {
+      return jar.getName();
+    }
+  }
+
+  static void addSourceRoot (Config config, List<SourceRoot> roots, String spec){
+    SourceRoot sr = null;
+
+    try {
+      int i = spec.indexOf(".jar");
+      if (i >= 0) {  // jar
+        String pn = FileUtils.asPlatformPath(spec.substring(0, i + 4));
+        File jar = new File(pn);
+        if (jar.exists()) {
+          int i0 = i + 5; // scrub the leading path separator
+          // JarFile assumes Unix for archive-internal paths (also on Windows)
+          String ep = (spec.length() > i0) ? FileUtils.asCanonicalUnixPath(spec.substring(i0)) : null;
+          // we should probably check here if there is such a dir in the Jar
+          sr = new JarRoot(pn, ep);
+        }
+
+      } else {       // directory
+        String pn = FileUtils.asPlatformPath(spec);
+        File dir = new File(pn);
+        if (dir.exists()) {
+          sr = new DirRoot(pn);
+        }
+      }
+    } catch (IOException iox) {
+      // we report this below
+      }
+
+    if (sr != null) {
+      if (!roots.contains(sr)){
+        roots.add(sr);
+      }
+    } else {
+      logger.info("not a valid source root: " + spec);
+    }
+  }
+
+  static String findSrcRoot (String cpEntry){
+    if (cpEntry.endsWith(".jar")){
+      // check if there is a 'src' dir in the jar
+      try {
+        JarFile jf = new JarFile(cpEntry);
+        JarEntry srcEntry = jf.getJarEntry("src");
+        if (srcEntry != null && srcEntry.isDirectory()) {
+          return jf.getName() + "/src"; // jar internal paths use '/' separators
+        }
+      } catch (IOException iox){
+        return null;
+      }
+
+    } else { // is it a dir?
+      File cpe = new File(cpEntry);
+      if (cpe.isDirectory()){
+        // go up until you hit a dir that has a 'src' subdir
+        // remember the traversed path elements
+        LinkedList<String> dirStack = new LinkedList<String>();
+        dirStack.addFirst(cpe.getName());
+        for (File pd = cpe.getParentFile(); pd != null; pd = pd.getParentFile()){
+          File sd = new File(pd,"src");
+          if (sd.isDirectory()){
+            String srcRoot = sd.getPath();
+            for (String e : dirStack) {
+              srcRoot = srcRoot + File.separatorChar + e;
+            }
+            sd = new File(srcRoot);
+            if (sd.isDirectory()){
+              return srcRoot;
+            }
+          } else {
+            dirStack.addFirst(pd.getName());
+          }
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public static void init (Config config) {
+    ArrayList<SourceRoot> roots = new ArrayList<SourceRoot>();
+
+    String[] srcPaths = config.getCompactStringArray("sourcepath");
+    if (srcPaths != null){
+      for (String e : srcPaths){
+        addSourceRoot(config, roots, e);
+      }
+    }
+
+    sourceRoots = roots;
+    sources.clear();
+    
+    //printRoots();
+  }
+
+  // for debugging purposes
+  static void printRoots() {
+    System.out.println("source roots:");
+    for (SourceRoot sr : sourceRoots){
+      System.out.println("  " + sr);
+    }
+  }
+
+  public static Source getSource (String relPathName) {
+    if (relPathName == null){
+      return null;
+    }
+    
+    Source s = sources.get(relPathName);
+    if (s == noSource) {
+       return null;
+    }
+
+    if (s == null) {
+      for (SourceRoot root : sourceRoots) {
+        InputStream is = root.getInputStream(relPathName);
+        if (is != null) {
+          try {
+          s = new Source(root,relPathName);
+          s.loadLines(is);
+          is.close();
+
+          sources.put(relPathName, s);
+          return s;
+          } catch (IOException iox) {
+            logger.warning("error reading " + relPathName + " from" + root);
+            return null;
+          }
+        }
+      }
+    } else {
+      return s;
+    }
+
+    sources.put(relPathName, noSource);
+    return null;
+  }
+
+  //--- the Source instance data itself
+  protected SourceRoot root;
+  protected String     fname;
+  protected String[]   lines;
+
+
+  protected Source (SourceRoot root, String fname) {
+    this.root = root;
+    this.fname = fname;
+  }
+
+  protected void loadLines (InputStream is) throws IOException {
+    BufferedReader in = new BufferedReader(new InputStreamReader(is));
+
+    ArrayList<String> l = new ArrayList<String>();
+    for (String line = in.readLine(); line != null; line = in.readLine()) {
+      l.add(line);
+    }
+    in.close();
+
+    if (l.size() > 0) {
+      lines = l.toArray(new String[l.size()]);
+    }
+  }
+
+
+  /**
+   * this is our sole purpose in life - answer line strings
+   * line index is 1-based
+   */
+  public String getLine (int i) {
+    if ((lines == null) || (i <= 0) || (i > lines.length)) {
+      return null;
+    } else {
+      return lines[i-1];
+    }
+  }
+
+  public int getLineCount()
+  {
+     return(lines.length);
+  }
+
+  public String getPath() {
+    return root.toString() + File.separatorChar + fname;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SourceRef.java b/src/main/gov/nasa/jpf/util/SourceRef.java
new file mode 100644 (file)
index 0000000..8d946fd
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * a source reference abstraction wrapping file and line information
+ */
+public class SourceRef {
+  public String fileName;
+  public int    line;
+
+  public SourceRef (String f, int l) {
+    if (f == null) {
+      fileName = "?";
+    } else {
+      fileName = f;
+    }
+    
+    line = l;
+  }
+
+  public SourceRef (String spec){
+    int idx = spec.indexOf(':');
+    if (idx > 0){
+      fileName = spec.substring(0, idx);
+      line = Integer.parseInt(spec.substring(idx+1));
+    } else {
+      fileName = spec;
+      line = 0;
+    }
+  }
+
+  public String getLocationString() {
+    return (fileName + ':' + line);
+  }
+  
+  public String getLineString () {
+    Source source = Source.getSource(fileName);
+    if (source != null) {
+      return source.getLine(line);
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o == null) {
+      return false;
+    }
+
+    if (!(o instanceof SourceRef)) {
+      return false;
+    }
+
+    SourceRef that = (SourceRef) o;
+
+    if (this.fileName == null) {
+      return false;
+    }
+
+    if (this.line == -1) {
+      return false;
+    }
+
+    if (!this.fileName.equals(that.fileName)) {
+      return false;
+    }
+
+    if (this.line != that.line) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public boolean equals (String f, int l) {
+    if (fileName == null) {
+      return false;
+    }
+
+    if (line == -1) {
+      return false;
+    }
+
+    if (!fileName.equals(f)) {
+      return false;
+    }
+
+    if (line != l) {
+      return false;
+    }
+
+    return true;
+  }
+
+  public boolean equals (String filePos){
+    if (filePos.startsWith(fileName)){
+      int len = fileName.length();
+      if (filePos.charAt(len) == ':'){
+        if (Integer.parseInt(filePos.substring(len+1)) == line){
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  public int hashCode() {
+    assert false : "hashCode not designed";
+    return 42; // any arbitrary constant will do
+    // thanks, FindBugs!
+  }
+
+  public String getFileName () {
+    return fileName;
+  }
+
+  public void set (SourceRef sr) {
+    fileName = sr.fileName;
+    line = sr.line;
+  }
+
+  @Override
+  public String toString () {
+    return (fileName + ':' + line);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SparseClusterArray.java b/src/main/gov/nasa/jpf/util/SparseClusterArray.java
new file mode 100644 (file)
index 0000000..baa3b10
--- /dev/null
@@ -0,0 +1,783 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * A generic sparse reference array that assumes clusters, and more
+ * frequent intra-cluster access.
+ *
+ * This is motivated by looking for a more suitable ADT to model a heap address
+ * space, where reference values consist of segment/offset pairs, we have
+ * reasonably dense and very dynamic population inside of well separated segment
+ * clusters, and intra-cluster access is more likely than inter-cluster access.
+ *
+ * An especially important feature is to be able to iterate efficiently over
+ * set/unset elements in index intervals (cluster sizes).
+ *
+ * The result should find a compromise between the fast element access & iteration
+ * of a simple, dense array, and the efficient memory storage of a HashMap
+ * (if only it could avoid box objects).
+ *
+ * <2do> so far, we are totally ignorant of population constraints
+ */
+public class SparseClusterArray <E> implements Iterable<E> {
+
+  public static final int CHUNK_BITS = 8;
+  public static final int CHUNK_SIZE = 256;
+  public static final int N_ELEM = 1 << CHUNK_BITS;     // 8 bits chunk index -> 24 bits segment key (3x8bits / 256 segs)
+  protected static final int ELEM_MASK = 0xff;
+  protected static final int BM_ENTRIES = N_ELEM / 64;     // number of bitmap long entries
+  protected static final int MAX_BM_INDEX = BM_ENTRIES-1;
+
+
+  // 8 bits per segment -> 256 children
+  public static final int SEG_BITS = 8;
+  public static final int N_SEG = 1 << SEG_BITS;
+  protected static final int SEG_MASK = 0xff;
+  public static final int S1 = 32-SEG_BITS; // L1 shift
+  public static final int S2 = S1-SEG_BITS; // L2 shift
+  public static final int S3 = S2-SEG_BITS; // L3 shift
+  protected static final int CHUNK_BASEMASK = ~SEG_MASK;
+
+  public static final int MAX_CLUSTERS = CHUNK_SIZE;      // max int with CHUNK_BITS bits (8)
+  public static final int MAX_CLUSTER_ENTRIES = 0xffffff; // max int with 32-CHUNK_BITS bits (24) = 16,777,215 elements
+
+  protected Root root;
+  protected Chunk lastChunk;
+  protected Chunk head;   // linked list for traversal
+  protected int   nSet; // number of set elements;
+
+  protected boolean trackChanges = false;
+  protected Entry changes; // on demand change (LIFO) queue
+
+  //------------------------------------ public types
+  public static class Snapshot<T,E> {
+    Object[] values;
+    int[] indices;
+
+    public Snapshot (int size){
+      values = new Object[size];
+      indices = new int[size];
+    }
+
+    public int size() {
+      return indices.length;
+    }
+    public T getValue(int i){
+      return (T) values[i];
+    }
+    public int getIndex(int i){
+      return indices[i];
+    }
+  }
+
+
+  public static class Entry<E> {  // queued element
+    int index;
+    E value;
+
+    Entry<E> next;
+
+    Entry (int index, E value){
+      this.index = index;
+      this.value = value;
+    }
+  }
+
+  //------------------------------------ internal types
+
+  //--- how we keep our data - index based trie
+  protected static class Root {
+    public Node[] seg = new Node[N_SEG];
+  }
+
+  /**
+   * this corresponds to a toplevel cluster (e.g. thread heap)
+   */
+  protected static class Node  {
+    public ChunkNode[] seg = new ChunkNode[N_SEG];
+    //int minNextFree; // where to start looking for free elements, also used to determine if Node is full
+  }
+
+  protected static class ChunkNode  {
+    public Chunk[] seg  = new Chunk[N_SEG];
+    //int minNextFree; // where to start looking for free elements, also used to determine if ChunkNode is full
+  }
+
+  protected static class Chunk implements Cloneable { // with some extra info to optimize in-chunk access
+    public int base, top;
+    public Chunk next;
+    public Object[] elements;  // it's actually E[], but of course we can't create arrays of a generic type
+    public long[] bitmap;
+
+    //int minNextFree; // where to start looking for free elements, also used to determine if Chunk is full
+
+    protected Chunk() {}
+
+    protected Chunk(int base){
+      this.base = base;
+      this.top = base + N_ELEM;
+
+      elements = new Object[N_ELEM];
+      bitmap = new long[BM_ENTRIES];
+    }
+
+    @Override
+       public String toString() {
+      return "Chunk [base=" + base + ",top=" + top + ']';
+    }
+
+    @SuppressWarnings("unchecked")
+    public <E> Chunk deepCopy( Cloner<E> cloner) throws CloneNotSupportedException {
+      Chunk nc = (Chunk) super.clone();
+
+      E[] elem = (E[])elements;   // bad, but we have to cope with type erasure
+      Object[] e = new Object[N_ELEM];
+
+      for (int i=nextSetBit(0); i>=0; i=nextSetBit(i+1)) {
+        e[i] = cloner.clone(elem[i]);
+      }
+
+      nc.elements = e;
+      nc.bitmap = bitmap.clone();
+
+      return nc;
+    }
+
+    protected int nextSetBit (int iStart) {
+      if (iStart < CHUNK_SIZE){
+        long[] bm = bitmap;
+        int j = (iStart >> 6); // bm word : iStart/64
+        long l = bm[j] & (0xffffffffffffffffL << iStart);
+
+        while (true) {
+          if (l != 0) {
+            return Long.numberOfTrailingZeros(l) + (j << 6);
+          } else {
+            if (++j < BM_ENTRIES) {
+              l = bm[j];
+            } else {
+              return -1;
+            }
+          }
+        }
+      } else {
+        return -1;
+      }
+    }
+
+    protected int nextClearBit (int iStart) {
+      if (iStart < CHUNK_SIZE){
+        long[] bm = bitmap;
+        int j = (iStart >> 6); // bm word : iStart/64
+        long l = ~bm[j] & (0xffffffffffffffffL << iStart);
+
+        while (true) {
+          if (l != 0) {
+            return Long.numberOfTrailingZeros(l) + (j << 6);
+          } else {
+            if (++j < BM_ENTRIES) {
+              l = ~bm[j];
+            } else {
+              return -1;
+            }
+          }
+        }
+      } else {
+        return -1;
+      }
+    }
+
+
+    public boolean isEmpty() {
+      long[] bm = bitmap;
+
+      for (int i=0; i<BM_ENTRIES; i++){
+        if (bm[i] != 0) return false;
+      }
+
+      return true;
+    }
+  }
+
+  //--- iteration over set elements
+
+  protected class ElementIterator<T>  implements Iterator<T>, Iterable<T> {
+    int idx;    // next chunk index
+    Chunk cur;  // next chunk
+
+    public ElementIterator () {
+      for (Chunk c = head; c != null; c = c.next){
+        int i = c.nextSetBit(0);
+        if (i>=0){
+          cur = c;
+          idx = i;
+          return;
+        }
+      }
+    }
+
+    @Override
+       public boolean hasNext() {
+      return (cur != null);
+    }
+
+    @Override
+       @SuppressWarnings("unchecked")
+    public T next() {
+      Chunk c = cur;
+      int i = idx;
+
+      if (i < 0 || c == null){
+        throw new NoSuchElementException();
+      }
+
+      Object ret = c.elements[i];
+      cur = null;
+
+      while (c!=null){
+        i = c.nextSetBit(i+1);
+        if (i>= 0){
+          idx = i;
+          cur = c;
+
+          if (ret == null){
+            // try to recover from a concurrent modification, maybe there is one left
+            ret = c.elements[i];
+            continue;
+          } else {
+            break;
+          }
+        } else {
+          i = -1;
+        }
+        c = c.next;
+      }
+
+      if (ret == null){
+        // somebody pulled the rug under our feet
+        throw new ConcurrentModificationException();
+      }
+      return (T)ret;
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException();
+    }
+
+    @Override
+       public Iterator<T> iterator() {
+      return this;
+    }
+  }
+
+  protected class ElementIndexIterator implements IndexIterator {
+    int idx;
+    Chunk cur;
+
+    public ElementIndexIterator () {
+      for (Chunk c = head; c != null; c = c.next){
+        int i = c.nextSetBit(0);
+        if (i>=0){
+          cur = c;
+          idx = i;
+          return;
+        }
+      }
+    }
+
+    public ElementIndexIterator (int startIdx){
+      // locate the start chunk (they are sorted)
+      Chunk c;
+      int i;
+
+      // get the first chunk at or above the startIdx
+      for (c=head; c!= null; c=c.next) {
+        if (c.top > startIdx) {
+          cur = c;
+          break;
+        }
+      }
+
+      if (c.base < startIdx){
+        i = startIdx & ELEM_MASK;
+      } else {
+        i = 0;
+      }
+
+      for (; c != null; c = c.next){
+        i = c.nextSetBit(i);
+        if (i>=0){
+          cur = c;
+          idx = i;
+          return;
+        } else {
+          i = 0;
+        }
+      }
+    }
+
+
+    @Override
+       public int next () {
+      Chunk c = cur;
+      int i = idx;
+
+      if (i < 0 || c == null){
+        return -1;
+      }
+
+      int iRet = (c.elements[i] != null) ? c.base + i : -1;
+      cur = null;
+
+      while (c!=null){
+        i = c.nextSetBit(i+1);
+        if (i>= 0){
+          idx = i;
+          cur = c;
+
+          if (iRet < 0){
+            // try to recover from a concurrent modification, maybe there is one left
+            iRet = c.base + i;
+            continue;
+          } else {
+            break;
+          }
+        } else {
+          i = -1;
+        }
+        c = c.next;
+      }
+
+      if (iRet < 0){
+        // somebody pulled the rug under our feet
+        throw new ConcurrentModificationException();
+      }
+      return iRet;
+    }
+
+  }
+
+
+  //------------------------------------ internal methods
+
+  void sortInChunk (Chunk newChunk) {
+    if (head == null) {
+      head = newChunk;
+    } else {
+      int base = newChunk.base;
+      if (base < head.base) {
+        newChunk.next = head;
+        head = newChunk;
+      } else {
+        Chunk cprev, c;
+        for (cprev=head, c=cprev.next; c != null; cprev=c, c=c.next) {
+          if (base < c.base) {
+            newChunk.next = c;
+            break;
+          }
+        }
+        cprev.next = newChunk;
+      }
+    }
+  }
+
+  //------------------------------------ public API
+
+  public SparseClusterArray (){
+    root = new Root();
+  }
+
+  /**
+   * be careful, this should only be used to get old stored elements during
+   * a Snapshot restore
+   */
+  protected SparseClusterArray (SparseClusterArray base){
+    root = base.root;
+    nSet = base.nSet;
+    head = base.head;
+  }
+
+  @SuppressWarnings("unchecked")
+  public E get (int i) {
+    Node l1;
+    ChunkNode l2;
+    Chunk l3 = lastChunk;
+
+    if (i < 0){
+      throw new IndexOutOfBoundsException();
+    }
+
+    if (l3 != null && (l3.base == (i & CHUNK_BASEMASK))) {  // cache optimization for in-cluster access
+      return (E) l3.elements[i & ELEM_MASK];
+    }
+
+    int  j = i >>>  S1;
+    if ((l1 = root.seg[j]) != null) {           // L1
+      j = (i >>> S2) & SEG_MASK;
+      if ((l2 = l1.seg[j]) != null) {           // L2
+        j = (i >>> S3) & SEG_MASK;
+        if ((l3 = l2.seg[j]) != null) {         // L3
+          // too bad we can't get rid of this cast
+          lastChunk = l3;
+          return  (E) l3.elements[i & ELEM_MASK];
+        }
+      }
+    }
+
+    lastChunk = null;
+    return null;
+  }
+
+
+  public void set (int i, E e) {
+    Node l1;
+    ChunkNode l2;
+    Chunk l3 = lastChunk;
+    int j;
+
+    if (i < 0){
+      throw new IndexOutOfBoundsException();
+    }
+
+    if (l3 == null || (l3.base != (i & CHUNK_BASEMASK))) { // cache optimization for in-cluster access
+      j = i >>>  S1;
+      if ((l1 = root.seg[j]) == null) {         // new L1 -> new L2,L3
+        l1 = new Node();
+        root.seg[j] = l1;
+
+        j = (i >>> S2) & SEG_MASK;
+        l2 = new ChunkNode();
+        l1.seg[j] = l2;
+
+        j = (i >>> S3) & SEG_MASK;
+        l3 = new Chunk(i & ~ELEM_MASK);
+        sortInChunk(l3);
+        l2.seg[j] = l3;
+
+      } else {                                  // had L1
+        j = (i >>> S2) & SEG_MASK;
+        if ((l2 = l1.seg[j]) == null) {         // new L2 -> new L3
+          l2 = new ChunkNode();
+          l1.seg[j] = l2;
+
+          j = (i >>> S3) & SEG_MASK;
+          l3 = new Chunk(i & ~ELEM_MASK);
+          sortInChunk(l3);
+          l2.seg[j] = l3;
+
+        } else {                                // had L2
+          j = (i >>> S3) & SEG_MASK;
+          if ((l3 = l2.seg[j]) == null) {       // new L3
+            l3 = new Chunk(i & ~ELEM_MASK);
+            sortInChunk(l3);
+            l2.seg[j] = l3;
+          }
+        }
+      }
+
+      lastChunk = l3;
+    }
+
+    j = i & ELEM_MASK;
+
+    long[] bm = l3.bitmap;
+    int u = (j >> 6);    // j / 64 (64 bits per bm entry)
+    int v = (i & 0x7f);  // index into bm[u] bitset
+    boolean isSet = ((bm[u] >> v) & 0x1) > 0;
+
+    if (trackChanges) {
+      Entry entry = new Entry(i,l3.elements[j]);
+      entry.next = changes;
+      changes = entry;
+    }
+
+    if (e != null) {
+      if (!isSet) {
+        l3.elements[j] = e;
+        bm[u] |= (1L<<v);
+        nSet++;
+      }
+
+    } else {
+      if (isSet) {
+        l3.elements[j] = null;
+        bm[u] &= ~(1L<<v);
+        nSet--;
+        // <2do> discard upwards if chunk is empty ? (maybe as an option)
+      }
+    }
+  }
+
+  /**
+   * find first null element within given range [i, i+length[
+   * @return -1 if there is none
+   */
+  public int firstNullIndex (int i, int length) {
+    Node l1;
+    ChunkNode l2;
+    Chunk l3 = lastChunk;
+    int j;
+    int iMax = i + length;
+
+    if (l3 == null || (l3.base != (i & CHUNK_BASEMASK))) { // cache optimization for in-cluster access
+      j = i >>>  S1;
+      if ((l1 = root.seg[j]) != null) {         // new L1 -> new L2,L3
+        j = (i >>> S2) & SEG_MASK;
+        if ((l2 = l1.seg[j]) != null) {         // new L2 -> new L3
+          j = (i >>> S3) & SEG_MASK;
+          if ((l3 = l2.seg[j]) == null){
+            return i; // no such l3 segment -> index is free
+          }
+        } else {
+          return i; // no such l2 segment yet -> index is free
+        }
+      } else { // we don't have that root segment yet -> index is free
+        return i;
+      }
+    }
+
+    int k = i & SEG_MASK;
+    while (l3 != null) {
+      k = l3.nextClearBit(k);
+
+      if (k >= 0) {             // Ok, got one in the chunk
+        lastChunk = l3;
+        i = l3.base + k;
+        return (i < iMax) ? i : -1;
+
+      } else {                  // chunk full
+        Chunk l3Next = l3.next;
+        int nextBase = l3.base + CHUNK_SIZE;
+        if ((l3Next != null) && (l3Next.base == nextBase)) {
+          if (nextBase < iMax) {
+            l3 = l3Next;
+            k=0;
+          } else {
+            return -1;
+          }
+        } else {
+          lastChunk = null;
+          return (nextBase < iMax) ? nextBase : -1;
+        }
+      }
+    }
+
+    // no allocated chunk for 'i'
+    lastChunk = null;
+    return i;
+  }
+
+  /**
+   * deep copy
+   * we need to do this depth first, right-to-left, to maintain the
+   * Chunk list ordering. We also compact during cloning, i.e. remove
+   * empty chunks and ChunkNodes/Nodes w/o descendants
+   */
+  public SparseClusterArray<E> deepCopy (Cloner<E> elementCloner) {
+    SparseClusterArray<E> a = new SparseClusterArray<E>();
+    a.nSet = nSet;
+
+    Node[] newNodeList = a.root.seg;
+
+    Node newNode = null;
+    ChunkNode newChunkNode = null;
+    Chunk newChunk = null, lastChunk = null;
+
+    Node[] nList = root.seg;
+
+    try {
+      for (int i=0, i1=0; i<nList.length; i++) {
+        Node n = nList[i];
+        if (n != null) {
+          ChunkNode[] cnList = n.seg;
+
+          for (int j=0, j1=0; j<cnList.length; j++) {
+            ChunkNode cn = cnList[j];
+            if (cn != null) {
+              Chunk[] cList = cn.seg;
+
+              for (int k=0, k1=0; k<cList.length; k++) {
+                Chunk c = cList[k];
+
+                if (c != null && !c.isEmpty()) {
+                  newChunk = c.deepCopy(elementCloner);
+                  if (lastChunk == null) {
+                    a.head = lastChunk = newChunk;
+                  } else {
+                    lastChunk.next = newChunk;
+                    lastChunk = newChunk;
+                  }
+
+                  // create the required ChunkNode/Node instances
+                  if (newNode == null) {
+                    newNode = new Node();
+                    j1 = k1 = 0;
+                    newNodeList[i1++] = newNode;
+                  }
+
+                  if (newChunkNode == null) {
+                    newChunkNode = new ChunkNode();
+                    newNode.seg[j1++] = newChunkNode;
+                  }
+
+                  newChunkNode.seg[k1++] = newChunk;
+                }
+              }
+            }
+            newChunkNode = null;
+          }
+        }
+        newNode = null;
+      }
+    } catch (CloneNotSupportedException cnsx) {
+      return null; // maybe we should re-raise
+    }
+
+    return a;
+  }
+
+  /**
+   * create a snapshot that can be used to restore a certain state of our array
+   * This is more suitable than cloning in case the array is very sparse, or
+   * the elements contain a lot of transient data we don't want to store
+   */
+  public <T> Snapshot<E,T> getSnapshot (Transformer<E,T> transformer){
+    Snapshot<E,T> snap = new Snapshot<E,T>(nSet);
+    populateSnapshot(snap, transformer);
+
+    return snap;
+  }
+
+  protected <T> void populateSnapshot (Snapshot<E,T> snap, Transformer<E,T> transformer){
+    int n = nSet;
+
+    Object[] values = snap.values;
+    int[] indices = snap.indices;
+
+    int j=0;
+    for (Chunk c = head; c != null; c = c.next) {
+      int base = c.base;
+      int i=-1;
+      while ((i=c.nextSetBit(i+1)) >= 0) {
+        Object val = transformer.transform((E)c.elements[i]);
+        values[j] = val;
+        indices[j] = base + i;
+
+        if (++j >= n) {
+          break;
+        }
+      }
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T> void restore (Snapshot<E,T> snap, Transformer<T,E> transformer) {
+    // <2do> - there are more efficient ways to restore small changes,
+    // but since snapshot elements are ordered it should be reasonably fast
+    clear();
+
+    T[] values = (T[])snap.values;
+    int[] indices = snap.indices;
+    int len = indices.length;
+
+    for (int i=0; i<len; i++){
+      E obj = transformer.transform(values[i]);
+      int index = indices[i];
+
+      set(index,obj);
+    }
+  }
+
+  public void clear() {
+    lastChunk = null;
+    head = null;
+    root = new Root();
+    nSet = 0;
+
+    changes = null;
+  }
+
+  public void trackChanges () {
+    trackChanges = true;
+  }
+
+  public void stopTrackingChanges() {
+    trackChanges = false;
+  }
+
+  public boolean isTrackingChanges() {
+    return trackChanges;
+  }
+
+  public Entry<E> getChanges() {
+    return changes;
+  }
+
+  public void resetChanges() {
+    changes = null;
+  }
+
+  public void revertChanges (Entry<E> changes) {
+    for (Entry<E> e = changes; e != null; e = e.next) {
+      set(e.index, e.value);
+    }
+  }
+
+  @Override
+  public String toString() {
+    return "SparseClusterArray [nSet=" + nSet + ']';
+  }
+
+  public int numberOfElements() {
+    return nSet;
+  }
+  
+  public int numberOfChunks() {
+    // that's only for debugging purposes, we should probably cache
+    int n = 0;
+    for (Chunk c = head; c != null; c = c.next) {
+      n++;
+    }
+    return n;
+  }
+
+  //--- iteration over set elements
+
+  public IndexIterator getElementIndexIterator () {
+    return new ElementIndexIterator();
+  }
+
+  public IndexIterator getElementIndexIterator (int fromIndex) {
+    return new ElementIndexIterator(fromIndex);
+  }
+  
+  @Override
+  public Iterator<E> iterator() {
+    return new ElementIterator<E>();
+  }
+
+  public int cardinality () {
+    return nSet;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SparseIntVector.java b/src/main/gov/nasa/jpf/util/SparseIntVector.java
new file mode 100644 (file)
index 0000000..2d3920b
--- /dev/null
@@ -0,0 +1,408 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import static java.lang.Integer.MIN_VALUE;
+
+import java.util.Arrays;
+
+/**
+ * This has approximately the interface of IntVector but uses a hash table
+ * instead of an array.  Also, does not require allocation with each add.
+ * Configurable default value. 
+ */
+public class SparseIntVector implements Cloneable {
+  private static final boolean DEBUG = false;
+  
+  static final double MAX_LOAD_WIPE = 0.6;
+  static final double MAX_LOAD_REHASH = 0.4;
+  static final int DEFAULT_POW = 10;
+  static final int DEFAULT_VAL = 0;
+
+  /**
+   * a simplistic snapshot implementation that stores set indices/values in order to save space
+   */
+  public static class Snapshot {
+    private final int length;
+    private final int pow, mask, nextWipe, nextRehash;
+    
+    private final int[] positions;
+    private final int[] indices;
+    private final int[] values;
+    
+    Snapshot (SparseIntVector v){
+      int len = v.idxTable.length;
+      
+      length = len;
+      pow = v.pow;
+      mask = v.mask;
+      nextWipe = v.nextWipe;
+      nextRehash = v.nextRehash;
+      
+      int size = v.count;
+      positions = new int[size];
+      indices = new int[size];
+      values = new int[size];
+      
+      int[] idxTable = v.idxTable;
+      int[] valTable = v.valTable;
+      int j=0;
+      for (int i=0; i<len; i++) {
+        if (idxTable[i] != MIN_VALUE) {
+          positions[j] = i;
+          indices[j] = idxTable[i];
+          values[j] = valTable[i];
+          j++;
+        }
+      }
+    }
+    
+    void restore (SparseIntVector v) {
+      int size = indices.length;
+      
+      v.count = size;
+      v.pow = pow;
+      v.mask = mask;
+      v.nextWipe = nextWipe;
+      v.nextRehash = nextRehash;
+      
+      int len = length;
+      int[] idxTable = new int[len];
+      int[] valTable = new int[len];
+      
+      Arrays.fill(idxTable, MIN_VALUE);
+      
+      for (int i=0; i<size; i++) {
+        int j = positions[i];        
+        idxTable[j] = indices[i];
+        valTable[j] = values[i];
+      }
+      
+      v.idxTable = idxTable;
+      v.valTable = valTable;
+    }
+  }
+  
+  int[] idxTable;  // MIN_VALUE => unoccupied
+  int[] valTable;  // can be bound to null
+  
+  int count;
+  int pow;
+  int mask;
+  int nextWipe;
+  int nextRehash;
+  
+  int defaultValue;
+    
+  /**
+   * Creates a SimplePool that holds about 716 elements before first
+   * rehash.
+   */
+  public SparseIntVector() {
+    this(DEFAULT_POW,DEFAULT_VAL);
+  }
+  
+  /**
+   * Creates a SimplePool that holds about 0.7 * 2**pow elements before
+   * first rehash.
+   */
+  public SparseIntVector(int pow, int defValue) {
+    this.pow = pow;
+    newTable();
+    count = 0;
+    mask = valTable.length - 1;
+    nextWipe = (int)(MAX_LOAD_WIPE * mask);
+    nextRehash = (int)(MAX_LOAD_REHASH * mask);
+    defaultValue = defValue;
+  }  
+  
+  // INTERNAL //
+  
+  @SuppressWarnings("unchecked")
+  protected void newTable() {
+    valTable = new int[1 << pow];
+    idxTable = new int[1 << pow];
+    if (defaultValue != 0) {
+      Arrays.fill(valTable, defaultValue);
+    }
+    Arrays.fill(idxTable, MIN_VALUE);
+  }
+  
+  protected int mix(int x) {
+    int y = 0x9e3779b9;
+    x ^= 0x510fb60d;
+    y += (x >> 8) + (x << 3);
+    x ^= (y >> 5) + (y << 2);
+    return y - x;
+  }
+  
+  
+  // ********************* Public API ******************** //
+
+  public Snapshot getSnapshot() {
+    return new Snapshot(this);
+  }
+  
+  public void restore (Snapshot snap) {
+    snap.restore(this);
+  }
+  
+  @Override
+  public SparseIntVector clone() {
+    try {
+      SparseIntVector o = (SparseIntVector) super.clone();
+      o.idxTable = idxTable.clone();
+      o.valTable = valTable.clone();
+      
+      return o;
+      
+    } catch (CloneNotSupportedException cnsx) {
+      // can't happen
+      return null;
+    }
+  }
+  
+  public int size() {
+    return count;
+  }
+  
+  public void clear() {
+    Arrays.fill(valTable, defaultValue);
+    Arrays.fill(idxTable, MIN_VALUE);
+    count = 0;
+  }
+  
+  public void clear(int idx) {
+    int code = mix(idx);
+    int pos = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = pos;
+
+    for(;;) {
+      int tidx = idxTable[pos];
+      if (tidx == MIN_VALUE) {
+        return; // nothing to clear
+      }
+      if (tidx == idx) {
+        count--;
+        idxTable[pos] = MIN_VALUE;
+        valTable[pos] = defaultValue;
+        return;
+      }
+      pos = (pos + delta) & mask;
+      assert (pos != oidx); // should never wrap around
+    }
+  }
+  
+  @SuppressWarnings("unchecked")
+  public int get(int idx) {
+    int code = mix(idx);
+    int pos = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = pos;
+
+    for(;;) {
+      int tidx = idxTable[pos];
+      if (tidx == MIN_VALUE) {
+        return defaultValue;
+      }
+      if (tidx == idx) {
+        return valTable[pos];
+      }
+      pos = (pos + delta) & mask;
+      assert (pos != oidx); // should never wrap around
+    }
+  }
+
+  // for debug only
+  int count() {
+    int count = 0;
+    for (int i = 0; i < idxTable.length; i++) {
+      if (idxTable[i] != MIN_VALUE /*&& valTable[i] != defaultValue*/) {
+        count++;
+      }
+    }
+    return count;
+  }
+  
+  public void set(int idx, int val) {
+    int code = mix(idx);
+    int pos = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = pos;
+
+    for(;;) {
+      int tidx = idxTable[pos];
+      if (tidx == MIN_VALUE) {
+        break;
+      }
+      if (tidx == idx) {
+        valTable[pos] = val; // update
+        return;            // and we're done
+      }
+      pos = (pos + delta) & mask;
+      assert (pos != oidx); // should never wrap around
+    }
+    // idx not in table; add it
+    
+    if ((count+1) >= nextWipe) { // too full
+      if (count >= nextRehash) {
+        pow++;
+      }
+      
+      /**
+      // determine if size needs to be increased or just wipe null blocks
+      int oldCount = count;
+      count = 0;
+      for (int i = 0; i < idxTable.length; i++) {
+        //if (idxTable[i] != MIN_VALUE && valTable[i] != defaultValue) {
+        if (idxTable[i] != MIN_VALUE) {
+          count++;
+        }
+      }
+      if (count >= nextRehash) {
+        pow++; // needs to be increased in size
+        if (DEBUG) {
+          System.out.println("Rehash to capacity: 2**" + pow);
+        }
+      } else {
+        if (DEBUG) {
+          System.out.println("Rehash reclaiming this many nulls: " + (oldCount - count));
+        }
+      }
+      **/
+      
+      int[] oldValTable = valTable;
+      int[] oldIdxTable = idxTable;
+      newTable();
+      mask = idxTable.length - 1;
+      nextWipe = (int)(MAX_LOAD_WIPE * mask);
+      nextRehash = (int)(MAX_LOAD_REHASH * mask);
+
+      int oldLen = oldIdxTable.length;
+      for (int i = 0; i < oldLen; i++) {
+        int tidx = oldIdxTable[i];
+        if (tidx == MIN_VALUE) continue;
+        int o = oldValTable[i];
+        //if (o == defaultValue) continue;
+        // otherwise:
+        code = mix(tidx);
+        pos = code & mask;
+        delta = (code >> (pow - 1)) | 1; // must be odd!
+        while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
+          pos = (pos + delta) & mask;
+        }
+        idxTable[pos] = tidx;
+        valTable[pos] = o;
+      }
+      // done with rehash; now get idx to empty slot
+      code = mix(idx);
+      pos = code & mask;
+      delta = (code >> (pow - 1)) | 1; // must be odd!
+      while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
+        pos = (pos + delta) & mask;
+      }
+            
+    } else {
+      // pos already pointing to empty slot
+    }
+
+    count++;
+
+    idxTable[pos] = idx;
+    valTable[pos] = val;
+  }
+  
+  
+  public void setRange (int fromIndex, int toIndex, int val) {
+    for (int i=fromIndex; i<toIndex; i++) {
+      set(i, val);
+    }
+  }
+  
+  // ************************** Test main ************************ //
+  
+  public static void main(String[] args) {
+    SparseIntVector vect = new SparseIntVector(3, MIN_VALUE);
+    
+    // add some
+    for (int i = -4200; i < 4200; i += 10) {
+      vect.set(i, i);
+    }
+    
+    // check for added & non-added
+    for (int i = -4200; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4205; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != MIN_VALUE) {
+        throw new IllegalStateException();
+      }
+    }
+    
+    // add some more
+    for (int i = -4201; i < 4200; i += 10) {
+      vect.set(i, i);
+    }
+
+    // check all added
+    for (int i = -4200; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4201; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != i) {
+        throw new IllegalStateException();
+      }
+    }
+    
+    // "remove" some
+    for (int i = -4200; i < 4200; i += 10) {
+      vect.set(i,MIN_VALUE);
+    }
+    
+    // check for added & non-added
+    for (int i = -4201; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4200; i < 4200; i += 10) {
+      int v = vect.get(i);
+      if (v != MIN_VALUE) {
+        throw new IllegalStateException();
+      }
+    }
+
+    // add even more
+    for (int i = -4203; i < 4200; i += 10) {
+      vect.set(i, i);
+    }
+    for (int i = -4204; i < 4200; i += 10) {
+      vect.set(i, i);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SparseObjVector.java b/src/main/gov/nasa/jpf/util/SparseObjVector.java
new file mode 100644 (file)
index 0000000..684fc28
--- /dev/null
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import static java.lang.Integer.MIN_VALUE;
+
+import java.util.Arrays;
+
+/**
+ * This has approximately the interface of ObjVector but uses a hash table
+ * instead of an array.  Also, does not require allocation with each add. 
+ */
+public class SparseObjVector<E> {
+  private static final boolean DEBUG = false; 
+  
+  static final double MAX_LOAD_WIPE = 0.6;
+  static final double MAX_LOAD_REHASH = 0.4;
+  static final int DEFAULT_POW = 10;
+
+  int[]    idxTable;  // MIN_VALUE => unoccupied
+  Object[] valTable;  // can be bound to null
+  
+  int count;
+  int pow;
+  int mask;
+  int nextWipe;
+  int nextRehash;
+  
+  
+  /**
+   * Creates a SimplePool that holds about 716 elements before first
+   * rehash.
+   */
+  public SparseObjVector() {
+    this(DEFAULT_POW);
+  }
+  
+  /**
+   * Creates a SimplePool that holds about 0.7 * 2**pow elements before
+   * first rehash.
+   */
+  public SparseObjVector(int pow) {
+    this.pow = pow;
+    newTable();
+    count = 0;
+    mask = valTable.length - 1;
+    nextWipe = (int)(MAX_LOAD_WIPE * mask);
+    nextRehash = (int)(MAX_LOAD_REHASH * mask);
+  }
+
+  public void clear() {
+    pow = DEFAULT_POW;
+    newTable();
+    count = 0;
+    mask = valTable.length - 1;
+    nextWipe = (int) (MAX_LOAD_WIPE * mask);
+    nextRehash = (int) (MAX_LOAD_REHASH * mask);
+  }
+  
+  // INTERNAL //
+  
+  @SuppressWarnings("unchecked")
+  protected void newTable() {
+    valTable = new Object[1 << pow];
+    idxTable = new int[1 << pow];
+    Arrays.fill(idxTable, MIN_VALUE);
+  }
+  
+  protected int mix(int x) {
+    int y = 0x9e3779b9;
+    x ^= 0x510fb60d;
+    y += (x >> 8) + (x << 3);
+    x ^= (y >> 5) + (y << 2);
+    return y - x;
+  }
+  
+  
+  // ********************* Public API ******************** //
+  
+  
+  @SuppressWarnings("unchecked")
+  public E get(int idx) {
+    int code = mix(idx);
+    int pos = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = pos;
+
+    for(;;) {
+      int tidx = idxTable[pos];
+      if (tidx == MIN_VALUE) {
+        return null;
+      }
+      if (tidx == idx) {
+        return (E) valTable[pos];
+      }
+      pos = (pos + delta) & mask;
+      assert (pos != oidx); // should never wrap around
+    }
+  }
+
+  public void set(int idx, E e) {
+    int code = mix(idx);
+    int pos = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = pos;
+
+    for(;;) {
+      int tidx = idxTable[pos];
+      if (tidx == MIN_VALUE) {
+        break;
+      }
+      if (tidx == idx) {
+        valTable[pos] = e; // update
+        return;            // and we're done
+      }
+      pos = (pos + delta) & mask;
+      assert (pos != oidx); // should never wrap around
+    }
+    // idx not in table; add it
+    
+    if ((count+1) >= nextWipe) { // too full
+      if (count >= nextRehash) {
+        pow++;
+      }
+      
+      /**
+      // determine if size needs to be increased or just wipe null blocks
+      int oldCount = count;
+      count = 0;
+      for (int i = 0; i < idxTable.length; i++) {
+        if (idxTable[i] != MIN_VALUE && valTable[i] != null) {
+          count++;
+        }
+      }
+      if (count >= nextRehash) {
+        pow++; // needs to be increased in size
+        if (DEBUG) {
+          System.out.println("Rehash to capacity: 2**" + pow);
+        }
+      } else {
+        if (DEBUG) {
+          System.out.println("Rehash reclaiming this many nulls: " + (oldCount - count));
+        }
+      }
+      **/
+      
+      Object[] oldValTable = valTable;
+      int[] oldIdxTable = idxTable;
+      newTable();
+      mask = idxTable.length - 1;
+      nextWipe = (int)(MAX_LOAD_WIPE * mask);
+      nextRehash = (int)(MAX_LOAD_REHASH * mask);
+
+      int oldLen = oldIdxTable.length;
+      for (int i = 0; i < oldLen; i++) {
+        int tidx = oldIdxTable[i];
+        if (tidx == MIN_VALUE) continue;
+        Object o = oldValTable[i];
+        //if (o == null) continue;
+        // otherwise:
+        code = mix(tidx);
+        pos = code & mask;
+        delta = (code >> (pow - 1)) | 1; // must be odd!
+        while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
+          pos = (pos + delta) & mask;
+        }
+        idxTable[pos] = tidx;
+        valTable[pos] = o;
+      }
+      // done with rehash; now get idx to empty slot
+      code = mix(idx);
+      pos = code & mask;
+      delta = (code >> (pow - 1)) | 1; // must be odd!
+      while (idxTable[pos] != MIN_VALUE) { // we know enough slots exist
+        pos = (pos + delta) & mask;
+      }
+    } else {
+      // pos already pointing to empty slot
+    }
+
+    count++;
+    
+    idxTable[pos] = idx;
+    valTable[pos] = e;
+  }
+  
+  public void remove(int idx) {
+    set(idx, null);
+  }
+  
+  
+  // ************************** Test main ************************ //
+  
+  public static void main(String[] args) {
+    SparseObjVector<Integer> vect = new SparseObjVector<Integer>(3);
+    
+    // add some
+    for (int i = -4200; i < 4200; i += 10) {
+      vect.set(i, new Integer(i));
+    }
+    
+    // check for added & non-added
+    for (int i = -4200; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v.intValue() != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4205; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v != null) {
+        throw new IllegalStateException();
+      }
+    }
+    
+    // add some more
+    for (int i = -4201; i < 4200; i += 10) {
+      vect.set(i, new Integer(i));
+    }
+
+    // check all added
+    for (int i = -4200; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v.intValue() != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4201; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v.intValue() != i) {
+        throw new IllegalStateException();
+      }
+    }
+    
+    // "remove" some
+    for (int i = -4200; i < 4200; i += 10) {
+      vect.remove(i);
+    }
+    
+    // check for added & non-added
+    for (int i = -4201; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v.intValue() != i) {
+        throw new IllegalStateException();
+      }
+    }
+    for (int i = -4200; i < 4200; i += 10) {
+      Integer v = vect.get(i);
+      if (v != null) {
+        throw new IllegalStateException();
+      }
+    }
+
+    // add even more
+    for (int i = -4203; i < 4200; i += 10) {
+      vect.set(i, new Integer(i));
+    }
+    for (int i = -4204; i < 4200; i += 10) {
+      vect.set(i, new Integer(i));
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SplitInputStream.java b/src/main/gov/nasa/jpf/util/SplitInputStream.java
new file mode 100644 (file)
index 0000000..aca4361
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.concurrent.locks.ReentrantLock;
+
+/* Note: This class fails after 8 petabytes of data has been read.  This should
+ * never be a problem.  For example, assuming a 10 Ghz clock and 1 cycle to read
+ * 4-bytes from L1 cache, it would take 7.3 years to read that much data.
+ */
+public class SplitInputStream {
+
+  static final int INITIAL_BUFFER_SIZE = 1024; // Recommended to be a power of 2.  If not, it will be rounded up to the next power of 2.
+
+  private final ReentrantLock m_sourceLock = new ReentrantLock();
+  private final ReentrantLock m_dataLock = new ReentrantLock();
+  private final InputStream m_source;      // Protected by m_sourceGate lock
+  private final Stream m_stream[];    // Not protected by a lock
+  private long m_write;       // Must hold m_dataGate lock to read.  Must hold m_dataGate and m_sourceGate to write.
+  private int m_available;   // Must hold m_dataGate lock to read.  Must hold m_dataGate and m_sourceGate to write.
+  private int m_openStreams; // Must hold m_dataGate lock to access.
+  private byte m_buffer[];    // Must hold m_dataGate lock to read.  Must hold m_sourceGate lock to write.  The written data doesn't become available until m_write is updated.
+
+  public SplitInputStream(InputStream source, int streamCount) {
+    this(source, streamCount, INITIAL_BUFFER_SIZE);
+  }
+
+  public SplitInputStream(InputStream source, int streamCount, int initialSize) {
+    int i;
+
+    if (source == null) {
+      throw new NullPointerException("source == null");
+    }
+
+    if (streamCount <= 0) {
+      throw new IllegalArgumentException("streamCount <= 0 : " + streamCount);
+    }
+
+    if (initialSize <= 0) {
+      throw new IllegalArgumentException("initialSize <= 0 : " + initialSize);
+    }
+
+    m_source = source;
+    m_openStreams = streamCount;
+    m_stream = new Stream[streamCount];
+
+    for (i = streamCount; --i >= 0;) {
+      m_stream[i] = new Stream(i);
+    }
+
+    initialSize--;                     // Rounds initialSize up to the next power of 2
+    initialSize |= initialSize >> 1;
+    initialSize |= initialSize >> 2;
+    initialSize |= initialSize >> 4;
+    initialSize |= initialSize >> 8;
+    initialSize |= initialSize >> 16;
+    initialSize++;
+
+    m_buffer = new byte[initialSize];
+  }
+
+  public int getStreamCount() {
+    return (m_stream.length);
+  }
+
+  public InputStream getStream(int index) {
+    return (m_stream[index]);
+  }
+
+  private int read(int index) throws IOException {
+    long position;
+    int offset, result;
+
+    m_dataLock.lock();
+
+    try {
+      position = m_stream[index].getPosition();
+
+      if (position == m_write) {
+        if (!fill(index)) {
+          return (-1);
+        }
+
+        position = m_stream[index].getPosition();
+      }
+
+      offset = getBufferOffset(position);
+      result = m_buffer[offset] & 0x0FF;
+
+      m_stream[index].setPosition(position + 1);
+    } finally {
+      m_dataLock.unlock();
+    }
+
+    return (result);
+  }
+
+  private int read(int index, byte buffer[], int offset, int length) throws IOException {
+    long position;
+    int off;
+
+    if (buffer == null) {
+      throw new NullPointerException("buffer == null");
+    }
+
+    if (offset < 0) {
+      throw new IndexOutOfBoundsException("offset < 0 : " + offset);
+    }
+
+    if (length < 0) {
+      throw new IndexOutOfBoundsException("length < 0 : " + length);
+    }
+
+    if (offset + length > buffer.length) {
+      throw new IndexOutOfBoundsException("offset + length > buffer.length : " + offset + " + " + length + " > " + buffer.length);
+    }
+
+    if (length == 0) {
+      return (0);
+    }
+
+    m_dataLock.lock();
+
+    try {
+      position = m_stream[index].getPosition();
+
+      if (position == m_write) {
+        if (!fill(index)) {
+          return (-1);
+        }
+
+        position = m_stream[index].getPosition();
+      }
+
+      off = getBufferOffset(position);
+      length = (int) Math.min(length, m_write - position);
+      length = Math.min(length, m_buffer.length - off);
+
+      m_stream[index].setPosition(position + length);
+      System.arraycopy(m_buffer, off, buffer, offset, length);
+    } finally {
+      m_dataLock.unlock();
+    }
+
+    return (length);
+  }
+
+  private long skip(int index, long n) throws IOException {
+    long position;
+
+    if (n <= 0) {
+      return (0);
+    }
+
+    m_dataLock.lock();
+
+    try {
+      position = m_stream[index].getPosition();
+
+      if (position == m_write) {
+        if (!fill(index)) {
+          return (0);
+        }
+
+        position = m_stream[index].getPosition();
+      }
+
+      n = Math.min(n, m_write - position);
+
+      m_stream[index].setPosition(position + n);
+    } finally {
+      m_dataLock.unlock();
+    }
+
+    return (n);
+  }
+
+  private boolean fill(int index) throws IOException {
+    long minPosition, write;
+    int length, offsetPosition, offsetWrite;
+
+    try {
+      if (!doLock(index)) {
+        return (true);
+      }
+
+      minPosition = getMinPosition();
+
+      if (m_write - minPosition + 1 >= m_buffer.length) {
+        expand();
+      }
+
+      write = m_write;               // Capture the data in local variables so the calculations can take place outside m_dataLock.
+      length = m_buffer.length;
+      m_available = m_source.available();
+
+      m_dataLock.unlock();                 // Don't hold m_dataLock while blocked reading.  That way other Streams with data left to read can do so.
+
+      offsetWrite = getBufferOffset(write);
+      offsetPosition = getBufferOffset(minPosition); // If the minPosition advances while not holding the lock, no big deal.  It simply means less data will be read from m_source.
+      length = getReadLength(offsetPosition, offsetWrite, length);
+
+      do {
+        length = m_source.read(m_buffer, offsetWrite, length);
+      } while (length == 0); // Guarantee that at least 1 byte is read or end of file is reached.
+
+      if (length < 0) {
+        return (false);
+      }
+
+      m_dataLock.lock();
+
+      m_write += length;
+      m_available = m_source.available();
+    } finally {
+      m_sourceLock.unlock();
+
+      if (!m_dataLock.isHeldByCurrentThread()) // Restore the lock state when the method was called.
+      {
+        m_dataLock.lock();
+      }
+    }
+
+    return (true);
+  }
+
+  private boolean doLock(int index) {
+    long position;
+
+    /* m_sourceLock must be acquired before m_dataLock.  Otherwise, there will
+     * be a deadlock.  But, if we can tryLock() m_sourceLock while holding 
+     * m_dataLock, this will save CPU time.
+     */
+    if (m_sourceLock.tryLock()) {
+      return (true);
+    }
+
+    m_dataLock.unlock();
+    m_sourceLock.lock();
+    m_dataLock.lock();
+
+    position = m_stream[index].getPosition();
+
+    return (position == m_write);    // Does the thread still need to read data?
+  }
+
+  private long getMinPosition() {
+    long result, position;
+    int i;
+
+    result = Long.MAX_VALUE;
+
+    for (i = m_stream.length; --i >= 0;) {
+      if (!m_stream[i].isClosed()) {
+        position = m_stream[i].getPosition();
+        result = Math.min(result, position);
+      }
+    }
+
+    return (result);
+  }
+
+  private int getReadLength(int offsetPosition, int offsetWrite, int length) {
+    if (offsetPosition > offsetWrite) {
+      return (offsetPosition - offsetWrite - 1);
+    }
+
+    length -= offsetWrite;
+
+    if (offsetPosition == 0) {
+      length--;
+    }
+
+    return (length);
+  }
+
+  private void expand() {
+    int length;
+    byte buffer[];
+
+    buffer = m_buffer;
+    length = buffer.length;
+    m_buffer = Arrays.copyOf(buffer, 2 * length);
+
+      // Since we are doubling the length of the array, we simply have to duplicate the contents.
+    // This allows us to avoid figuring out which part of m_buffer is actually holding data and dealing with wrapping.
+    System.arraycopy(buffer, 0, m_buffer, length, length);
+  }
+
+  private int available(int index) throws IOException {
+    long result;
+    boolean sourceLock;
+
+    m_dataLock.lock();
+
+    sourceLock = m_sourceLock.tryLock();   // By putting this after locking m_dataLock, the only way tryLock() will fail is if a thread is reading from m_source.
+
+    try {
+      if (sourceLock) {
+        m_available = m_source.available();
+      }
+
+      result = m_available;
+      result += m_write - m_stream[index].getPosition();
+    } finally {
+      m_dataLock.unlock();
+
+      if (sourceLock) {
+        m_sourceLock.unlock();
+      }
+    }
+
+    if (result > Integer.MAX_VALUE) {
+      return (Integer.MAX_VALUE);
+    }
+
+    return ((int) result);
+  }
+
+  private void close() throws IOException {
+    boolean close;
+
+    m_dataLock.lock();
+
+    try {
+      m_openStreams--;
+
+      close = m_openStreams == 0;
+    } finally {
+      m_dataLock.unlock();
+    }
+
+    if (!close) {
+      return;
+    }
+
+    m_sourceLock.lock();
+
+    try {
+      m_source.close();
+    } finally {
+      m_sourceLock.unlock();
+    }
+  }
+
+  private int getBufferOffset(long position) {
+    return ((int) (position & (m_buffer.length - 1)));
+  }
+
+  private class Stream extends InputStream {
+
+    private long m_position;
+    private final int m_index;
+    private boolean m_closed;
+
+    private Stream(int index) {
+      m_index = index;
+    }
+
+    long getPosition() {
+      return (m_position);
+    }
+
+    void setPosition(long position) {
+      m_position = position;
+    }
+
+    synchronized boolean isClosed() {
+      return (m_closed);
+    }
+
+    @Override
+       public int read() throws IOException {
+      if (isClosed()) {
+        return (-1);
+      }
+
+      return (SplitInputStream.this.read(m_index));
+    }
+
+    @Override
+       public int read(byte buffer[], int offset, int length) throws IOException {
+      if (isClosed()) {
+        return (-1);
+      }
+
+      return (SplitInputStream.this.read(m_index, buffer, offset, length));
+    }
+
+    @Override
+       public long skip(long n) throws IOException {
+      if (isClosed()) {
+        return (0);
+      }
+
+      return (SplitInputStream.this.skip(m_index, n));
+    }
+
+    @Override
+       public int available() throws IOException {
+      if (isClosed()) {
+        return (0);
+      }
+
+      return (SplitInputStream.this.available(m_index));
+    }
+
+    @Override
+       public void close() throws IOException {
+      synchronized (this) {
+        if (m_closed) {
+          return;
+        }
+
+        m_closed = true;
+      }
+
+      SplitInputStream.this.close();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/SplitOutputStream.java b/src/main/gov/nasa/jpf/util/SplitOutputStream.java
new file mode 100644 (file)
index 0000000..f3c7907
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Arrays;
+
+public class SplitOutputStream extends OutputStream {
+
+  private final OutputStream m_sinks[];
+
+  public SplitOutputStream(OutputStream... sinks) {
+    int i;
+
+    if (sinks.length <= 0) {
+      throw new IllegalArgumentException("sinks.length <= 0 : " + sinks.length);
+    }
+
+    for (i = sinks.length; --i >= 0;) {
+      if (sinks[i] == null) {
+        throw new NullPointerException("sinks[i] == null : " + i);
+      }
+    }
+
+    m_sinks = Arrays.copyOf(sinks, sinks.length);
+  }
+
+  @Override
+  public void write(int data) throws IOException {
+    int i;
+
+    for (i = m_sinks.length; --i >= 0;) {
+      m_sinks[i].write(data);
+    }
+  }
+
+  @Override
+  public void write(byte buffer[], int offset, int length) throws IOException {
+    int i;
+
+    if (buffer == null) {
+      throw new NullPointerException("buffer == null");
+    }
+
+    if (offset < 0) {
+      throw new IndexOutOfBoundsException("offset < 0 : " + offset);
+    }
+
+    if (length < 0) {
+      throw new IndexOutOfBoundsException("length < 0 : " + length);
+    }
+
+    if (offset + length > buffer.length) {
+      throw new IndexOutOfBoundsException("offset + length > buffer.length : " + offset + " + " + length + " > " + buffer.length);
+    }
+
+    if (length == 0) {
+      return;
+    }
+
+    for (i = m_sinks.length; --i >= 0;) {
+      m_sinks[i].write(buffer, offset, length);
+    }
+  }
+
+  @Override
+  public void flush() throws IOException {
+    int i;
+
+    for (i = m_sinks.length; --i >= 0;) {
+      m_sinks[i].flush();
+    }
+  }
+
+  @Override
+  public void close() throws IOException {
+    int i;
+
+    for (i = m_sinks.length; --i >= 0;) {
+      m_sinks[i].close();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/StateExtensionClient.java b/src/main/gov/nasa/jpf/util/StateExtensionClient.java
new file mode 100644 (file)
index 0000000..7baf038
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPF;
+
+/**
+ * generic interface for state extensions that have to be backtracked/restored
+ */
+public interface StateExtensionClient <T> {
+  
+  // these are the automatic SearchListener callbacks from the corresponding StateExtensionListener
+  T getStateExtension();
+  void restore (T stateExtension);
+
+  // this needs to be called somewhere from the app
+  void registerListener (JPF jpf);
+}
diff --git a/src/main/gov/nasa/jpf/util/StateExtensionListener.java b/src/main/gov/nasa/jpf/util/StateExtensionListener.java
new file mode 100644 (file)
index 0000000..fdae4a1
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.SystemState;
+
+/**
+ * generic listener that keeps track of state extensions, using
+ * state ids as index values into a dynamic array of T objects
+ * 
+ * the purpose of this utility class is to make state extensions
+ * backtrackable, so that clients don't have to care about this
+ */
+public class StateExtensionListener <T> extends ListenerAdapter {
+  StateExtensionClient<T> client;
+  DynamicObjectArray<T> states;
+
+  public StateExtensionListener (StateExtensionClient<T> cli) {
+    client = cli;
+    states = new DynamicObjectArray<T>();
+
+    // set initial state
+    T se = client.getStateExtension();
+    states.set(0, se);
+  }
+
+  @Override
+  public void stateAdvanced (Search search) {
+    int idx = search.getStateId()+1;
+    T se = client.getStateExtension();
+    states.set(idx, se);
+  }
+
+  @Override
+  public void stateBacktracked (Search search) {
+    int idx = search.getStateId()+1;
+
+    T se = states.get(idx);
+    client.restore(se);
+  }
+
+  @Override
+  public void stateRestored (Search search) {
+    int idx = search.getStateId()+1;
+    T se = states.get(idx);
+    client.restore(se);
+
+    SystemState ss = search.getVM().getSystemState();
+    ChoiceGenerator<?> cgNext = ss.getNextChoiceGenerator();
+    cgNext.reset();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/StringExpander.java b/src/main/gov/nasa/jpf/util/StringExpander.java
new file mode 100644 (file)
index 0000000..75a3fcc
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * utility class to expand regular expression like strings. We support
+ * alternatives "<..|..>" and character categories "[.. X-Z ..]". Alternatives
+ * can be nested, char categories can include '-' ranges
+ *
+ *  e.g. "a<B|X[0-1]Y>z" => aBz, aX0Yz, aX1Yz
+ *
+ * <2do> this is awfully connected to ESParser - we should really make it more generic
+ * (this is the reason why it was moved from the general gov.nasa.jpf.util to
+ * this package)
+ */
+public class StringExpander {
+
+  public static final char META_CHAR = '`';  // starts a symbol
+
+  public static final char ALT_START_CHAR = '<';
+  public static final char ALT_END_CHAR   = '>';
+  public static final char ALT_CHAR = '|';
+
+  public static final char CAT_START_CHAR = '[';
+  public static final char CAT_END_CHAR = ']';
+  public static final char CAT_CHAR = '-';
+
+  static class Exception extends RuntimeException {
+    Exception(String details){
+      super(details);
+    }
+  }
+
+  static class Token {
+    String value;
+
+    Token (String value){
+      this.value = value;
+    }
+    int length() {
+      return value.length();
+    }
+    boolean isSymbol() {
+      return false;
+    }
+    @Override
+       public String toString(){
+      return value;
+    }
+
+  }
+  static class Symbol extends Token {
+    Symbol (String s){
+      super(s);
+    }
+    @Override
+       boolean isSymbol(){
+      return true;
+    }
+  }
+
+  // our symbol tokens
+  static final Symbol CAT_START = new Symbol("CAT_START");
+  static final Symbol CAT_END = new Symbol("CAT_END");
+
+  static final Symbol ALT_START = new Symbol("ALT_START");
+  static final Symbol ALT_END = new Symbol("ALT_END");
+  static final Symbol ALT = new Symbol("ALT");
+  static final Symbol EOS = new Symbol("END");
+
+  final String src;
+  final int len;
+
+  Token token;
+  int pos;
+
+/**
+    // a quoted symbol char version - doesn't look nice, but is more general
+  void nextToken () {
+
+    int i = pos;
+    int len = this.len;
+
+    if (i>=len){
+      token = EOS;
+
+    } else {
+      if (src.charAt(i) == META_CHAR){  // symbol
+        char c = src.charAt(++i);
+        switch (c){
+          case CAT_START_CHAR:       token = CAT_START; break;
+          case CAT_END_CHAR:         token = CAT_END; break;
+
+          case ALT_START_CHAR:       token = ALT_START; break;
+          case ALT_CHAR:             token = ALT; break;
+          case ALT_END_CHAR:         token = ALT_END; break;
+          default:
+            error("illegal symbol: " + c);
+        }
+        pos += 2;
+
+      } else { // string literal
+        int j = i + 1;
+        for (; j < len && src.charAt(j) != META_CHAR; j++);
+        pos = j;
+        token = new Token(src.substring(i, j));
+      }
+    }
+  }
+**/
+
+  private boolean isMetaChar(char c){
+    return ((c == CAT_START_CHAR) || (c == CAT_END_CHAR) ||
+            (c == ALT_START_CHAR) || (c == ALT_END_CHAR) || (c == ALT_CHAR));
+
+  }
+
+  void nextToken() {
+    int i = pos;
+    int len = this.len;
+
+    if (i>=len){
+      token = EOS;
+
+    } else {
+      char c = src.charAt(i);
+      switch (c){
+        case CAT_START_CHAR:       token = CAT_START;   pos++; break;
+        case CAT_END_CHAR:         token = CAT_END;     pos++; break;
+
+        case ALT_START_CHAR:       token = ALT_START;   pos++; break;
+        case ALT_CHAR:             token = ALT;         pos++; break;
+        case ALT_END_CHAR:         token = ALT_END;     pos++; break;
+
+        default:
+          int j = i + 1;
+          for (; j < len && !isMetaChar(src.charAt(j)); j++);
+          pos = j;
+          token = new Token(src.substring(i, j));
+      }
+    }
+  }
+
+  boolean match (Symbol sym){
+    if (token == sym){
+      nextToken();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  public StringExpander (String src){
+    this.src = src;
+    this.len = src.length();
+    this.pos = 0;
+  }
+
+  List<String> addSeq (List<String> list, List<String> seq){
+    List<String> result = new LinkedList<String>();
+
+    if (list != null && list.size() > 0){
+      result.addAll(list);
+    }
+    result.addAll(seq);
+
+    return result;
+  }
+
+  List<String> addLiteral (List<String> list, String s){
+    List<String> result = new LinkedList<String>();
+
+    if (list == null || list.size() == 0){
+      result.add(s);
+
+    } else {
+      for (String e : list) {
+        result.add(e + s);
+      }
+    }
+
+    return result;
+  }
+
+  List<String> addAlt (List<String> list, List<String>alt){
+    List<String> result = new LinkedList<String>();
+
+    if (list == null || list.size() == 0){
+      result.addAll(alt);
+
+    } else {
+      for (String e : list) {
+        for (String p : alt) {
+          result.add(e + p);
+        }
+      }
+    }
+
+    return result;
+  }
+
+  List<String> addCat (List<String> list, char[] cat){
+    List<String> result = new LinkedList<String>();
+
+    if (list == null || list.size() == 0){
+      for (char c : cat){
+        result.add(Character.toString(c));
+      }
+
+    } else {
+      for (String e : list) {
+        for (char c : cat){
+          result.add(e + c);
+        }
+      }
+    }
+
+    return result;
+  }
+
+  void error (String msg){
+    throw new Exception(msg);
+  }
+
+  protected char[] createCategory(String cat){
+    char[] s = cat.toCharArray();
+    int l1 = s.length-1;
+    char[] d = s;
+
+
+    for (int i=0, j=0; i<s.length; i++){
+      char c = s[i];
+      if ((c == CAT_CHAR) && (i>0) && (i<l1)){
+        char c0 = s[i-1];
+        char c1 = s[i+1];
+        int  n = c1 - c0;
+
+        int len = j + n + (s.length-i-2);
+        char[] dNew = new char[len];
+        System.arraycopy(d, 0, dNew, 0, j);
+        d = dNew;
+
+        for (int k=c0+1; k<=c1; k++){
+          d[j++] = (char)k;
+        }
+        i++;
+
+      } else {
+        d[j++] = c;
+      }
+    }
+
+    return d;
+  }
+
+  // seq       :=  {LIT | cat | alt}*
+  // cat       :=  `[ LIT `]
+  // alt       :=  `< spec {`| spec}* `>
+
+  public List<String> expand() {
+    return seq();
+  }
+
+  List<String> seq() {
+    List<String> result = null;
+
+    for (nextToken(); token != EOS; ){
+      if (!token.isSymbol()){
+        result = addLiteral( result,token.value);
+        nextToken();
+
+      } else if (token == ALT_START){
+        result = addAlt( result, alt());
+
+      } else if (token == CAT_START){
+        result = addCat( result, cat());
+
+      } else {
+        break;
+      }
+    }
+
+    return result;
+  }
+
+
+  List<String> alt() {
+    List<String> result = null;
+
+    assert token == ALT_START;
+
+    do {
+      result = addSeq(result, seq());
+    } while (token == ALT);
+
+    if (!match(ALT_END)){
+      error("unterminated alternative");
+    }
+    return result;
+  }
+
+  char[] cat() {
+    char[] set = null;
+
+    assert token == CAT_START;
+
+    nextToken();
+    if (!token.isSymbol()){
+      set = createCategory(token.value);
+      nextToken();
+    }
+
+    if (!match(CAT_END)){
+      error("unterminated category");
+    }
+    return set;
+  }
+
+
+  public static void main (String[] args) {
+    //String a = "<B[0-3]C>";
+    String a = args[0];
+    System.out.println(a);
+    
+    StringExpander ex = new StringExpander(a);
+
+/**
+    for (ex.nextToken(); ex.token != EOS; ex.nextToken()){
+      System.out.println(ex.token);
+    }
+**/
+/**/
+    for (String s : ex.expand()) {
+      System.out.println(s);
+    }
+/**/
+/**
+    System.out.println(new String(ex.createCategory(args[0])));
+**/
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/StringMatcher.java b/src/main/gov/nasa/jpf/util/StringMatcher.java
new file mode 100644 (file)
index 0000000..8c1a1ab
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * simple wrapper around Patter/Matcher pairs, with patterns using '*' wildcards
+ * same as StringSetMatcher but for single patterns
+ */
+public class StringMatcher {
+
+  boolean isAnyPattern; // do we have a universal '*' pattern?
+
+  Pattern pattern;
+  Matcher matcher;
+
+  public static boolean hasWildcard (String patternSpec){
+    return (patternSpec.indexOf('*') >= 0);
+  }
+
+  public StringMatcher (String patternSpec){
+    if (patternSpec.equals("*")) {
+      isAnyPattern = true;
+      // no need to compile this
+
+    } else {
+      Pattern p = createPattern(patternSpec);
+      pattern = p;
+      matcher = p.matcher(""); // gets reset upon use
+    }
+  }
+
+  protected Pattern createPattern (String s){
+    Pattern p;
+
+    StringBuilder sb = new StringBuilder();
+
+    int len = s.length();
+    for (int j=0; j<len; j++){
+      char c = s.charAt(j);
+      switch (c){
+      case '.' : sb.append("\\."); break;
+      case '$' : sb.append("\\$"); break;
+      case '[' : sb.append("\\["); break;
+      case ']' : sb.append("\\]"); break;
+      case '*' : sb.append(".*"); break;
+      case '(' : sb.append("\\("); break;
+      case ')' : sb.append("\\)"); break; 
+      // <2do> and probably more..
+      default:   sb.append(c);
+      }
+    }
+
+    p = Pattern.compile(sb.toString());
+    return p;
+  }
+
+  public boolean matches (String s){
+    if (isAnyPattern){
+      return true;
+    } else {
+      matcher.reset(s);
+      return matcher.matches();
+    }
+  }
+
+  @Override
+  public String toString() {
+    if (isAnyPattern){
+      return ".*";
+    } else {
+      return pattern.toString();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/StringSetMatcher.java b/src/main/gov/nasa/jpf/util/StringSetMatcher.java
new file mode 100644 (file)
index 0000000..844ae36
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * simple utility that can be used to check for string matches in
+ * sets with '*' wildcards, e.g. to check for class name lists such as
+ *
+ *   vm.halt_on_throw=java.lang.reflect.*:my.own.Exception
+ *
+ * Only meta chars in patterns are '*' and '!', i.e. '.' is a regular char to match
+ * A '!' prefix inverts the match
+ */
+public class StringSetMatcher {
+
+  public static final char WILDCARD = '*';
+  public static final char INVERTED = '!';
+  
+  boolean hasAnyPattern; // do we have a universal '*' pattern?
+
+  Pattern[] pattern;
+  Matcher[] matcher;
+  boolean[] inverted;
+
+  /**
+   * convenience method for matcher pairs containing of explicit excludes and
+   * includes
+   */
+  public static boolean isMatch (String s, StringSetMatcher includes, StringSetMatcher excludes){
+    if (excludes != null) {
+      if (excludes.matchesAny(s)){
+        return false;
+      }
+    }
+
+    if (includes != null) {
+      if (!includes.matchesAny(s)){
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public static StringSetMatcher getNonEmpty(String[] set){
+    if (set != null && set.length > 0){
+      return new StringSetMatcher(set);
+    } else {
+      return null;
+    }
+  }
+
+  public StringSetMatcher (String... set){
+    int n = set.length;
+    pattern = new Pattern[n];
+    matcher = new Matcher[n];
+    inverted = new boolean[n];
+
+    for (int i=0; i<n; i++){
+      String s = set[i];
+
+      if (s.equals("*")) {
+        hasAnyPattern = true;
+        // no need to compile this
+
+      } else {
+        Pattern p =  createPattern(s);
+        pattern[i] = p;
+        matcher[i] = p.matcher(""); // gets reset upon use
+        inverted[i] = isInverted(s);
+      }
+    }
+  }
+
+  @Override
+  public String toString() {
+    int n=0;
+    StringBuilder sb = new StringBuilder(64);
+    sb.append("StringSetMatcher {patterns=");
+
+    if (hasAnyPattern) {
+      sb.append(".*");
+      n++;
+    }
+
+    for (int i=0; i<pattern.length; i++) {
+      if (pattern[i] != null) {
+        if (n++>0) {
+          sb.append(',');
+        }
+        if (inverted[i]){
+          sb.append(INVERTED);
+        }
+        sb.append(pattern[i]);
+      }
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+
+  public void addPattern (String s){
+
+    if (s.equals("*")) { // no need to compile
+      // note that this doesn't include the - pointless - "!*", which would match nothing
+      hasAnyPattern = true;
+
+    } else {
+      int n = pattern.length;
+
+      Pattern[] pNew = new Pattern[n+1];
+      System.arraycopy(pattern, 0, pNew, 0, n);
+      pNew[n] = createPattern(s);
+
+      Matcher[] mNew = new Matcher[pNew.length];
+      System.arraycopy(matcher, 0, mNew, 0, n);
+      mNew[n] = pNew[n].matcher("");
+
+      boolean[] iNew = new boolean[pNew.length];
+      System.arraycopy( inverted, 0, iNew, 0, n);
+      iNew[n] = isInverted(s);
+      
+      pattern = pNew;
+      matcher = mNew;
+      inverted = iNew;
+    }
+  }
+
+  public static boolean isInverted (String s){
+    return (!s.isEmpty() && s.charAt(0) == INVERTED);
+  }
+  
+  protected Pattern createPattern (String s){
+    Pattern p;
+    int j = 0;
+    int len = s.length();
+
+    // inversion is better done outside of regex
+    if ((len > 0) && s.charAt(0) == INVERTED){
+      j++; // skip INVERTED char
+    }
+    
+    StringBuilder sb = new StringBuilder();
+        
+    for (; j<len; j++){
+      char c = s.charAt(j);
+      switch (c){
+      case '.' : sb.append("\\."); break;
+      case '$' : sb.append("\\$"); break;
+      case '[' : sb.append("\\["); break;
+      case ']' : sb.append("\\]"); break;
+      case '*' : sb.append(".*"); break;
+      case '(' : sb.append("\\("); break;
+      case ')' : sb.append("\\)"); break;
+      // <2do> and probably more..
+      default:   sb.append(c);
+      }
+    }
+
+    p = Pattern.compile(sb.toString());
+    return p;
+  }
+
+  /**
+   * does 's' match at least one of our patterns
+   */
+  public boolean matchesAny (String s){
+    if (s != null) {
+      if (hasAnyPattern) {
+        return true; // no need to check
+      }
+
+      for (int i=0; i<matcher.length; i++){
+        Matcher m = matcher[i];
+        m.reset(s);
+
+        if (m.matches() != inverted[i]){
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * does 's' match ALL of our patterns
+   */
+  public boolean matchesAll (String s){
+    if (s != null) {
+      if (hasAnyPattern && pattern.length == 1) { // there might be other patterns
+        return true; // no need to check
+      }
+
+      for (int i=0; i<pattern.length; i++){
+        Pattern p = pattern[i];
+        if (p != null){
+          Matcher m = matcher[i];
+          m.reset(s);
+
+          if (m.matches() == inverted[i]){
+            return false;
+          }
+        } else {
+          if (inverted[i]){
+            return false;
+          }
+        }
+      }
+
+      return true;
+
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * do all elements of 'set' match at least one of our patterns?
+   */
+  public boolean allMatch (String[] set){
+    if (hasAnyPattern) {
+      return true;
+    }
+
+    for (int i=0; i<set.length; i++){
+      if (!matchesAny(set[i])){
+        return false;
+      }
+    }
+    return true;
+  }
+
+
+  public static void main (String[] args){
+    String[] p = args[0].split(":");
+    String[] s = args[1].split(":");
+
+    StringSetMatcher sm = new StringSetMatcher(p);
+    if (sm.matchesAny(s[0])){
+      System.out.println("Bingo, \"" + s[0] + "\" matches " + sm);
+    } else {
+      System.out.println("nope, \"" + s[0] + "\" doesn't match " + sm);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/StructuredPrinter.java b/src/main/gov/nasa/jpf/util/StructuredPrinter.java
new file mode 100644 (file)
index 0000000..1969044
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.io.PrintWriter;
+
+/**
+ * a common base for printers that need to keep track of indentation levels
+ */
+public abstract class StructuredPrinter {
+
+  protected PrintWriter pw;
+
+  protected int indentLevel = 0;
+  protected String indent = "";
+  
+  protected StructuredPrinter(){
+    pw = new PrintWriter(System.out, true);
+  }
+  
+  protected StructuredPrinter (PrintWriter pw){
+    this.pw = pw;
+  }
+  
+  protected void incIndent(){
+    indentLevel++;
+    indent = indent();
+  }
+
+  protected void decIndent(){
+    if (indentLevel > 0){
+      indentLevel--;
+      indent = indent();
+    }
+  }
+
+  protected String indent(){
+    switch (indentLevel){
+      case 0: return "";
+      case 1: return "    ";
+      case 2: return "        ";
+      case 3: return "            ";
+      case 4: return "                ";
+      default:
+        StringBuilder sb = new StringBuilder();
+        for (int i=0; i<indentLevel; i++){
+          sb.append("    ");
+        }
+        return sb.toString();
+    }
+  }
+  
+  protected void printSectionHeader(String id){
+    pw.println();
+    pw.print("--------------------------------------------------- ");
+    pw.println(id);
+  }
+
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/TotalPermutationGenerator.java b/src/main/gov/nasa/jpf/util/TotalPermutationGenerator.java
new file mode 100644 (file)
index 0000000..685f750
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * a pull mode generator for permutations that executes in constant space,
+ * using Ives' algorithm (F. M. Ives: Permutation enumeration: four new 
+ * permutation algorithms, Comm. ACM, 19, 2, Feb 1976, pg. 68-72)
+ * 
+ * NOTE - this is mostly here for completeness, since use of full permutations
+ * in most cases is prohibitive due to N!
+ */
+public class TotalPermutationGenerator extends PermutationGenerator {
+  
+  protected int[] inverse; // inverse permutations array
+    
+  public TotalPermutationGenerator (int nElements){
+    super( nElements);
+    
+    initInverse();
+  }
+  
+  void initInverse (){
+    inverse = new int[nElements];
+    for (int i=0; i<nElements; i++){
+      inverse[i] = i;
+    }    
+  }
+  
+  @Override
+  public void reset(){
+    initPermutations();
+    initInverse();
+  }
+  
+  
+  public static long computeNumberOfPermutations(int nElements){
+    long n = 1;
+    for (int i=1; i<=nElements; i++){
+      n *= i;
+    }
+    return n;    
+  }
+  
+  @Override
+  protected long computeNumberOfPermutations(){
+    return computeNumberOfPermutations(nElements);
+  }
+    
+  @Override
+  public int[] next (){
+    if (nGenerated == 0){
+      nGenerated =1;
+      return permutation;
+      
+    } else {
+      for (int lower=0, upper=nElements-1; upper > lower; lower++, upper--){
+        int i = inverse[lower];
+        int j = (i == upper) ? lower : i+1;
+        int pj = permutation[j];
+
+        permutation[i] = pj;
+        permutation[j] = lower;
+
+        inverse[lower] = j;
+        inverse[pj] = i;
+
+        if ((permutation[lower] != lower) || (permutation[upper] != upper)){
+          nGenerated++;
+          return permutation;
+        }
+      }
+      throw new NoSuchElementException();
+    }
+  }  
+}
diff --git a/src/main/gov/nasa/jpf/util/Trace.java b/src/main/gov/nasa/jpf/util/Trace.java
new file mode 100644 (file)
index 0000000..3572d32
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * a generic, listener- created trace over property specific operations
+ * 
+ * we could register this as a listener itself, but since it usually is used from
+ * a listener, we might as well just delegate from there
+ */
+public class Trace<T> extends ListenerAdapter implements Iterable<T> {
+
+  TraceElement<T> lastElement;
+  TraceElement<T> lastTransition;
+
+  // for HeuristicSearches. Ok, that's braindead but at least no need for cloning
+  HashMap<Integer,TraceElement<T>> storedTransition;
+
+
+  // iterator that traverses the trace LIFO, i.e. starting from the last T
+  class TraceIterator implements Iterator<T> {
+
+    TraceElement<T> cur;
+    
+    TraceIterator () {
+      cur = lastElement;
+    }
+    
+    @Override
+       public boolean hasNext () {
+      return (cur != null);
+    }
+
+    @Override
+       public T next () {
+      if (cur != null){
+        T op = cur.op;
+        cur = cur.prevElement;
+        return op;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+       public void remove () {
+      throw new UnsupportedOperationException("TraceElement removal not supported");
+    } 
+  }
+  
+  @Override
+  public Iterator<T> iterator() {
+    return new TraceIterator();
+  }
+  
+  public void addOp (T o){
+    TraceElement<T> op = new TraceElement<T>(o);
+    
+    if (lastElement == null){
+      lastElement = op;
+    } else {
+      assert lastElement.stateId == 0;
+      
+      op.prevElement = lastElement;
+      lastElement = op;
+    }
+  }
+
+  public void removeLastOp() {
+    if (lastElement != null){
+      lastElement = lastElement.prevElement;
+    }
+  }
+
+  public T getLastOp() {
+    if (lastElement != null) {
+      return lastElement.getOp();
+    }
+    
+    return null;
+  }
+
+  public int size() {
+    int n=0;
+    for (TraceElement<T> te = lastElement; te != null; te = te.prevElement) {
+      n++;
+    }
+    
+    return n;
+  }
+  
+  public List<T> getOps () {
+    // this is a rather braindead way around the limitation that we can't explicitly
+    // create an T[] array object
+    
+    ArrayList<T> list = new ArrayList<T>();
+    
+    for (TraceElement<T> te = lastElement; te != null; te = te.prevElement) {
+      list.add(te.getOp());
+    }
+    
+    // reverse
+    for (int i=0, j=list.size()-1; i<j; i++, j--) {
+      T tmp = list.get(j);
+      list.set(j, list.get(i));
+      list.set(i, tmp);
+    }
+    
+    return list;
+  }
+
+  @Override
+  public void stateAdvanced (Search search) {
+    if (search.isNewState() && (lastElement != null)) {
+      int stateId = search.getStateId();
+      
+      for (TraceElement<T> op=lastElement; op != null; op=op.prevElement) {
+        assert op.stateId == 0;
+        
+        op.stateId = stateId;
+      }
+      
+      lastElement.prevTransition = lastTransition;
+      lastTransition = lastElement;
+    }
+    
+    lastElement = null;
+  }
+
+  @Override
+  public void stateBacktracked (Search search){
+    int stateId = search.getStateId();
+    while ((lastTransition != null) && (lastTransition.stateId > stateId)){
+      lastTransition = lastTransition.prevTransition;
+    }
+    lastElement = null;
+  }
+
+  @Override
+  public void stateStored (Search search) {
+    if (storedTransition == null){
+      storedTransition = new HashMap<Integer,TraceElement<T>>();
+    }
+    
+    // always called after stateAdvanced
+    storedTransition.put(search.getStateId(), lastTransition);
+  }
+
+  @Override
+  public void stateRestored (Search search) {
+    int stateId = search.getStateId();
+    TraceElement<T> op = storedTransition.get(stateId);
+    if (op != null) {
+      lastTransition = op;
+      storedTransition.remove(stateId);  // not strictly required, but we don't come back
+    }
+  }
+
+  @Override
+  public Trace clone() {
+    TraceElement<T> e0 = null, eLast = null;
+    
+    for (TraceElement<T> e = lastElement; e != null; e = e.prevElement){
+      TraceElement<T> ec = e.clone();
+
+      if (eLast != null){
+        eLast.prevElement = ec;
+        eLast = ec;
+      } else {
+        e0 = eLast = ec;
+      }
+    }
+    
+    Trace<T> t = new Trace<T>();
+    t.lastElement = e0;
+    
+    return t;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/TraceElement.java b/src/main/gov/nasa/jpf/util/TraceElement.java
new file mode 100644 (file)
index 0000000..c8442a5
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * encapsulates a listener-managed trace operation
+ */
+public class TraceElement<T> {
+  T op;
+  
+  int stateId;
+  TraceElement<T> prevElement;
+  TraceElement<T> prevTransition;
+  
+  public TraceElement (T op){
+    this.op = op;
+  }
+  
+  public TraceElement<T> getPrevElement() {
+    return prevElement;
+  }
+  
+  public T getOp() {
+    return op;
+  }
+  
+  @Override
+  public TraceElement<T> clone() {
+    TraceElement<T> e = new TraceElement<T>(op);
+    e.stateId = stateId;
+    
+    // we don't clone the linkage
+    e.prevElement = null;
+    e.prevTransition = null;
+    
+    return e;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/Transformer.java b/src/main/gov/nasa/jpf/util/Transformer.java
new file mode 100644 (file)
index 0000000..c5cc179
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * transforms between two types
+ *
+ * a utility interface that esp. can be used for generic containers
+ */
+public interface Transformer<T1,T2> {
+  T2 transform (T1 e);
+}
diff --git a/src/main/gov/nasa/jpf/util/TwoTypeComparator.java b/src/main/gov/nasa/jpf/util/TwoTypeComparator.java
new file mode 100644 (file)
index 0000000..ddf085a
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * like a normal comparator, but comparing heterogenous objects
+ */
+public interface TwoTypeComparator<T1,T2> {
+  int compare (T1 o1, T2 o2);
+}
diff --git a/src/main/gov/nasa/jpf/util/TypeRef.java b/src/main/gov/nasa/jpf/util/TypeRef.java
new file mode 100644 (file)
index 0000000..978390b
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+
+/**
+ * this is a utility construct for type references that should
+ * not cause class loading when instantiated, but cannot use a
+ * classname String because of argument type ambiguity (Strings are just
+ * used everywhere).
+ * 
+ * TypeRefs can be used to specify both native and JPF executed (SUT) classes,
+ * it is up to the caller to use the proper access methods
+ *
+ * NOTE - loading and instantiation of TypeRefs is not allowed to cause loading of
+ * any JPF classes that are not in jpf-classes.jar
+ */
+public class TypeRef {
+  String clsName;
+
+  public TypeRef (String clsName){
+    this.clsName = clsName;
+  }
+  
+  public Class<?> getNativeClass() throws ClassNotFoundException {
+    return Class.forName(clsName);
+  }
+
+  /**
+   * return the host VM class for this ref.
+   * This will cause native on-demand class loading
+   */
+  public <T> Class<? extends T> asNativeSubclass(Class<T> superClazz) throws ClassNotFoundException, ClassCastException {
+    Class<?> clazz = Class.forName(clsName);
+    return clazz.asSubclass(superClazz);
+  }
+
+  /**
+   * obtain the ClassInfo (JPF class) for this ref.
+   * This will cause on-demand class loading by JPF
+   */
+  public ClassInfo getClassInfo (){
+    return ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+  }
+  
+  @Override
+  public String toString(){
+    return "TypeRef(" + clsName + ")";
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/TypeSpec.java b/src/main/gov/nasa/jpf/util/TypeSpec.java
new file mode 100644 (file)
index 0000000..2d3b398
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+
+/**
+ * wildcard supporting type specification to be used for JPF configuration.
+ * This supports supertype spec ('+') and inversion ('-')
+ * Examples:
+ *   "x.y.Foo" : class x.y.Foo
+ *   "+x.y.Foo" : everything that is an instance of x.y.Foo
+ *   "x.y.*" : every class that starts with "x.y."
+ *   "!x.y.*" : everything that does not start with "x.y."
+ */
+public class TypeSpec extends FeatureSpec {
+  
+  
+  public static TypeSpec createTypeSpec (String s){
+    ParseData d = new ParseData();
+
+    s = s.trim();
+    String src = s; // keep the original spec around
+
+    s = parseInversion(s,d);
+    parseType(s,d);
+    
+    try {
+      return new TypeSpec(src, d.typeSpec, d.matchInverted);
+    } catch (IllegalArgumentException iax){
+      return null;
+    }
+  }
+  
+  protected TypeSpec (String rawSpec, String cls, boolean inverted){
+    super(rawSpec,cls,null,inverted);
+  }
+  
+  @Override
+  public boolean matches (Object o){
+    if (o instanceof ClassInfo){
+      return matches( (ClassInfo) o);
+    } else if (o instanceof Class){
+      return matches( (Class)o);
+    } else {
+      return false;
+    }
+  }
+  
+  public boolean matches (Class<?> cls){
+    return isMatchingType(cls);
+  }
+  
+  public boolean matches (ClassInfo ci){
+    return isMatchingType(ci);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/TypeSpecMatcher.java b/src/main/gov/nasa/jpf/util/TypeSpecMatcher.java
new file mode 100644 (file)
index 0000000..6e0d03e
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.vm.ClassInfo;
+
+/**
+ * a matcher for collections of TypeSpecs
+ */
+public class TypeSpecMatcher {
+
+  protected TypeSpec[] mypeSpecs;
+  
+  public static TypeSpecMatcher create (String[] specs){
+    if (specs != null && specs.length > 0){
+      return new TypeSpecMatcher(specs);
+    } else {
+      return null;
+    }
+  }
+  
+  public TypeSpecMatcher(String[] specs){
+    int len = specs.length;
+    mypeSpecs = new TypeSpec[len];
+    for (int i=0; i<len; i++){
+      mypeSpecs[i] = TypeSpec.createTypeSpec(specs[i]);
+    }
+  }
+  
+  public boolean matches (Class<?> cls){
+    for (int i=0; i<mypeSpecs.length; i++){
+      if (mypeSpecs[i].matches(cls)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  
+  public boolean matches (ClassInfo ci){
+    for (int i=0; i<mypeSpecs.length; i++){
+      if (mypeSpecs[i].matches(ci)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/UniqueRandomPermGenerator.java b/src/main/gov/nasa/jpf/util/UniqueRandomPermGenerator.java
new file mode 100644 (file)
index 0000000..7fc555d
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+
+/**
+ * a RandomPermutationGenerator that keeps track of previously produced values
+ * to avoid duplicates.
+ * Note this only makes sense for relatively small sample sizes, but then again
+ * that is what RandomPermutationGenerators are used for (to avoid N!)
+ */
+public class UniqueRandomPermGenerator extends RandomPermutationGenerator {
+  
+  protected SortedArrayIntSet visited;
+  
+  public UniqueRandomPermGenerator (int nElements, int nPermutations, int seed){
+    super(nElements, nPermutations, seed);
+    
+    visited = new SortedArrayIntSet();
+    this.nPermutations = Math.min( TotalPermutationGenerator.computeNumberOfPermutations(nElements), nPermutations);
+  }
+  
+  public void reset(){
+    super.reset();
+    visited = new SortedArrayIntSet();
+  }
+    
+  public int[] next(){    
+    while (nGenerated < nPermutations){
+      int[] p = super.next();
+      int h = OATHash.hash(p);
+      
+      if (visited.add(h)){
+        return p;
+      } else {
+        nGenerated--; // that one didn't count, we already have seen it
+      }
+    }
+    throw new NoSuchElementException();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/UnsortedArrayIntSet.java b/src/main/gov/nasa/jpf/util/UnsortedArrayIntSet.java
new file mode 100644 (file)
index 0000000..6bda52d
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+/**
+ * a simplistic IntSet implementation that uses an unsorted array to keep elements.
+ * Obviously this is O(N) and therefore not a good choice if the list grows,
+ * but if we know there are only a few elements then it isn't worth to
+ * do any sorting or fancy lookup - the JIT would beat algorithm.
+ * 
+ * If the set is empty there is no memory allocated for the elements
+ */
+public class UnsortedArrayIntSet extends ArrayIntSet {
+
+  static final int DEFAULT_CAPACITY = 4;
+  static final int GROWTH = 8;
+  
+  public UnsortedArrayIntSet (){
+    // nothing
+  }
+  
+  public UnsortedArrayIntSet (int initialCapacity){
+    super(initialCapacity);
+  }
+
+  
+  
+  @Override
+  public boolean add (int v) {
+    int len = size;
+    if (len == 0){
+      elements = new int[DEFAULT_CAPACITY];
+      
+    } else {
+      int[] a = elements;
+      int i=0;
+      for (; i<len; i++){
+        if (a[i] == v){
+          return false; // was already there
+        }
+      }
+      
+      if (i == a.length){
+        int[] newElements = new int[a.length + GROWTH];
+        System.arraycopy(a, 0, newElements, 0, size);
+        elements = newElements;
+      }    
+    }
+    
+    elements[size++] = v;
+    return true;
+  }
+
+  @Override
+  public boolean remove(int v) {
+    int len = size;
+    if (len > 0){
+      int[] a = elements;
+      for (int i=0; i<len; i++){
+        if (a[i] == v){
+          if (len == 1){
+            elements = null;
+          } else {
+            i++;
+            if (i < len) {
+              System.arraycopy(a, i, a, i-1, len-i);
+            }
+          }
+          
+          size--;
+          return true;
+        }
+      }
+    }
+    
+    return false; // wasn't there
+  }
+
+  @Override
+  public boolean contains(int v) {
+    int len = size;
+    if (len > 0){
+      int[] a = elements;
+      for (int i=0; i<len; i++){
+        if (a[i] == v){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/VarSpec.java b/src/main/gov/nasa/jpf/util/VarSpec.java
new file mode 100644 (file)
index 0000000..c2e8a43
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+/**
+ * utility class to specify local variables in JPF options
+ * example:
+ *
+ *  x.y.MyClass.foo(int,double):x
+ * 
+ * Note: this is not derived from FeatureSpec, it only used a MethodSpec for delegation
+ *
+ * <2do> we don't deal with scopes or types yet
+ */
+public class VarSpec  {
+
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.util");
+
+  String varName;
+  MethodSpec mthSpec;
+
+  public static VarSpec createVarSpec(String spec) {
+    int idx = spec.indexOf(':');
+
+    if (idx > 0) {
+      String ms = spec.substring(0, idx).trim();
+      String vs = spec.substring(idx+1).trim();
+
+      MethodSpec mspec = MethodSpec.createMethodSpec(ms);
+      if (mspec != null){
+        return new VarSpec(vs, mspec);
+      }
+    }
+
+    logger.warning("illegal variable spec ", spec);
+    return null;
+  }
+
+  public VarSpec (String varName, MethodSpec mthSpec){
+    this.varName = varName;
+    this.mthSpec = mthSpec;
+  }
+
+  public LocalVarInfo getMatchingLocalVarInfo (MethodInfo mi, int pc, int slotIdx){
+
+    if (mthSpec.matches(mi)){
+      LocalVarInfo lvar = mi.getLocalVar(slotIdx, pc);
+      if (lvar != null && lvar.getName().equals(varName)){
+        return lvar;
+      }
+    }
+
+    return null;
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/util/WeakPool.java b/src/main/gov/nasa/jpf/util/WeakPool.java
new file mode 100644 (file)
index 0000000..9676390
--- /dev/null
@@ -0,0 +1,214 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.lang.ref.WeakReference;
+
+/**
+ * This is a simplified hash pool that does not support removal or
+ * numbering of elements.
+ */
+public class WeakPool<E> {
+  private static final boolean DEBUG = false; 
+  
+  static final double MAX_LOAD_WIPE = 0.6;
+  static final double MAX_LOAD_REHASH = 0.4;
+  static final int DEFAULT_POW = 10;
+
+  WeakReference<E>[] table;
+  
+  int count;
+  int pow;
+  int mask;
+  int nextWipe;
+  int nextRehash;
+  
+  /**
+   * Creates a SimplePool that holds about 716 elements before first
+   * rehash.
+   */
+  public WeakPool() {
+    this(DEFAULT_POW);
+  }
+  
+  /**
+   * Creates a SimplePool that holds about 0.7 * 2**pow elements before
+   * first rehash.
+   */
+  public WeakPool(int pow) {
+    this.pow = pow;
+    newTable();
+    count = 0;
+    mask = table.length - 1;
+    nextWipe = (int)(MAX_LOAD_WIPE * mask);
+    nextRehash = (int)(MAX_LOAD_REHASH * mask);
+  }
+
+  @SuppressWarnings("unchecked")
+  protected void newTable() {
+    table = new WeakReference[1 << pow];
+  }
+  
+  // ********************** API as simple hash pool ******************* //
+  
+  /**
+   * Asks whether a particular element is already pooled.  NOT A TYPICAL
+   * OPERATION.
+   */
+  public boolean isPooled(E e) {
+    return e == null || query(e) != null;
+  }
+  
+  /**
+   * Returns the matching element if there is one, null if not.
+   */
+  public E query(E e) {
+    if (e == null) return null;
+    int code = e.hashCode();
+    int idx = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = idx;
+
+    for(;;) {
+      WeakReference<E> r = table[idx];
+      if (r == null) break;
+      E o = r.get();
+      if (o != null && e.equals(o)) {
+        return o; // seen before!
+      }
+      idx = (idx + delta) & mask;
+      assert (idx != oidx); // should never wrap around
+    }
+    return null;
+  }
+
+  /**
+   * Returns a pooled element matching e, which will be e if no match
+   * has been previously pooled.
+   */
+  public E pool(E e) {
+    if (e == null) return null;
+    int code = e.hashCode();
+    int idx = code & mask;
+    int delta = (code >> (pow - 1)) | 1; // must be odd!
+    int oidx = idx;
+
+    for(;;) {
+      WeakReference<E> r = table[idx];
+      if (r == null) break;
+      E o = r.get();
+      if (o != null && e.equals(o)) {
+        return o; // seen before!
+      }
+      idx = (idx + delta) & mask;
+      assert (idx != oidx); // should never wrap around
+    }
+    assert (table[idx] == null); // should never fill up
+    // not seen before; add it
+    
+    count++;
+    if (count >= nextWipe) { // too full
+      // determine if size needs to be increased or just wipe unused weak refs
+      int oldCount = count;
+      count = 0;
+      for (int i = 0; i < table.length; i++) {
+        if (table[i] != null && table[i].get() != null) {
+          count++;
+        }
+      }
+      if (DEBUG && oldCount > count) {
+        System.out.println("Weak references collected: " + (oldCount - count));
+      }
+      if (count >= nextRehash) {
+        pow++; // needs to be increased in size
+      }
+      WeakReference<E>[] oldTable = table;
+      newTable();
+      mask = table.length - 1;
+      nextWipe = (int)(MAX_LOAD_WIPE * mask);
+      nextRehash = (int)(MAX_LOAD_REHASH * mask);
+
+      int oldLen = oldTable.length;
+      for (int i = 0; i < oldLen; i++) {
+        WeakReference<E> r = oldTable[i];
+        if (r == null) continue;
+        E o = r.get();
+        if (o == null) continue;
+        // otherwise:
+        code = o.hashCode();
+        idx = code & mask;
+        delta = (code >> (pow - 1)) | 1; // must be odd!
+        while (table[idx] != null) { // we know enough slots exist
+          idx = (idx + delta) & mask;
+        }
+        table[idx] = r;
+      }
+      // done with rehash; now get idx to empty slot
+      code = e.hashCode();
+      idx = code & mask;
+      delta = (code >> (pow - 1)) | 1; // must be odd!
+      while (table[idx] != null) { // we know enough slots exist & new element
+        idx = (idx + delta) & mask;
+      }
+    } else {
+      // idx already pointing to empty slot
+    }
+
+    table[idx] = new WeakReference<E>(e);
+    return e;
+  }
+  
+  
+  // ******************* API as add-only weak hash set *************** //
+  
+  public boolean isMember(E e) {
+    return query(e) != null;
+  }
+  
+  public void add(E e) {
+    /*(void)*/ pool(e);
+  }
+  
+  
+  // ************************** Test main ************************ //
+  
+  /**
+   * BROKEN Test main.
+   */
+  public static void main(String[] args) {
+    WeakPool<Integer> pool = new WeakPool<Integer>(4);
+    for (int i = 0; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o != p) throw new RuntimeException();
+      Integer q = pool.pool(p);
+      if (q != p) throw new RuntimeException();
+    }
+    for (int i = 0; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o == p) throw new RuntimeException();
+      if (!o.equals(p)) throw new RuntimeException();
+    }
+    for (int i = 1; i < 1000000; i += 42) {
+      Integer o = new Integer(i);
+      Integer p = pool.pool(o);
+      if (o != p) throw new RuntimeException();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/automaton/Automaton.java b/src/main/gov/nasa/jpf/util/automaton/Automaton.java
new file mode 100644 (file)
index 0000000..e68fc9f
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.automaton;
+
+import java.io.PrintStream;
+
+
+/**
+ * generic class for modeling automatons
+ * 
+ * Since this is used in so many extensions from both model and native code,
+ * it seems appropriate to add a basis implementation to util.
+ * 
+ * To make it more amenable to modeling (e.g. for native peer implementation),
+ * we avoid using standard Java containers at the expense of efficiency for
+ * large numbers of states and transitions 
+ */
+public class Automaton <S extends State> {
+    
+  static final int STATE_INC = 16;
+  static final int INPUT_INC = 16;
+  
+  protected String label;
+  
+  protected int nStates;
+  protected State[] states;
+  
+  protected int nInputs;
+  protected String[] alphabet;
+  
+  protected int current;
+  
+  
+  public Automaton (String label, int numberOfStates, int numberOfInputs){
+    this.label = label;
+    states = new State[numberOfStates];
+    alphabet = new String[numberOfInputs];    
+  }
+  
+  public Automaton (String label, int numberOfStates){
+    this( label, numberOfStates, INPUT_INC);
+  }
+
+  public Automaton(String label){
+    this(label, STATE_INC, INPUT_INC);
+  }
+  
+  public void addState (State newState){
+    if (nStates == states.length){
+      State[] a = new State[nStates + STATE_INC];
+      System.arraycopy(states, 0, a, 0, nStates);
+      states = a;
+    }
+    
+    states[nStates] = newState;
+    newState.setId(nStates);
+    nStates++;
+  }
+
+  public void addStates (State ... newStates){
+    int n = nStates + newStates.length;
+    if (n >= states.length){
+      State[] a = new State[n];
+      System.arraycopy(states, 0, a, 0, nStates);
+      states = a;      
+    }
+    
+    for (int i=0; i<newStates.length; i++){
+      State s = newStates[i];
+      states[nStates] = s;
+      s.setId(nStates);
+      nStates++;
+    }
+  }
+  
+  public String getLabel(){
+    return label;
+  }
+  
+  public int getNumberOfStates(){
+    return nStates;
+  }
+  
+  public S getCurrentState(){
+    return (S)states[current];
+  }
+  
+  public String[] computeAlphabet(){
+    for (int i = 0; i < nStates; i++) {
+      State s = states[i];
+      int nTrans = s.getNumberOfTransitions();
+
+      nextTransition:
+      for (int j = 0; j < nTrans; j++) {
+        Transition t = s.getTransition(j);
+        String label = t.getLabel();
+
+        for (int k = 0; k < nInputs; k++) {
+          if (alphabet[k].equals(label)) {
+            break nextTransition;
+          }
+        }
+
+        if (nInputs == alphabet.length) {
+          String[] a = new String[nInputs + INPUT_INC];
+          System.arraycopy(alphabet, 0, a, 0, nInputs);
+          alphabet = a;
+        }
+
+        alphabet[nInputs] = label;
+        nInputs++;
+      }
+    }
+    
+    return alphabet;
+  }
+  
+  public String[] getAlphabet(){
+    if (nInputs == 0){
+      return computeAlphabet();
+    } else {
+      return alphabet;
+    }
+  }
+  
+  public void printOn (PrintStream ps){
+    ps.printf("Automaton '%s'\n", label);
+    
+    for (int i=0; i<nStates; i++){
+      states[i].printOn(ps);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/automaton/State.java b/src/main/gov/nasa/jpf/util/automaton/State.java
new file mode 100644 (file)
index 0000000..7d9e975
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.automaton;
+
+import java.io.PrintStream;
+
+/**
+ *
+ */
+public class State {
+  
+  static final int TRANSITION_INC = 4;
+  
+  protected int id;
+  protected String label;
+  
+  protected int nTransitions;
+  protected Transition[] transitions;
+  
+  public State (String label, int numberOfTransitions){
+    this.label = label;
+    transitions = new Transition[numberOfTransitions];
+  }
+  
+  public State (String label){
+    this( label, TRANSITION_INC);
+  }
+
+  public State (){
+    this( null, TRANSITION_INC);
+  }
+  
+  // to be set by Automaton.addState()
+  protected void setId(int id){
+    this.id = id;
+    if (label == null){
+      label = Integer.toString(id);
+    }
+  }
+  
+  public int getId(){
+    return id;
+  }
+  
+  public String getLabel(){
+    return label;
+  }
+  
+  public int getNumberOfTransitions(){
+    return nTransitions;
+  }
+  
+  public Transition getTransition (int idx){
+    return transitions[idx];
+  }
+  
+  public void addTransition(Transition newTransition){
+    if (nTransitions == transitions.length){
+      Transition[] a = new Transition[nTransitions + TRANSITION_INC];
+      System.arraycopy(transitions, 0, a, 0, nTransitions);
+      transitions = a;
+    }
+    
+    transitions[nTransitions] = newTransition;
+    newTransition.setId(nTransitions);
+    nTransitions++;
+  }
+  
+  public void addTransitions(Transition ... newTransitions){
+    int n = nTransitions + newTransitions.length;
+    if (n >= transitions.length){
+      Transition[] a = new Transition[n];
+      System.arraycopy(transitions, 0, a, 0, nTransitions);
+      transitions = a;      
+    }
+    
+    for (int i=0; i<newTransitions.length; i++){
+      transitions[nTransitions] = newTransitions[i];
+      nTransitions++;
+    }
+  }
+  
+  public void enter(){
+    // just here to be overridden, for Moore machines
+  }
+  
+  public void exit(){
+    // just here to be overridden, for Moore machines
+  }
+  
+  public void printOn (PrintStream ps){
+    ps.printf("\t[%d] State '%s'\n", id, label);
+    for (int i=0; i<nTransitions; i++){
+      transitions[i].printOn( ps);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/automaton/Transition.java b/src/main/gov/nasa/jpf/util/automaton/Transition.java
new file mode 100644 (file)
index 0000000..d41e493
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.automaton;
+
+import java.io.PrintStream;
+
+/**
+ *
+ */
+public class Transition {
+  
+  protected int id;
+  protected String label;
+  
+  protected State fromState;
+  protected State toState;
+  
+  public Transition (String label, State toState){
+    // the fromState is set when we add this transition to the state
+    this.toState = toState;
+
+    this.label = label;
+    // id will be set when this transition is added to its fromState
+  }
+  
+  protected void setFromState (State fromState){
+    this.fromState = fromState;
+  }
+  
+  protected void setId (int id){
+    this.id = id;
+  }
+  
+  public int getId(){
+    return id;
+  }
+  
+  public String getLabel(){
+    return label;
+  }
+  
+  public boolean checkGuard(){
+    // override if you have guards
+    return true;
+  }
+  
+  public void fire (){
+    // just here to be overridden, for Mealy machines
+  }
+  
+  public void printOn (PrintStream ps){
+    ps.printf("\t\t[%d] '%s' => state '%s'\n", id, label, toState.getLabel());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/CheckEvent.java b/src/main/gov/nasa/jpf/util/event/CheckEvent.java
new file mode 100644 (file)
index 0000000..e6feb57
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+/**
+ * a pseudo event that encapsulates a (possibly composed) check
+ * 
+ * This event type uses 'alt' for disjunction and 'next' for conjunction if
+ * they point to CheckEvents
+ */
+public abstract class CheckEvent extends SystemEvent {
+  
+  protected CheckEvent (String name, Object... arguments){
+    super(name, arguments);
+  }
+
+  /**
+   * this is the single check condition for this event
+   */
+  public abstract boolean evaluate(MJIEnv env, int objRef);
+
+  /**
+   * conjunctions and disjunctions of this check event
+   */
+  public boolean check (MJIEnv env, int objRef){
+    if (!evaluate(env, objRef)){
+      if (alt != null && alt instanceof CheckEvent){
+        return ((CheckEvent)alt).check(env, objRef);
+      } else {
+        return false;
+      }
+      
+    } else {
+      if (next != null && next instanceof CheckEvent){
+        return ((CheckEvent)next).check(env, objRef);
+      } else {
+        return true;
+      }
+    }
+  }
+
+  /**
+   * generic check evaluation that throws assertions if failed
+   */
+  @Override
+  public void process (MJIEnv env, int objRef){
+    if (!check(env, objRef)){
+      env.throwAssertion("check event failed: " + this);
+    }
+  }
+
+  public CheckEvent or (CheckEvent orCheck){
+    addAlternative(orCheck);
+    
+    return this;
+  }
+  
+  public CheckEvent and (CheckEvent andCheck){
+    addNext( andCheck);
+    
+    return this;
+  }
+  
+  public String getExpression(){
+    if (alt == null && !(next instanceof CheckEvent)){
+      return toString();
+      
+    } else {
+      StringBuilder sb = new StringBuilder();
+      
+      sb.append('(');
+      sb.append(name);
+      
+      for (Event e = alt; e != null; e = e.alt){
+        sb.append( " || ");
+        sb.append(e.name);
+      }
+      
+      for (Event e = next; e instanceof CheckEvent; e = e.next){
+        sb.append( " && ");
+        sb.append(e.name);        
+      }
+      
+      sb.append(')');
+      
+      return sb.toString();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/ControlEvent.java b/src/main/gov/nasa/jpf/util/event/ControlEvent.java
new file mode 100644 (file)
index 0000000..bf76480
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+/**
+ * pseudo event that is used to control the system under test execution
+ */
+public abstract class ControlEvent extends SystemEvent {
+
+  protected ControlEvent (String name, Object... arguments){
+    super(name, arguments);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/event/Event.java b/src/main/gov/nasa/jpf/util/event/Event.java
new file mode 100644 (file)
index 0000000..6ed18cd
--- /dev/null
@@ -0,0 +1,651 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.OATHash;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * class that represents an external stimulus to the SUT, which is produced by EventTree instances
+ * (our environment models)
+ * 
+ * Note that albeit concrete EventTree can provide their own, specialized Event types, this class
+ * is generic enough that we don't declare it as abstract
+ */
+public class Event implements Cloneable {
+
+  static final Object[] NO_ARGUMENTS = new Object[0];
+  
+  //--- linkage
+  protected Event next;
+  protected Event prev;
+  protected Event alt;
+
+  //--- payload
+  protected String name;
+  protected Object[] arguments;
+  
+  protected Object source;  // optional, set on demand to keep track of where an event came from
+
+  public Event (String name){
+    this( name, NO_ARGUMENTS, null);
+  }
+
+  public Event (String name, Object source){
+    this( name, NO_ARGUMENTS, source);
+  }  
+  
+  public Event(String name, Object[] arguments) {
+    this(name, arguments, null);
+  }
+  
+  public Event(String name, Object[] arguments, Object source) {
+    this.name = name;
+    this.arguments = arguments != null ? arguments : NO_ARGUMENTS;
+    this.source = source;
+  }
+
+  
+  
+  @Override
+  public boolean equals (Object o){
+    if (o instanceof Event){
+      Event other = (Event)o;
+      
+      if (name.equals(other.name)){
+        return Misc.equals(arguments, other.arguments);
+      }
+    }
+    
+    return false;
+  }
+  
+  @Override
+  public int hashCode(){
+    int h = name.hashCode();
+    
+    for (int i=0; i<arguments.length; i++){
+      h = OATHash.hashMixin(h, arguments[i].hashCode());
+    }
+    
+    return OATHash.hashFinalize(h);
+  }
+  
+  protected void setNext (Event e){
+    next = e;
+    e.prev = this;
+  }
+
+  protected void setPrev (Event e){
+    prev = e;
+
+    if (alt != null){
+      alt.setPrev(e);
+    }
+  }
+
+  protected void setAlt (Event e){
+    alt = e;
+
+    if (prev != null) {
+      e.setPrev(prev);
+    }
+  }
+  
+  public void setLinksFrom (Event other){
+    prev = other.prev;
+    next = other.next;
+    alt = other.alt;
+  }
+
+  public Event replaceWithSequenceFrom (List<Event> list){
+    Event eLast = null;
+    
+    for (Event e: list){
+      if (eLast == null){
+        e.prev = prev;
+        e.alt = alt;
+      } else {
+        e.prev = eLast;
+        eLast.next = e;
+      }
+      
+      eLast = e;
+    }
+    
+    if (eLast != null){
+      eLast.next = next;
+      return list.get(0);
+    } else {
+      return null;
+    }
+  }
+  
+  public Event replaceWithAlternativesFrom (List<Event> list){
+    Event eLast = null;
+    for (Event e: list){
+      e.prev = prev;
+      e.next = next;
+      
+      if (eLast != null){
+        eLast.alt = e;
+      }
+      
+      eLast = e;
+    }
+    
+    if (eLast != null){
+      eLast.alt = alt;
+      return list.get(0);
+    } else {
+      return null;
+    }
+  }
+
+  public Event replaceWith (Event e){
+    e.prev = prev;
+    e.next = next;
+    e.alt = alt;
+    
+    return e;
+  }
+  
+  protected void setSource (Object source){
+    this.source = source;
+  }
+  
+  public int getNumberOfAlternatives(){
+    int n = 0;
+    for (Event e = alt; e != null; e = e.alt) {
+      n++;
+    }
+
+    return n;
+  }
+
+  public boolean hasAlternatives(){
+    return (alt != null);
+  }
+  
+  public List<Event> getAlternatives(){
+    List<Event> list = new ArrayList<Event>();
+    list.add(this);
+    for (Event e = alt; e != null; e = e.alt) {
+      list.add(e);
+    }
+    return list;
+  }
+  
+  
+  public Event unlinkedClone(){
+    try {
+      Event e = (Event)super.clone();
+      e.next = e.prev = e.alt = null;
+      return e;
+
+    } catch (CloneNotSupportedException x) {
+      throw new RuntimeException("event clone failed", x);
+    }
+    
+  }
+
+  public Event clone(){
+    try {
+      return (Event) super.clone();
+    } catch (CloneNotSupportedException cnsx){
+      throw new RuntimeException("Event clone failed");
+    }
+  }
+
+  public Event deepClone(){
+    try {
+      Event e = (Event)super.clone();
+
+      if (next != null) {
+        e.next = next.deepClone();
+        e.next.prev = e;
+
+        if (next.alt != null){
+          e.next.alt.prev = e;
+        }
+      }
+
+      if (alt != null) {
+        e.alt = alt.deepClone();
+      }
+
+      return e;
+
+    } catch (CloneNotSupportedException x) {
+      throw new RuntimeException("event clone failed", x);
+    }
+  }
+
+  public String getName(){
+    return name;
+  }
+
+  public Object[] getArguments(){
+    return arguments;
+  }
+
+  public Object getArgument(int idx){
+    if (idx < arguments.length){
+      return arguments[idx];
+    }
+    return null;
+  }
+  
+  public Event getNext(){
+    return next;
+  }
+  
+  public Event getAlt(){
+    return alt;
+  }
+  
+  public Event getPrev(){
+    return prev;
+  }
+  
+  public Object getSource(){
+    return source;
+  }
+  
+  public Event addNext (Event e){
+    boolean first = true;
+    for (Event ee : endEvents()){  // this includes alternatives
+      if (!first){
+        e = e.deepClone();
+      } else {
+        first = false;      // first one doesn't need a clone
+      }
+      ee.setNext(e);
+      e.setPrev(ee);
+    }
+
+    return this;
+  }
+
+  public Event addAlternative (Event e){
+    Event ea ;
+    for (ea = this; ea.alt != null; ea = ea.alt);
+    ea.setAlt(e);
+
+    if (next != null){
+      e.setNext( next.deepClone());
+    }
+
+    return this;
+  }
+  
+  protected static Event createClonedSequence (int firstIdx, int len, Event[] events){
+    Event base = events[firstIdx].unlinkedClone();
+    Event e = base;
+
+    for (int i = firstIdx+1; i < len; i++) {
+      Event ne = events[i].unlinkedClone();
+      e.setNext( ne);
+      e = ne;
+    }
+    
+    return base;
+  }
+  
+  /**
+   * extend this tree with a new path 
+   */
+  public void addPath (int pathLength, Event... path){
+    Event t = this;
+    Event pe;
+    
+    outer:
+    for (int i=0; i<pathLength; i++){
+      pe = path[i];
+      for (Event te = t; te != null; te = te.alt){
+        if (pe.equals(te)){      // prefix is in tree
+          
+          if (te.next == null){  // reached tree leaf
+            if (++i < pathLength){ // but the path still has events
+              Event tail = createClonedSequence( i, pathLength, path);
+              te.setNext(tail);
+              tail.setAlt( new NoEvent()); // preserve the tree path
+            }
+            return;
+            
+          } else { // there is a next in the tree
+            t = te.next;
+            
+            if (i == pathLength-1){ // but the path is done, add a NoEvent as a next alternative to mark the end
+              Event e = t.getLastAlt();
+              e.setAlt(new NoEvent());
+              return;
+              
+            } else {
+              continue outer;
+            }
+          }
+        }
+      }
+      
+      //--- path prefix was not in tree, append as (last) alternative
+      Event tail = createClonedSequence( i, pathLength, path);
+      Event e = t.getLastAlt();
+      e.setAlt( tail);
+      
+      return;
+    }
+  }
+
+  public Event getLastAlt (){
+    Event e;
+    for (e=this; e.alt != null; e = e.alt);
+    return e;
+  }
+  
+  protected void collectEndEvents (List<Event> list, boolean includeNoEvents) {
+    if (next != null) {
+      next.collectEndEvents(list, includeNoEvents);
+      
+    } else { // base case: no next
+      // strip trailing NoEvents 
+      if (prev == null){
+        list.add(this); // root NoEvents have to stay
+        
+      } else { // else we skip trailing NoEvents up to root
+        Event ee = this;
+        if (!includeNoEvents){
+          for (Event e=this; e.prev != null && (e instanceof NoEvent); e = e.prev){
+            ee = e.prev;
+          }
+        }
+        list.add(ee);
+      }
+    }
+
+    if (alt != null) {
+      alt.collectEndEvents(list, includeNoEvents);
+    }
+  }
+
+  public Event endEvent() {
+    if (next != null) {
+      return next.endEvent();
+    } else {
+      return this;
+    }
+  }
+
+  public List<Event> visibleEndEvents(){
+    List<Event> list = new ArrayList<Event>();
+    collectEndEvents(list, false);
+    return list;
+  }
+  
+  public List<Event> endEvents(){
+    List<Event> list = new ArrayList<Event>();
+    collectEndEvents(list, true);
+    return list;
+  }
+  
+  private void interleave (Event a, Event b, Event[] path, int pathLength, int i, Event result){
+    if (a == null && b == null){ // base case
+      result.addPath(pathLength, path);
+      
+    } else {
+      if (a != null) {
+        path[i] = a;
+        interleave(a.prev, b, path, pathLength, i - 1, result);
+      }
+
+      if (b != null) {
+        path[i] = b;
+        interleave(a, b.prev, path, pathLength, i - 1, result);
+      }
+    }
+  }
+  
+  /**
+   * this creates a new tree that contains all paths resulting from
+   * all interleavings of all paths of this tree with the specified other events
+   * 
+   * BEWARE: this is a combinatorial bomb that should only be used if we know all
+   * paths are short
+   */
+  public Event interleave (Event... otherEvents){
+    Event t = new NoEvent(); // we need a root for the new tree
+    
+    Event[] pathBuffer = new Event[32];
+    int mergedTrees = 0;
+    
+    for (Event o : otherEvents){
+      List<Event> endEvents = (mergedTrees++ > 0) ? t.visibleEndEvents() : visibleEndEvents();
+
+      for (Event ee1 : endEvents) {
+        for (Event ee2 : o.visibleEndEvents()) {
+          int n = ee1.getPathLength() + ee2.getPathLength();
+          if (n > pathBuffer.length){
+            pathBuffer = new Event[n];
+          }
+
+          interleave(ee1, ee2, pathBuffer, n, n - 1, t);
+        }
+      }
+    }
+        
+    return t.alt;
+  }
+  
+  
+  
+  private void removeSource (Object src, Event[] path, int i, Event result){
+    
+    if (alt != null){
+      alt.removeSource(src, path, i, result);
+    }
+    
+    if (source != src){
+      path[i++] = this;
+    }
+    
+    if (next != null){
+      next.removeSource(src, path, i, result);
+      
+    } else { // done, add path to result
+      result.addPath( i, path);
+    }
+  }
+  
+  /**
+   * remove all events from this tree that are from the specified source 
+   */
+  public Event removeSource (Object src){
+    Event base = new NoEvent(); // we need a root to add to
+    int maxDepth = getMaxDepth();
+    Event[] pathBuffer = new Event[maxDepth];
+    
+    removeSource( src, pathBuffer, 0, base);
+    
+    return base.alt;
+  }
+  
+  private void printPath (PrintStream ps, boolean isLast){
+    if (prev != null){
+      prev.printPath(ps, false);
+    }
+    
+    if (!isNoEvent()){
+      ps.print(name);
+      if (!isLast){
+        ps.print(',');
+      }
+    }
+  }
+  
+  public void printPath (PrintStream ps){
+    printPath(ps, true);
+  }
+
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(name);
+    if (arguments != NO_ARGUMENTS) {
+      sb.append('(');
+      boolean first = true;
+      for (Object a : arguments) {
+        if (first){
+          first = false;
+        } else {
+          sb.append(',');
+        }
+        sb.append(a.toString());
+      }
+      sb.append(')');
+    }
+    return sb.toString();
+  }
+
+  
+  /**
+   * upwards path length 
+   */
+  public int getPathLength(){
+    int n=0;
+    
+    for (Event e=this; e != null; e = e.prev){
+      n++;
+    }
+    
+    return n;
+  }
+  
+  
+  private int getMaxDepth (int depth){
+    int maxAlt = depth;
+    int maxNext = depth;
+    
+    if (alt != null){
+      maxAlt = alt.getMaxDepth(depth);
+    }
+    
+    if (next != null){
+      maxNext = next.getMaxDepth(depth + 1);
+    }
+    
+    if (maxAlt > maxNext){
+      return maxAlt;
+    } else {
+      return maxNext;
+    }
+  }
+  
+  /**
+   * maximum downwards tree depth 
+   */
+  public int getMaxDepth(){
+    return getMaxDepth(1);
+  }
+  
+  public Event[] getPath(){
+    int n = getPathLength();
+    Event[] trace = new Event[n];
+    
+    for (Event e=this; e != null; e = e.prev){
+      trace[--n] = e;
+    }
+    
+    return trace;
+  }
+  
+  public void printTree (PrintStream ps, int level) {
+    for (int i = 0; i < level; i++) {
+      ps.print(". ");
+    }
+    
+    ps.print(this);
+    //ps.print(" [" + prev + ']');
+    ps.println();
+
+    if (next != null) {
+      next.printTree(ps, level + 1);
+    }
+
+    if (alt != null) {
+      alt.printTree(ps, level);
+    }
+  }
+  
+  public boolean isEndOfTrace (String[] eventNames){
+    int n = eventNames.length-1;
+    
+    for (Event e=this; e!= null; e = e.prev){
+      if (e.getName().equals(eventNames[n])){
+        n--;
+      } else {
+        return false;
+      }
+    }
+    
+    return (n == 0);
+  }
+  
+  protected void collectTrace (StringBuilder sb, String separator, boolean isLast){
+    if (prev != null){
+      prev.collectTrace(sb, separator, false);    
+    }
+
+    if (!isNoEvent()){
+      sb.append(toString());
+      
+      if (!isLast && separator != null){
+        sb.append(separator);        
+      }
+    }
+  }
+  
+  public String getPathString (String separator){
+    StringBuilder sb = new StringBuilder();
+    collectTrace( sb, separator, true);
+    return sb.toString();
+  }
+  
+  public boolean isNoEvent(){
+    return false;
+  }
+
+  public boolean isSystemEvent(){
+    return false;
+  }
+
+  //--- generic processing interface
+  
+  public void process(Object source){
+    // can be overridden by subclass if instance has sufficient context info
+  }
+  
+  public void setProcessed(){
+    // can be overridden by subclass, e.g. to maintain event caches
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/EventChoiceGenerator.java b/src/main/gov/nasa/jpf/util/event/EventChoiceGenerator.java
new file mode 100644 (file)
index 0000000..5a142d8
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.util.Predicate;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.SystemState;
+
+
+/**
+ * ChoiceGenerator for Events.
+ * This is basically just a pointer into the event tree
+ */
+public class EventChoiceGenerator extends ChoiceGeneratorBase<Event> {
+
+  protected Event base;
+  protected Event cur;
+  protected int nProcessed;
+  
+  protected EventContext ctx; // optional, can replace/expand events during execution
+  
+  /**
+   * convenience method to get successors from current CG chain 
+   */
+  public static EventChoiceGenerator getNext (SystemState ss, String id, Event base, EventContext ctx){
+    EventChoiceGenerator cgPrev = ss.getLastChoiceGeneratorOfType(EventChoiceGenerator.class);
+    if (cgPrev == null){
+      return new EventChoiceGenerator( id, base, ctx);
+    } else {
+      return cgPrev.getSuccessor(id, ctx);
+    }
+  }
+  
+  public EventChoiceGenerator (String id, Event base){
+    this(id, base, null);
+  }
+  
+  public EventChoiceGenerator (String id, Event base, EventContext ctx) {
+    super(id);
+    this.base = base;
+    this.ctx = ctx;
+  }
+  
+  @Override
+  public Event getChoice (int idx){
+    if (idx >= 0){
+      Event e = base;
+      for (int i=0; i<idx; i++){
+        e = e.alt;
+        if (e == null){
+          break;
+        } else {
+          return e;
+        }
+      }
+      
+    }
+    throw new IllegalArgumentException("choice index out of range: " + idx);
+  }
+
+  
+  public void setContextExpander (EventContext ctx){
+    this.ctx = ctx;
+  }
+  
+  public boolean containsMatchingChoice (Predicate<Event> predicate){
+    for (Event e = base; e != null; e = e.getAlt()){
+      if (predicate.isTrue(e)){
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  public void addChoice (Event newEvent){
+    for (Event e = base; e != null;){
+      Event eAlt = e.getAlt();
+      if (eAlt == null){
+        e.setAlt(newEvent);
+        return;
+      }
+      e = eAlt;
+    }
+  }
+  
+  public EventChoiceGenerator getSuccessor (String id){
+    return getSuccessor(id, null);
+  }
+  
+  public EventChoiceGenerator getSuccessor (String id, EventContext ctx){
+    if (cur == null){
+      return new EventChoiceGenerator(id, base.getNext(), ctx);
+      
+    } else {
+      Event next = cur.getNext();
+      
+      if (cur instanceof CheckEvent){ // CheckEvents use next for conjunction
+        while (next instanceof CheckEvent){
+          next = next.getNext();
+        }
+      }
+      
+      if (next != null){
+        return new EventChoiceGenerator( id, next, ctx);
+      } else {
+        return null; // done
+      }
+    }
+  }
+  
+  @Override
+  public Event getNextChoice () {
+    return cur;
+  }
+
+
+  @Override
+  public boolean hasMoreChoices () {
+    if (cur == null){
+      return (nProcessed == 0);
+    } else {
+      return (cur.getAlt() != null);
+    }
+  }
+
+  @Override
+  public void advance () {
+    if (cur == null){
+      if (nProcessed == 0){
+        cur = base;
+        nProcessed = 1;
+      }
+    } else {
+      cur = cur.getAlt();
+      nProcessed++;
+    }
+    
+    if (ctx != null){
+      Event newCur = ctx.map(cur);
+      if (newCur != cur){
+        cur = newCur;
+      }
+    }
+  }
+
+  @Override
+  public void reset () {
+    isDone = false;
+    cur = null;
+    nProcessed = 0;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return base.getNumberOfAlternatives() + 1; // include base itself
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return nProcessed;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append("{id:\"");
+    sb.append(id);
+    sb.append('"');
+
+    //sb.append(",isCascaded:");
+    //sb.append(Boolean.toString(isCascaded));
+
+    sb.append(",[");
+    for (Event e=base; e!= null; e = e.getAlt()){
+      if (e != base){
+        sb.append(',');
+      }
+      if (e == cur){
+        sb.append(MARKER);        
+      }
+      sb.append(e.toString());
+    }
+    sb.append("],cur:");
+    sb.append(cur);
+    sb.append("}");
+    
+    return sb.toString();
+  }
+
+  @Override
+  public Class<Event> getChoiceType() {
+    return Event.class;
+  }
+  
+  protected Event[] getFirstNChoices(int n){
+    Event[] a = new Event[n];
+    
+    Event e = base;
+    for (int i=0; i<n; i++){
+      a[i] = e;
+      e = e.getAlt();
+    }
+    
+    return a;
+  }
+
+  @Override
+  public Event[] getAllChoices(){
+    return getFirstNChoices( getTotalNumberOfChoices());
+  }
+
+  @Override
+  public Event[] getProcessedChoices(){
+    return getFirstNChoices( getProcessedNumberOfChoices());
+  }
+  
+  @Override
+  public Event[] getUnprocessedChoices(){
+    int n=0;
+    for (Event e=cur; e != null; e = e.getAlt()){
+      n++;
+    }
+    
+    Event[] a = new Event[n];
+    
+    Event e = cur;
+    for (int i=0; i<n; i++){
+      a[i] = e;
+      e = e.getAlt();
+    }
+    
+    return a;    
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/EventConstructor.java b/src/main/gov/nasa/jpf/util/event/EventConstructor.java
new file mode 100644 (file)
index 0000000..4ab0348
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+/**
+ * abstract class that hold API to create event trees
+ * 
+ * this factors out constructor methods so that they can be used inside of
+ * EventTrees and EventForests
+ */
+public interface EventConstructor {
+
+  //--- overridable event factory method to facilitate creation of specialized event classes
+
+  default Event event (String name) {
+    return new Event(name, this);
+  }
+  
+  default Event event (String name, Object... arguments){
+    return new Event(name, arguments, this);
+  }
+
+  //--- compound constructors that create sets of events
+  
+  default Event alternatives (Event... events){
+    Event last = events[0];
+    for (int i = 1; i < events.length; i++) {
+      Event e = events[i];
+      last.setAlt(e);
+      last = e;
+    }
+    return events[0];
+  }
+
+  default Event sequence (Event... events) {
+    Event base = events[0];
+
+    for (int i = 1; i < events.length; i++) {
+      base.addNext(events[i]);
+    }
+    return base;
+  }
+
+  default Event iteration (int count, Event... events) {
+    Event seq = sequence(events);
+    Event[] it = new Event[count];
+
+    it[0] = seq;
+    for (int i=1; i<count; i++){
+      it[i] = seq.deepClone();
+    }
+
+    return sequence(it);
+  }
+
+  /**
+   * an alterative of all combinations of the specified events (regardless of order) 
+   */
+  default Event anyCombination (Event... events){
+    int n = events.length;
+    int max = 0;
+    for (int i=0; i<n; i++){
+      max = (max << 1) | 1;
+    }
+    
+    Event[] pathBuffer = new Event[n];
+    
+    // we use the no-event as the anchor
+    Event eFirst = new NoEvent();
+    
+    // now fill in all the remaining combinations
+    for (int i=1; i<=max; i++){
+      // init the path buffer
+      int pathLength=0;
+      for (int j=0, m=i; m != 0; j++){
+        if ((m & 1) != 0){
+          pathBuffer[pathLength++] = events[j];
+        }
+        m>>>=1;
+      }
+      
+      eFirst.addPath( pathLength, pathBuffer);
+    }
+      
+    return eFirst;
+  }
+  
+  
+  default void generatePermutation (int length, Event[] events, Event anchor, Event perm){
+    if (length == 0){
+      anchor.addAlternative(perm);
+      
+    } else {
+      outer:
+      for (Event e : events){
+        if (perm != null){
+          // check if e is already in there
+          for (Event ee = perm; ee != null; ee = ee.getNext()){
+            if (ee.equals(e)){
+              continue outer;
+            }
+          }          
+          e = perm.deepClone().addNext(e.deepClone());
+          
+        } else {
+          e = e.deepClone();
+        }
+        
+        generatePermutation( length-1, events, anchor, e);
+      }
+    }
+  }
+  
+  /**
+   * generate tree with all event permutations without repetitions.
+   * <2do> this is not particularly optimized
+   */
+  default Event anyPermutation (Event... events){
+    Event a = new NoEvent();
+    generatePermutation( events.length, events, a, null);
+    return a.getAlt();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/event/EventContext.java b/src/main/gov/nasa/jpf/util/event/EventContext.java
new file mode 100644 (file)
index 0000000..b49f1c5
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+/**
+ * functional interface that is used to expand events from execution context 
+ */
+public interface EventContext {
+  Event map (Event original);
+}
diff --git a/src/main/gov/nasa/jpf/util/event/EventForest.java b/src/main/gov/nasa/jpf/util/event/EventForest.java
new file mode 100644 (file)
index 0000000..fc4945f
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import java.util.HashMap;
+
+/**
+ * a forest of named event trees
+ *
+ * This class mostly exists for the purpose of tree construction, which happens from respective ctors like
+ * 
+ *  EventForest myForest = new EventForest(){
+ *    @Override
+ *    protected Event createRoot(){
+ *        addRoot("someState",
+ *                sequence(
+ *                    event(..),
+ *                 ...
+ *        );
+ *
+ *        addRoot("someOtherState",
+ *                ...
+ *
+ *        return sequence( ... ); // default tree
+ *   }
+ * 
+ */
+public abstract class EventForest extends EventTree {
+
+  protected HashMap<String,Event> rootMap;
+
+  //--- construction
+
+  /**
+   *  usually called from createRootEvent()
+   */
+  public void addRoot (String name, Event nextRoot){
+    if (rootMap == null){
+      rootMap = new HashMap<>();
+    }
+    rootMap.put(name, nextRoot);
+  }
+
+
+  //--- accessors
+
+  public Event getRoot (String name) {
+    if (rootMap != null) {
+      return rootMap.get(name);
+    } else {
+      return null;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/EventTree.java b/src/main/gov/nasa/jpf/util/event/EventTree.java
new file mode 100644 (file)
index 0000000..a451495
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * an abstract class that creates Event trees
+ * 
+ * While there is no need to provide specialized Event types or additional event 
+ * constructors, concrete subclasses have to provide a createRoot() implementation.
+ * 
+ * A typical implementation looks like this
+ *
+ *   class MyTree extends EventTree {
+ *     @Override
+ *     public Event createRoot() {
+ *       return sequence(
+ *               event("a"),
+ *               alternatives(
+ *                       event("1"),
+ *                       iteration(2,
+ *                               event("x")
+ *                       )
+ *               ),
+ *               event("b")
+ *       );
+ *     }
+ *   }
+ *
+ *   or alternatively
+ *   class MyTree extends EventTree {
+ *     MyTree(){
+ *       root = sequence(
+ *                 event("a"),
+ *                    alternative(
+ *                       event("1),
+ *                       event("2")
+ *                 )
+ *              );
+ *     }
+ *   }
+ *
+ *   or alternatively as an anonymous class
+ *
+ *     EventTree myTree = new EventTree(
+ *         sequence(
+ *             event("a"),
+ *             alternatives(
+ *                 event("1"),
+ *                 iteration(2,
+ *                     event("x")
+ *                 )
+ *             ),
+ *             event("b")
+ *         )
+ *     );
+ *
+ */
+public class EventTree implements EventConstructor {
+  
+  public static final String CONFIG_KEY = "event.tree.class";
+  
+  protected Event root;
+
+  /**
+   * this is our purpose in life, which has to be provided by concrete subclasses 
+   */
+  public Event createRoot() {
+    // nothing here, needs to be overridden by subclass to populate tree
+    return null;
+  }
+
+  protected EventTree () {
+    root = createRoot();
+  }
+
+  protected EventTree (Event root){
+    this.root = root;
+  }
+  
+  public Event getRoot(){
+    return root;
+  }
+    
+  //--- inspection and debugging
+
+  static final List<Event> NO_EVENTS = new ArrayList<Event>(0);
+  
+  public List<Event> visibleEndEvents(){
+    if (root != null){
+      return root.visibleEndEvents();
+    } else {
+      return NO_EVENTS;
+    }
+  }
+  
+  public void printPaths(){
+    for (Event es : visibleEndEvents()){
+      es.printPath(System.out);
+      System.out.println('.');
+    }
+  }
+
+  public void printTree (){
+    if (root != null){
+      root.printTree(System.out, 0);
+    }
+  }
+
+  /**
+   * this should be overridden in case we want to check if this is an expected trace
+   * The generic form can only check if this is a valid end event.
+   * 
+   * To check for a whole trace, implementors should keep some sort of expected event specs
+   */
+  public boolean checkPath (Event lastEvent){
+    for (Event ee : root.visibleEndEvents()){
+      if (ee.equals(lastEvent)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean checkPath (Event lastEvent, String[] pathSpecs) {
+    String trace = lastEvent.getPathString(null);
+
+    for (int i = 0; i < pathSpecs.length; i++) {
+      if (trace.equals(pathSpecs[i])) {
+        pathSpecs[i] = null;
+        return true;
+      }
+    }
+
+    return false; // unexpected trace
+  }
+  
+  /**
+   * override this if the concrete model keeps track of coverage
+   * 
+   * @return [0.0..1.0]
+   */
+  public float getPathCoverage (){
+    throw new UnsupportedOperationException("path coverage not supported by generic EventTree");
+  }
+  
+  /**
+   * override this if the concrete model can keep track of coverage
+   * call at the end of execution
+   */
+  public boolean isCompletelyCovered (){
+    throw new UnsupportedOperationException("path coverage not supported by generic EventTree");
+  }
+  
+  /**
+   * extend this tree with a new path 
+   */
+  public void addPath (Event... path){
+    if (root != null){
+      root.addPath(path.length, path);
+    } else {
+      root = sequence(path);
+    }
+  }
+  
+  public Event interleave (Event... otherTrees){
+    if (root != null){
+      return root.interleave( otherTrees);
+      
+    } else {
+      if (otherTrees == null || otherTrees.length == 0){
+        return root;
+        
+      } else {
+        Event first = null;
+        List<Event> rest = new ArrayList<Event>();
+        for (int i=0; i< otherTrees.length; i++){
+          if (otherTrees[i] != null){
+            if (first == null){
+              first = otherTrees[i];
+            } else {
+              rest.add( otherTrees[i]);
+            }
+          }
+        }
+        
+        if (first != null){
+          if (rest.isEmpty()){
+            return first;
+          } else {
+            Event[] ot = new Event[rest.size()];
+            rest.toArray(ot);
+            return first.interleave(ot);
+          }
+          
+        } else {      // nothing to interleave at all
+          return null;
+        }
+      }
+    }
+  }
+
+  public EventTree interleave (EventTree... otherTrees){
+    Event[] otherRoots = new Event[otherTrees.length];
+    for (int i=0; i<otherRoots.length; i++){
+      otherRoots[i] = otherTrees[i].root;
+    }
+    return new EventTree( interleave( otherRoots));
+  }  
+  
+  public Event removeSource (Object source){
+    if (root != null){
+      return root.removeSource(source);
+      
+    } else { // nothing to remove from
+      return null;
+    }
+  }
+  
+  public int getMaxDepth(){
+    if (root != null){
+      return root.getMaxDepth();
+    } else {
+      return 0;
+    }
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/util/event/NoEvent.java b/src/main/gov/nasa/jpf/util/event/NoEvent.java
new file mode 100644 (file)
index 0000000..b795038
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+/**
+ * a null event, which is usually ignored by EventProducers
+ */
+public class NoEvent extends Event {
+  
+  // we don't have a singleton since we couldn't detect at compile time if
+  // links are going to be modified
+  
+  public NoEvent (){
+    super("<NONE>");
+  } 
+  
+  @Override
+  public boolean isNoEvent(){
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/event/PropagatingEventContext.java b/src/main/gov/nasa/jpf/util/event/PropagatingEventContext.java
new file mode 100644 (file)
index 0000000..32b06da
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+/**
+ * an EventContext that is invariant and hence can be automatically
+ * propagated along the path
+ */
+public interface PropagatingEventContext extends EventContext {
+  // no additional methods, just a type
+}
diff --git a/src/main/gov/nasa/jpf/util/event/SystemEvent.java b/src/main/gov/nasa/jpf/util/event/SystemEvent.java
new file mode 100644 (file)
index 0000000..e3c90df
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+/**
+ * event type that is not supposed to be handled by the SUT
+ */
+public abstract class SystemEvent extends Event {
+
+  protected SystemEvent (String name, Object... arguments){
+    super(name, arguments);
+  }
+
+  @Override
+  public boolean isSystemEvent(){
+    return true;
+  }
+
+  public void process (MJIEnv env, int objRef){
+    // nothing, optionally overridden by subclass
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/event/TestEventTree.java b/src/main/gov/nasa/jpf/util/event/TestEventTree.java
new file mode 100644 (file)
index 0000000..f5b74bf
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+/**
+ * EventTree that can check traces and coverage against expected traces
+ * for testing purposes.
+ * 
+ * This has little purpose except of keeping tree spec and expected traces
+ * together in the same native class, so that we can check paths ad hoc
+ * from regression tests without having to create expected path strings
+ * in the JPF part of the test, only to translate them into native strings
+ */
+public class TestEventTree extends EventTree {
+  
+  protected String[] expected; // to be optionally initialized by subclasses
+  
+  public TestEventTree (){
+    // nothing here
+  }
+  
+  public TestEventTree (Event root){
+    super(root);
+  } 
+  
+  @Override
+  public boolean checkPath (Event lastEvent) {
+    if (expected != null){
+      return checkPath( lastEvent, expected);
+    } else {
+      System.err.println("warning: trying to check path of " + this + " without 'expected' specification");
+      return true; // nothing to check
+    }
+  }
+
+  @Override
+  public boolean isCompletelyCovered (){
+    if (expected != null){
+      return isCompletelyCovered(expected);
+    } else {
+      System.err.println("warning: trying to check coverage of " + this + " without 'expected' specification");
+      return true;
+    }
+  }
+  
+  public boolean isCompletelyCovered (String[] expected) {
+    for (int i = 0; i < expected.length; i++) {
+      if (expected[i] != null) {
+        // no checkPath() call for this one
+        return false;
+      }
+    }
+
+    return true; // no un-visited expected trace left
+  }
+
+  public float getPathCoverage (String[] expected) {
+    int n = 0;
+
+    for (int i = 0; i < expected.length; i++) {
+      if (expected[i] == null) {
+        n++;
+      }
+    }
+
+    return (float) n / expected.length;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/AbstractValue.java b/src/main/gov/nasa/jpf/util/json/AbstractValue.java
new file mode 100644 (file)
index 0000000..f757dd4
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * Implementation of all Value methods. Classes that inherits this class should
+ * only override methods they can handle. Other methods will throw exceptions with
+ * error description.
+ *
+ * @author Ivan Mushketik
+ */
+public class AbstractValue implements Value {
+
+  String read;
+
+  protected AbstractValue(String read) {
+    this.read = read;
+  }
+
+  @Override
+  public String getString() {
+    throw new JPFException("Can't convert '" + read + " to String");
+  }
+
+  @Override
+  public Double getDouble() {
+    throw new JPFException("Can't convert '" + read + "' to Double");
+  }
+
+  @Override
+  public JSONObject getObject() {
+    throw new JPFException("Can't convert '" + read + "' to JSON object");
+  }
+
+  @Override
+  public Value[] getArray() {
+    throw new JPFException("Can't convert '" + read + "' to Array");
+  }
+
+  @Override
+  public Boolean getBoolean() {
+    throw new JPFException("Can't convert '" + read + "' to Boolean");
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/json/ArrayValue.java b/src/main/gov/nasa/jpf/util/json/ArrayValue.java
new file mode 100644 (file)
index 0000000..855f9c7
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPFException;
+
+import java.util.ArrayList;
+
+/**
+ * Array parsed from JSON document
+ * @author Ivan Mushketik
+ */
+public class ArrayValue implements Value {
+
+  ArrayList<Value> values = new ArrayList<Value>();
+
+
+
+  @Override
+  public String getString() {
+    throw new JPFException("Can't convert array to string");
+  }
+
+  @Override
+  public Double getDouble() {
+    throw new JPFException("Can't convert array to double");
+  }
+
+  @Override
+  public JSONObject getObject() {
+    throw new JPFException("Can't convert array to object");
+  }
+
+  @Override
+  public Value[] getArray() {
+    Value[] result = new Value[values.size()];
+
+    return values.toArray(result);
+  }
+
+  @Override
+  public Boolean getBoolean() {
+    throw new JPFException("Can't convert array to boolean");
+  }
+
+  void addValue(Value value) {
+    values.add(value);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/json/BooleanValue.java b/src/main/gov/nasa/jpf/util/json/BooleanValue.java
new file mode 100644 (file)
index 0000000..c4cce2a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * Boolean value from JSON document
+ * @author Ivan Mushketik
+ */
+class BooleanValue extends AbstractValue {
+
+  Boolean value;
+
+  public BooleanValue(boolean b, String read) {
+    super(read);
+    value = b;
+  }
+
+  @Override
+  public Boolean getBoolean() {
+    return value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/CGCall.java b/src/main/gov/nasa/jpf/util/json/CGCall.java
new file mode 100644 (file)
index 0000000..32f24bd
--- /dev/null
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+public class CGCall {
+
+  private ArrayList<Value> params = new ArrayList<Value>();
+  private String name;
+
+  public CGCall(String name) {
+    this.name = name;
+  }
+
+  void addParam(Value value) {
+    if (value == null) {
+      throw new NullPointerException("Null value added to CGCall");
+    }
+
+    params.add(value);
+  }
+
+  public Value[] getValues() {
+    Value paramsArr[] = new Value[params.size()];
+    params.toArray(paramsArr);
+
+    return paramsArr;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  /**
+   * This method go through JSON object and finds all CGs to set in a current state.
+   * It also gives each CG it's a unique id. Each id is calculated in a following way:
+   * <li> If CG call is in the root of JSON object it's id will be equals to it's key in JSON
+   * <li> If CG call isn't in a root object it's id will be concatenated keys of
+   * all parent objects + key of CG.
+   * <li> If CG call is in an object that is an element of array CG's id will be
+   * concatenated keys of all parent array + "[i" (where i is a pos of parent object
+   * of CG in array + CG's key)
+   *
+   * This id also used in JSONObject.fillObject() to find CG to use when field that
+   * should be set with CG is found.
+   *
+   * @param jsonObject - parsed JSON object
+   * @param CGCreators - hash of factories to create Choice Generators
+   * @return list of choice generators that should be set in a current state.
+   */
+  public static List<ChoiceGenerator<?>> createCGList(JSONObject jsonObject) {
+    List<ChoiceGenerator<?>> result = new ArrayList<ChoiceGenerator<?>>();
+    createCGs(jsonObject, "", result);
+
+    return result;
+  }
+
+  private static void createCGs(JSONObject jsonObject, String prefix, List<ChoiceGenerator<?>> result) {
+    for (String cgKey : jsonObject.getCGCallsKeys()) {
+      CGCall cgCall = jsonObject.getCGCall(cgKey);
+      CGCreator creator = CGCreatorFactory.getFactory().getCGCreator(cgCall.getName());
+
+      ChoiceGenerator<?> newCG = creator.createCG(prefix + cgKey, cgCall.getValues());
+      result.add(newCG);
+    }
+
+    for (String valueKey : jsonObject.getValuesKeys()) {
+      Value v = jsonObject.getValue(valueKey);
+
+      if (v instanceof JSONObjectValue) {
+        createCGs(v.getObject(), prefix + valueKey, result);
+        
+      } else if (v instanceof ArrayValue) {
+        Value[] values = v.getArray();
+
+        for (int i = 0; i < values.length; i++) {
+          if (values[i] instanceof JSONObjectValue) {
+            createCGs(values[i].getObject(), prefix + valueKey + "[" + i, result);
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/CGCreator.java b/src/main/gov/nasa/jpf/util/json/CGCreator.java
new file mode 100644 (file)
index 0000000..2915276
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+
+/**
+ * Creates Choice generator from Value array.
+ * We need this interface because there are too many Choice Generators in JPF if we
+ * will try to create CG trying to match CG constructors parameters and Values array
+ * we will have following problems:
+ * <li> Ambiguity with some constructors
+ * <li> User would unable to create some kind of CG (some requires Config for example)
+ * <li> User would need to specify unique ids in JSON
+ * @see Value
+ * @author Ivan Mushketik
+ */
+public interface CGCreator {
+
+  /**
+   * Create choice generator
+   * @param id - unique id for this CG
+   * @param params - params read from JSON file
+   * @return new CG.
+   */
+  public ChoiceGenerator<?> createCG(String id, Value[] params);
+}
diff --git a/src/main/gov/nasa/jpf/util/json/CGCreatorFactory.java b/src/main/gov/nasa/jpf/util/json/CGCreatorFactory.java
new file mode 100644 (file)
index 0000000..a00e222
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.BooleanChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.choice.DoubleChoiceFromList;
+import gov.nasa.jpf.vm.choice.DoubleThresholdGenerator;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
+import gov.nasa.jpf.vm.choice.RandomIntIntervalGenerator;
+
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+/**
+ * Singleton factory for creating CGCreators.
+ * @author Ivan Mushketik
+ */
+public class CGCreatorFactory {
+
+  private static CGCreatorFactory factory;
+
+  ClassLoader loader = CGCreatorFactory.class.getClassLoader();
+
+  // Hash where key is a name that user can use in JSON document to set a
+  // ChoiceGenerator and value is creator of ChoiceGenerator that uses Values[]
+  // from JSON to creat CG
+  private HashMap<String, CGCreator> cgTable = new HashMap<String, CGCreator>() {{
+    put("TrueFalse", new BoolCGCreator());
+    put("IntSet", new IntFromSetCGCreator());
+    put("IntInterval", new IntIntervalCGCreator());
+    put("DoubleSet", new DoubleFromSetCGCreator());
+    put("DoubleThreshold", new DoubleThresholdGeneratorCGCreator());
+    put("RandomIntInerval", new RandomIntIntervalGeneratorCGCreator());
+  }};
+
+  private CGCreatorFactory() {
+    Config config = VM.getVM().getConfig();
+    String[] cgCreators = config.getStringArray("cg-creators");
+
+    // If user specified names for additional CG creators, lets add them
+    if (cgCreators != null) {
+      for (String creator : cgCreators) {
+        String[] nameClassPair = creator.split(":");
+        String cgName = nameClassPair[0];
+        String cgCreatorClassName = nameClassPair[1];
+
+        CGCreator cgCreator = createCGCreator(cgCreatorClassName);
+
+        addCGCreator(cgName, cgCreator);
+      }
+    }
+  }
+
+  public static CGCreatorFactory getFactory() {
+    if (factory == null) {
+      factory = new CGCreatorFactory();
+    }
+
+    return factory;
+  }
+
+  public CGCreator getCGCreator(String key) {
+    return cgTable.get(key);
+  }
+
+  public void addCGCreator(String cgName, CGCreator cgCreator) {
+    if (cgTable.containsKey(cgName)) {
+      throw new JPFException("CGCreator with name '" + cgName + "' already exists");
+    }
+
+    cgTable.put(cgName, cgCreator);
+  }
+
+  private CGCreator createCGCreator(String cgCreatorClassName) {
+    try {
+      Class cgCreatorClass = loader.loadClass(cgCreatorClassName);
+      // We search for a constructor with no parameters
+      Constructor ctor = cgCreatorClass.getDeclaredConstructor();
+      ctor.setAccessible(true);
+      return (CGCreator) ctor.newInstance();
+    } catch (Exception ex) {
+      throw new JPFException(ex);
+    }
+
+  }
+}
+
+/**
+ * CGCreator that creates instance of BooleanChoiceGenerator
+ */
+class BoolCGCreator implements CGCreator {
+
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    return new BooleanChoiceGenerator(id);
+  }
+}
+
+
+/**
+ * CGCreator that creates IntChoiceFromSet CG 
+ */
+class IntFromSetCGCreator implements CGCreator {
+
+  // <2do> add support from ctor with no params
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    int[] intSet = new int[params.length];
+
+    for (int i = 0; i < intSet.length; i++) {
+      intSet[i] = params[i].getDouble().intValue();
+    }
+
+    return new IntChoiceFromSet(id, intSet);
+  }
+}
+
+class IntIntervalCGCreator implements CGCreator {
+
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    int min = params[0].getDouble().intValue();
+    int max = params[1].getDouble().intValue();
+    if (params.length == 2) {
+      return new IntIntervalGenerator(id, min, max);
+    } else if (params.length == 3) {
+      int delta = params[2].getDouble().intValue();
+      return new IntIntervalGenerator(id, min, max, delta);
+    }
+
+    throw new JPFException("Can't create IntIntevalChoiceGenerator with id " + id);
+  }
+}
+
+class DoubleFromSetCGCreator implements CGCreator {
+
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    double[] doubleSet = new double[params.length];
+
+    for (int i = 0; i < doubleSet.length; i++) {
+      doubleSet[i] = params[i].getDouble().doubleValue();
+    }
+
+    return new DoubleChoiceFromList(id, doubleSet);
+  }
+
+}
+
+class DoubleThresholdGeneratorCGCreator implements CGCreator {
+
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    if (params.length != 0) {
+      throw new JPFException("Double threshold generator requires empty parameters list");
+    }
+    Config config = VM.getVM().getConfig();
+    return new DoubleThresholdGenerator(config, id);
+  }
+
+}
+
+class RandomIntIntervalGeneratorCGCreator implements CGCreator {
+
+  @Override
+  public ChoiceGenerator<?> createCG(String id, Value[] params) {
+    int min = params[0].getDouble().intValue();
+    int max = params[1].getDouble().intValue();
+    int nChoices = params[2].getDouble().intValue();
+
+    if (params.length == 3) {
+      return new RandomIntIntervalGenerator(id, min, max, nChoices);
+    } else if (params.length == 4) {
+      long seed = params[3].getDouble().longValue();
+
+      return new RandomIntIntervalGenerator(id, min, max, nChoices, seed);
+    }
+
+    throw new JPFException("Unexpected length of parameters list " + params.length);
+  }
+  
+}
+
diff --git a/src/main/gov/nasa/jpf/util/json/Creator.java b/src/main/gov/nasa/jpf/util/json/Creator.java
new file mode 100644 (file)
index 0000000..08321e6
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+/**
+ * Creates new JPF from JSON value.
+ * @author Ivan Mushketik
+ */
+interface Creator {
+  /**
+   * Create new object, according to read value.
+   * @param env - MJI environment
+   * @param typeName - name of the new object's type
+   * @param value - value read from JSON document
+   * @return reference to the new object
+   */
+  public int create(MJIEnv env, String typeName, Value value);
+}
diff --git a/src/main/gov/nasa/jpf/util/json/CreatorsFactory.java b/src/main/gov/nasa/jpf/util/json/CreatorsFactory.java
new file mode 100644 (file)
index 0000000..33c519d
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+
+import java.util.HashMap;
+
+public class CreatorsFactory {
+
+  static private final HashMap<String, Creator> creatorsTable = new HashMap<String, Creator>();
+
+  static {
+    creatorsTable.put("java.lang.Boolean", new BoxedBoolCreator());
+    creatorsTable.put("java.lang.Byte", new BoxedByteCreator());
+    creatorsTable.put("java.lang.Short", new BoxedShortCreator());
+    creatorsTable.put("java.lang.Integer", new BoxedIntCreator());
+    creatorsTable.put("java.lang.Long", new BoxedLongCreator());
+    creatorsTable.put("java.lang.Float", new BoxedFloatCreator());
+    creatorsTable.put("java.lang.Double", new BoxedDoubleCreator());
+    creatorsTable.put("java.lang.String", new StringCreator());
+  }
+
+  public static Creator getCreator(String typeName) {
+
+    return creatorsTable.get(typeName);
+  }
+}
+
+
+class BoxedBoolCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Boolean read = value.getBoolean();
+    int boolRef = MJIEnv.NULL;
+
+    if (read != null) {
+      boolRef = env.newObject("java.lang.Boolean");
+      ElementInfo ei = env.getModifiableElementInfo(boolRef);
+      ei.setBooleanField("value", (read == true));
+    }
+
+    return boolRef;
+  }
+}
+
+class BoxedByteCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int byteRef = MJIEnv.NULL;
+
+    if (read != null) {
+      byteRef = env.newObject("java.lang.Byte");
+      ElementInfo ei = env.getModifiableElementInfo(byteRef);
+      ei.setByteField("value", read.byteValue());
+    }
+
+    return byteRef;
+  }
+}
+
+class BoxedShortCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int shortRef = MJIEnv.NULL;
+
+    if (read != null) {
+      shortRef = env.newObject("java.lang.Short");
+      ElementInfo ei = env.getModifiableElementInfo(shortRef);
+      ei.setShortField("value", read.shortValue());
+    }
+
+    return shortRef;
+  }
+}
+
+class BoxedIntCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int intRef = MJIEnv.NULL;
+
+    if (read != null) {
+      intRef = env.newObject("java.lang.Integer");
+      ElementInfo ei = env.getModifiableElementInfo(intRef);
+      ei.setIntField("value", read.intValue());
+    }
+
+    return intRef;
+  }
+}
+
+class BoxedLongCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int longRef = MJIEnv.NULL;
+
+    if (read != null) {
+      longRef = env.newObject("java.lang.Long");
+      ElementInfo ei = env.getModifiableElementInfo(longRef);
+      ei.setLongField("value", read.longValue());
+    }
+
+    return longRef;
+  }
+}
+
+class BoxedFloatCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int floatRef = MJIEnv.NULL;
+
+    if (read != null) {
+      floatRef = env.newObject("java.lang.Float");
+      ElementInfo ei = env.getModifiableElementInfo(floatRef);
+      ei.setFloatField("value", read.floatValue());
+    }
+
+    return floatRef;
+  }
+}
+
+class BoxedDoubleCreator implements Creator {
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    Double read = value.getDouble();
+    int doubleRef = MJIEnv.NULL;
+
+    if (read != null) {
+      doubleRef = env.newObject("java.lang.Double");
+      ElementInfo ei = env.getModifiableElementInfo(doubleRef);
+      ei.setDoubleField("value", read.doubleValue());
+    }
+
+    return doubleRef;
+  }
+}
+
+class StringCreator implements Creator {
+
+  @Override
+  public int create(MJIEnv env, String typeName, Value value) {
+    String strVal = value.getString();
+    int stringRef = MJIEnv.NULL;
+
+    if (strVal != null) {
+      stringRef = env.newString(strVal);
+    }
+
+    return stringRef;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/DoubleValue.java b/src/main/gov/nasa/jpf/util/json/DoubleValue.java
new file mode 100644 (file)
index 0000000..abe454e
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * Double value from JSON document
+ * @author Ivan Mushketik
+ */
+public class DoubleValue extends AbstractValue {
+
+  Double value;
+
+  public DoubleValue(String strValue) {
+    super(strValue);
+    value = Double.parseDouble(strValue);
+  }
+
+  @Override
+  public Double getDouble() {
+    return value;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/JSONLexer.java b/src/main/gov/nasa/jpf/util/json/JSONLexer.java
new file mode 100644 (file)
index 0000000..6fb860d
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPFException;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+/**
+ * Lexical analyzer that reads stream and return JSON tokens.
+ * @author Ivan Mushketik
+ */
+public class JSONLexer {
+
+  // JSON document reader
+  private Reader reader;
+  // number of symbol in text
+  int symbolNumber;
+  // number line
+  int lineNumber;
+  // number of symbol in line
+  int symbolNumberInLine;
+
+  // If parser backtracked to previous symbol
+  boolean backtracked;
+  // Last read character
+  int currentChar;
+
+  private final int STREAM_END = -1;
+
+  public JSONLexer(Reader reader) {
+    this.reader = reader;
+    backtracked = false;
+  }
+
+  public JSONLexer(String JSONStr) {
+    this(new StringReader(JSONStr));
+  }
+
+  /**
+   * Read next token from input stream.
+   * @return new read token
+   */
+  public Token getNextToken() {
+
+    int c;
+    // Skip whitespaces
+    do {
+      c = next();
+    } while(isSkipChar(c));
+
+    if (c == STREAM_END) {
+      return new Token(Token.Type.DocumentEnd, null);
+    }
+
+    if (c == '{') {
+      return new Token(Token.Type.ObjectStart, "{");
+    }
+
+    if (c == '}') {
+      return new Token(Token.Type.ObjectEnd, "}");
+    }
+
+    if (c == '[') {
+      return new Token(Token.Type.ArrayStart, "[");
+    }
+
+    if (c == ']') {
+      return new Token(Token.Type.ArrayEnd, "]");
+    }
+
+    if (c == ':') {
+      return new Token(Token.Type.KeyValueSeparator, ":");
+    }
+
+    if (c == ',') {
+      return new Token(Token.Type.Comma, ",");
+    }
+
+    if (c == '(') {
+      return new Token(Token.Type.CGCallParamsStart, "(");
+    }
+
+    if (c == ')') {
+      return new Token(Token.Type.CGCallParamsEnd, ")");
+    }
+
+    if (c == '\"' || c == '\'') {
+      return parseString(c);
+    }
+
+    if (Character.isDigit(c) || c == '-') {
+      back();
+      return parseNumber();
+    }
+
+    if (isIdentifierStartSymbol(c)) {
+      back();
+      return parseIdentifier();
+    }
+
+    // No sutable symbols found
+    error("Unexpected sybmol");
+    return null;
+  }
+
+  /**
+   * Method checks if parser has more input to read
+   * @return true if scanner has more tokens to read
+   */
+  public boolean hasMore() {
+    return currentChar != STREAM_END;
+  }
+
+  /**
+   * Read next symbol from input stream
+   * @return new read symbol
+   */
+  private int next() {
+    try {
+      if (backtracked) {
+        backtracked = false;
+        return currentChar;
+      }
+
+      currentChar = reader.read();
+      
+      symbolNumber++;
+      symbolNumberInLine++;
+      if (currentChar == '\n') {
+        lineNumber++;
+        symbolNumberInLine = 0;
+      }
+
+      return currentChar;
+    } catch (IOException ex) {
+      throw new JPFException("IOException during tokenizing JSON", ex);
+    }
+  }
+
+  /**
+   * Backtrack to previous symbol
+   */
+  private void back() {
+    if (backtracked) {
+      throw new JPFException("Tried to return twice. Posibly an error. Please report");
+    }
+    backtracked = true;
+  }
+
+  // Scaner doesn't backtrack before call this method
+  private Token parseString(int delimiter) {
+    StringBuilder result = new StringBuilder();
+    int c;
+
+    while((c = next()) != delimiter) {
+      if (c == '\\') {
+          result.append((char) readEscapedSymbol());
+      } else {
+         result.append((char) c);
+      }
+    }
+
+    return new Token(Token.Type.String, result.toString());
+  }
+
+  private int readEscapedSymbol() {
+    int escaped = next();
+
+    int res = -1;
+
+    switch(escaped) {
+      case '\"':
+      case '\\':
+      case '/':
+        res = escaped;
+        break;
+
+      case 'b':
+        res = '\b';
+        break;
+
+      case 'f':
+        res = '\f';
+        break;
+
+      case 'n':
+        res = '\n';
+        break;
+
+      case 'r':
+        res = '\r';
+        break;
+
+      case 't':
+        res = '\t';
+        break;
+
+      // Extract hexadecimal Unicode symbol (\\uXXXX)
+      case 'u': {
+        String r = "";
+        int i = 0;
+        int c;
+
+        while (hexadecimalChar(c = next()) && i < 4) {
+          r += (char) c;
+          i++;
+        }
+
+        // Unicode escape consists of 4 hexadecimal symbols
+        if (i < 4) {
+          error("Escaped Unicode symbol should consist of 4 hexadecimal digits");
+        }
+        
+        back();
+
+        res = Integer.parseInt(r, 16);
+      }
+      break;
+
+      default:
+        error("Illegal excape");
+        break;
+    }
+
+    return res;
+  }
+
+  private Token parseNumber() {    
+    StringBuilder sb = new StringBuilder();
+    int c = next();
+
+    // '-' symbol is not obligatory
+    if (c == '-') {
+      sb.append('-');
+    } else {
+      // We read unnecessary symbol, need to bactrack
+      back();
+    }
+
+    c = next();
+
+    // Integer part of digit is either '0' or '1'..'9' and digits
+    if (c == '0') {
+      sb.append('0');
+    } else {
+      back();
+      sb.append(readDigits());
+    }
+
+    c = next();
+
+    // "float part"
+    if (c == '.') {
+      sb.append('.');
+      sb.append(readDigits());
+    } else {
+      back();
+    }
+
+    c = next();
+
+    if (c == 'e' || c == 'E') {
+      sb.append((char) c);
+      c = next();
+      if (c == '+' || c == '-') {
+        sb.append((char) c);
+      } else {
+        back();
+      }
+
+      sb.append(readDigits());
+    } else {
+      back();
+    }
+
+    return new Token(Token.Type.Number, sb.toString());
+  }
+
+  /**
+   * Read at least one digit
+   * @return String that represents read number
+   */
+  private String readDigits() {
+    StringBuilder sb = new StringBuilder();
+    int c;
+    int n = 0;
+    while (Character.isDigit(c = next())) {
+      sb.append((char) c);
+      n++;
+    }
+
+    if (n == 0) {
+      error("Expected not empty sequence of digits");
+    }
+
+    back();
+    return sb.toString();
+  }
+
+  private Token parseIdentifier() {
+    StringBuilder result = new StringBuilder();
+
+    int c = next();
+
+    while (Character.isJavaIdentifierPart(c)) {
+      result.append((char) c);
+
+      c = next();
+    }
+
+    back();
+
+    return new Token(Token.Type.Identificator, result.toString());
+  }
+
+  private boolean isIdentifierStartSymbol(int c) {
+    return Character.isJavaIdentifierStart(c);
+  }
+
+  private boolean isSkipChar(int currentChar) {
+    return Character.isSpaceChar(currentChar);
+  }
+
+  private void error(String string) {
+    throw new JPFException(string + " '" + (char) currentChar + "' charCode = " + currentChar +
+                           "; in line " + lineNumber + " pos " + symbolNumberInLine);
+  }
+
+  private boolean hexadecimalChar(int i) {
+    return Character.isDigit(i) || (i <= 'F' && i >= 'A') || (i <= 'f' && i >= 'a');
+  }
+
+  int getLineNumber() {
+    return lineNumber;
+  }
+
+  int getCurrentPos() {
+    return symbolNumberInLine;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/JSONObject.java b/src/main/gov/nasa/jpf/util/json/JSONObject.java
new file mode 100644 (file)
index 0000000..9ba6975
--- /dev/null
@@ -0,0 +1,398 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.ObjectConverter;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClinitRequired;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.HashMap;
+import java.util.Set;
+
+/**
+ * Object parsed from JSON document.
+ * @author Ivan Mushketik
+ */
+public class JSONObject{
+
+  private static final JPFLogger logger = JPF.getLogger("gov.nasa.jpf.util.json.JSONObject");
+
+  private HashMap<String, Value> keyValues = new HashMap<String, Value>();
+  private HashMap<String, CGCall> cgCalls = new HashMap<String, CGCall>();
+
+  void addValue(String key, Value value) {
+    if (keyValues.containsKey(key)) {
+      throw new JPFException("Attempt to add two nodes with the same key in JSON object");
+    }
+
+    keyValues.put(key, value);
+  }
+
+  /**
+   * Get value read from JSON document with specified key.
+   * @param key - value's key.
+   * @return read value.
+   */
+  public Value getValue(String key) {
+    return keyValues.get(key);
+  }
+
+  public String[] getValuesKeys() {
+    Set<String> valuesKeys = keyValues.keySet();
+    String[] result = new String[keyValues.size()];
+
+    valuesKeys.toArray(result);
+    return result;
+  }
+
+  public void addCGCall(String key, CGCall cgCall) {
+    if (cgCalls.containsKey(key)) {
+      throw new JPFException("Attempt to add two CG with the same key in JSON object");
+    }
+
+    cgCalls.put(key, cgCall);
+  }
+
+  public CGCall getCGCall(String key) {
+    return cgCalls.get(key);
+  }
+
+  public String[] getCGCallsKeys() {
+    Set<String> cgKeys = cgCalls.keySet();
+    String[] result = new String[cgKeys.size()];
+
+    cgKeys.toArray(result);
+    return result;
+  }
+
+  /**
+   * check if all required ClassInfos for this object have been initialized so
+   * that the caller can decide if it has to re-execute before proceeding 
+   * 
+   * NOTE - this currently does not support concrete field types that are subtypes
+   * of the respective field types
+   */
+  public boolean requiresClinitExecution (ClassInfo ci, ThreadInfo ti){
+    while (ci != null){
+      if (ci.initializeClass(ti)){
+        return true;
+      }
+
+      for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
+        ClassInfo ciField = fi.getTypeClassInfo();
+        if (requiresClinitExecution(ciField, ti)){
+          return true;
+        }
+        if (ciField.isArray()){
+          ClassInfo ciComp = ciField.getComponentClassInfo();
+          if (requiresClinitExecution(ciComp, ti)) {
+            return true;
+          }
+        }
+      }
+      
+      ci = ci.getSuperClass();
+    }
+    
+    return false;
+  }
+  
+  //--- the fillers
+  
+  // NOTE - (pcm) before calling this method you have to make sure all required
+  // types are initialized
+  
+  public int fillObject (MJIEnv env, ClassInfo ci, ChoiceGenerator<?>[] cgs, String prefix) throws ClinitRequired {
+    int newObjRef = env.newObject(ci);
+    ElementInfo ei = env.getHeap().getModifiable(newObjRef);
+
+    // Fill all fields for this class until it has a super class
+    while (ci != null) {
+      FieldInfo[] fields = ci.getDeclaredInstanceFields();
+
+      for (FieldInfo fi : fields) {
+        String fieldName = fi.getName();
+        Value val = getValue(fieldName);
+        CGCall cgCall = getCGCall(fieldName);
+
+        // If a value was defined in JSON document
+        if (val != null) {
+          fillFromValue(fi, ei, val, env, cgs, prefix);
+          
+        } else if (cgCall != null) {
+          // Value of this field should be taken from CG
+          String cgId = prefix + fieldName;
+          ChoiceGenerator<?> cg = getCGByID(cgs, cgId);
+          assert cg != null : "Expected CG with id " + cgId;
+          
+          Object cgResult = cg.getNextChoice();
+
+          if (!fi.isReference()) {
+            convertPrimititve(ei, fi, cgResult);
+          } else {
+            int newFieldRef = ObjectConverter.JPFObjectFromJavaObject(env, cgResult);
+            ei.setReferenceField(fi, newFieldRef);
+          }
+        } else {
+          logger.warning("Value for field ", fi.getFullName(), " isn't specified");
+        }
+      }
+
+      ci = ci.getSuperClass();
+    }
+
+    return newObjRef;
+  }
+
+  private void fillFromValue(FieldInfo fi, ElementInfo ei, Value val, MJIEnv env, ChoiceGenerator<?>[] cgs, String prefix) {
+    String fieldName = fi.getName();
+    // Handle primitive types
+    if (!fi.isReference()) {
+      fillPrimitive(ei, fi, val);
+      
+    } else {
+      if (isArrayType(fi.getType())) {
+        int newArrRef = createArray(env, fi.getTypeClassInfo(), val, cgs, prefix + fieldName);
+        ei.setReferenceField(fi, newArrRef);
+
+      } else {
+        Creator creator = CreatorsFactory.getCreator(fi.getType());
+        if (creator != null) {
+          int newSubObjRef = creator.create(env, fi.getType(), val);
+          ei.setReferenceField(fi, newSubObjRef);
+          
+        } else {
+          // Not a special case. Fill it recursively
+          ClassInfo ciField = fi.getTypeClassInfo();
+          if (ciField.initializeClass(env.getThreadInfo())){
+            throw new ClinitRequired(ciField);
+          }
+          
+          JSONObject jsonObj = val.getObject();
+          int fieldRef = MJIEnv.NULL;
+          if (jsonObj != null) {
+            fieldRef = jsonObj.fillObject(env, ciField, cgs, prefix + fieldName);
+          }
+          ei.setReferenceField(fi.getName(), fieldRef);
+        }
+      }
+    }
+  }
+
+
+  private static void fillPrimitive(ElementInfo ei, FieldInfo fi, Value val) {
+    String primitiveName = fi.getType();
+
+    if (primitiveName.equals("boolean")) {
+      ei.setBooleanField(fi, val.getBoolean());
+
+    } else if (primitiveName.equals("byte")) {
+      ei.setByteField(fi, val.getDouble().byteValue());
+
+    } else if (primitiveName.equals("short")) {
+      ei.setShortField(fi, val.getDouble().shortValue());
+
+    } else if (primitiveName.equals("int")) {
+      ei.setIntField(fi, val.getDouble().intValue());
+
+    } else if (primitiveName.equals("long")) {
+      ei.setLongField(fi, val.getDouble().longValue());
+
+    } else if (primitiveName.equals("float")) {
+      ei.setFloatField(fi, val.getDouble().floatValue());
+
+    } else if (primitiveName.equals("double")) {
+      ei.setDoubleField(fi, val.getDouble());
+    }
+  }
+
+  public int createArray(MJIEnv env, ClassInfo ciArray, Value value, ChoiceGenerator<?>[] cgs, String prefix) {
+    Value vals[] = value.getArray();
+
+    ClassInfo ciElement = ciArray.getComponentClassInfo();
+    String arrayElementType = ciElement.getName();
+    int arrayRef;
+
+    // Handle arrays of primitive types
+    if (arrayElementType.equals("boolean")) {
+       arrayRef = env.newBooleanArray(vals.length);
+       ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+       boolean bools[] = arrayEI.asBooleanArray();
+
+       for (int i = 0; i < vals.length; i++) {
+        bools[i] = vals[i].getBoolean();
+      }
+    } else if (arrayElementType.equals("byte")) {
+       arrayRef = env.newByteArray(vals.length);
+       ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+       byte bytes[] = arrayEI.asByteArray();
+
+       for (int i = 0; i < vals.length; i++) {
+        bytes[i] = vals[i].getDouble().byteValue();
+      }
+    } else if (arrayElementType.equals("short")) {
+       arrayRef = env.newShortArray(vals.length);
+       ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+       short shorts[] = arrayEI.asShortArray();
+
+       for (int i = 0; i < vals.length; i++) {
+        shorts[i] = vals[i].getDouble().shortValue();
+      }
+    } else if (arrayElementType.equals("int")) {
+      arrayRef = env.newIntArray(vals.length);
+      ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+      int[] ints = arrayEI.asIntArray();
+
+      for (int i = 0; i < vals.length; i++) {
+        ints[i] = vals[i].getDouble().intValue();
+      }
+    } else if (arrayElementType.equals("long")) {
+      arrayRef = env.newLongArray(vals.length);
+      ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+      long[] longs = arrayEI.asLongArray();
+
+      for (int i = 0; i < vals.length; i++) {
+        longs[i] = vals[i].getDouble().longValue();
+      }
+    } else if (arrayElementType.equals("float")) {
+      arrayRef = env.newFloatArray(vals.length);
+      ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+      float[] floats = arrayEI.asFloatArray();
+
+      for (int i = 0; i < vals.length; i++) {
+        floats[i] = vals[i].getDouble().floatValue();
+      }
+    } else if (arrayElementType.equals("double")) {
+      arrayRef = env.newDoubleArray(vals.length);
+      ElementInfo arrayEI = env.getHeap().getModifiable(arrayRef);
+      double[] doubles = arrayEI.asDoubleArray();
+
+      for (int i = 0; i < vals.length; i++) {
+        doubles[i] = vals[i].getDouble();
+      }
+    } else {
+      // Not an array of primitive types
+      arrayRef = env.newObjectArray(arrayElementType, vals.length);
+      ElementInfo arrayEI = env.getModifiableElementInfo(arrayRef);
+
+      Fields fields = arrayEI.getFields();
+
+      Creator creator = CreatorsFactory.getCreator(arrayElementType);
+      for (int i = 0; i < vals.length; i++) {
+
+        int newObjRef;
+        if (creator != null) {
+          newObjRef = creator.create(env, arrayElementType, vals[i]);
+        } else{
+          if (isArrayType(arrayElementType)) {
+            newObjRef = createArray(env, ciElement, vals[i], cgs, prefix + "[" + i);
+          } else {
+            JSONObject jsonObj = vals[i].getObject();
+            if (jsonObj != null) {
+              newObjRef = jsonObj.fillObject(env, ciElement, cgs, prefix + "[" + i);
+            } else {
+              newObjRef = MJIEnv.NULL;
+            }
+          }
+        }
+
+        fields.setReferenceValue(i, newObjRef);
+      }
+    }
+
+    return arrayRef;
+  }
+
+
+  private boolean isArrayType(String typeName) {
+    return typeName.lastIndexOf('[') >= 0;
+  }
+
+  /**
+   * This is method is used to set field of primitive type from CG result object
+   * @param ei - ElementInfo to set field in
+   * @param fi - FieldInfo of a field we want to set
+   * @param cgResult - result of CG call
+   */
+  private void convertPrimititve(ElementInfo ei, FieldInfo fi, Object cgResult) {
+    String primitiveName = fi.getType();
+
+    if (primitiveName.equals("boolean") && cgResult instanceof Boolean) {
+      Boolean bool = (Boolean) cgResult;
+      ei.setBooleanField(fi, bool.booleanValue());
+    } else if (cgResult instanceof Number) {
+      Number number = (Number) cgResult;
+
+      if (primitiveName.equals("byte")) {
+        ei.setByteField(fi, number.byteValue());
+
+      } else if (primitiveName.equals("short")) {
+        ei.setShortField(fi, number.shortValue());
+
+      } else if (primitiveName.equals("int")) {
+        ei.setIntField(fi, number.intValue());
+
+      } else if (primitiveName.equals("long")) {
+        ei.setLongField(fi, number.longValue());
+
+      } else if (primitiveName.equals("float")) {
+        ei.setFloatField(fi, number.floatValue());
+
+      } else if (primitiveName.equals("double")) {
+        ei.setDoubleField(fi, number.doubleValue());
+      }
+    } else if (cgResult instanceof Character) {
+      Character c = (Character) cgResult;
+      ei.setCharField(fi, c);
+      
+    } else {
+      throw new JPFException("Can't convert " + cgResult.getClass().getCanonicalName() +
+                             " to " + primitiveName);
+    }
+  }
+
+  /**
+   * Get CG from current state CG list by it's ID
+   * @param cgs - array of CG from current state
+   * @param id - id of the CG that we search for
+   * @return - CG with a specified id or null if no id with such name found
+   */
+  private ChoiceGenerator<?> getCGByID(ChoiceGenerator<?>[] cgs, String id) {
+    if (cgs == null) {
+      return null;
+    }
+    
+    for (int i = 0; i < cgs.length; i++) {
+      if (cgs[i].getId().equals(id)) {
+        return cgs[i];
+      }
+    }
+
+    return null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/JSONObjectValue.java b/src/main/gov/nasa/jpf/util/json/JSONObjectValue.java
new file mode 100644 (file)
index 0000000..3b454a6
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+class JSONObjectValue implements Value {
+
+  JSONObject object;
+
+  public JSONObjectValue(JSONObject parseObject) {
+    object = parseObject;
+  }
+
+  @Override
+  public JSONObject getObject() {
+    return object;
+  }
+
+  @Override
+  public String getString() {
+    throw new JPFException("Can't convert JSON object to String");
+  }
+
+  @Override
+  public Double getDouble() {
+    throw new JPFException("Can't convert JSON object to Double");
+  }
+
+  @Override
+  public Value[] getArray() {
+    throw new JPFException("Can't convert JSON object to Array");
+  }
+
+  @Override
+  public Boolean getBoolean() {
+    throw new JPFException("Can't convert JSON object to Boolean");
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/JSONParser.java b/src/main/gov/nasa/jpf/util/json/JSONParser.java
new file mode 100644 (file)
index 0000000..191306e
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * JSON parser. Read tokenized stream from JSONTokenizer and returns root JSON
+ * node.
+ * Parser read extended JSON grammar (http://json.org).
+ * Standard grammar was extended by ability to set Choice Generator call as a
+ * value in JSON object.
+ * @author Ivan Mushketik
+ */
+public class JSONParser {
+
+  JSONLexer lexer;
+  // Last token returned by lexer
+  Token lastReadToken;
+  
+  Token prevReadToken;
+  // true if parser bactracked to previous token
+  int backtrack;
+
+  public JSONParser(JSONLexer lexer) {
+    this.lexer = lexer;
+  }
+
+  /**
+   * Parse JSON document
+   * @return root node of JSON tree.
+   */
+  public JSONObject parse() {
+    return parseObject();
+  }
+
+  /**
+   * Read next token from lexer output stream. If parser backtraced return previously
+   * read token
+   * @return
+   */
+  private Token next() {
+    if (lastReadToken != null && lastReadToken.getType() == Token.Type.DocumentEnd) {
+      return lastReadToken;
+    }
+
+    if (backtrack == 1) {
+      backtrack--;
+      return lastReadToken;
+    }
+
+    if (backtrack == 2) {
+      backtrack--;
+      return prevReadToken;
+    }
+
+    prevReadToken = lastReadToken;
+    lastReadToken = lexer.getNextToken();
+
+    return lastReadToken;
+  }
+
+  /**
+   * Backtrack to previous token
+   */
+  private void back() {
+    if (backtrack == 2) {
+      throw new JPFException("Attempt to bactrack three times. Posibly an error. Please report");
+    }
+
+    if (lastReadToken == null) {
+      throw new JPFException("Attempt to backtrack before starting to read token stream. Please report");
+    }
+
+    if (backtrack == 1 && prevReadToken == null) {
+      throw new JPFException("Attempt to backtrack twice when less then two tokens read. Please report");
+    }
+
+    backtrack++;
+  }
+
+  /**
+   * Read next token and check it's type. If type is wrong method throws exception
+   * else it returns read token
+   * @param type - type of the following token.
+   * @return read token if it has correct type
+   */
+  private Token consume(Token.Type type) {
+    Token t = next();
+
+    if (t.getType() != type) {
+      error("Unexpected token '" + t.getValue() + "' expected " + type);
+    }
+
+    return t;
+  }
+
+  /**
+   * Parse JSON object
+   * @return
+   */
+  private JSONObject parseObject() {
+    JSONObject pn = new JSONObject();
+    consume(Token.Type.ObjectStart);  
+    Token t = next();
+
+    // Check if object is empty
+    if (t.getType() != Token.Type.ObjectEnd) {
+      back();
+      while (true) {
+        Token key = consume(Token.Type.String);
+        consume(Token.Type.KeyValueSeparator);
+        
+
+        Token posibleId = next();
+        t = next();
+
+        if (posibleId.getType() == Token.Type.Identificator &&
+            t.getType() == Token.Type.CGCallParamsStart) {
+            CGCall cg = parseCGCall(posibleId.getValue());
+            pn.addCGCall(key.getValue(), cg);
+        } else {
+          back();
+          back();
+          Value v = parseValue();
+          pn.addValue(key.getValue(), v);
+        }
+
+        t = next();
+        // If next token is comma there is one more key-value pair to read
+        if (t.getType() != Token.Type.Comma) {
+          back();
+          break;
+        }
+      }
+      consume(Token.Type.ObjectEnd);
+    }
+    return pn;
+  }
+
+  /**
+   * Parse array of JSON objects
+   * @return parsed array of JSON objects
+   */
+  private ArrayValue parseArray() {
+    consume(Token.Type.ArrayStart);
+    ArrayValue arrayValue = new ArrayValue();
+    Token t = next();
+    if (t.getType() != Token.Type.ArrayEnd) {
+      back();
+      while (true) {
+        Value val = parseValue();
+        arrayValue.addValue(val);
+
+        t = next();
+        // If next token is comma there is one more object to parse
+        if (t.getType() != Token.Type.Comma) {
+          back();
+          break;
+        }
+      }
+    } else {
+      back();
+    }
+    consume(Token.Type.ArrayEnd);
+    
+    return arrayValue;
+  }
+
+  /**
+   * Parse identifier. Identifier can be "null", "true" or "false"
+   * @return appropriate value object
+   */
+  private Value parseIdentificator() {
+    Token id = consume(Token.Type.Identificator);
+
+    String val = id.getValue();
+    if (val.equals("true")) {
+      return new BooleanValue(true, "true");
+
+    } else if (val.equals("false")) {
+      return new BooleanValue(false, "false");
+      
+    } else if (val.equals("null")) {
+      return new NullValue();
+    }
+
+    error("Unknown identifier");
+    return null;
+  }
+
+  private void error(String string) {
+    throw new JPFException(string + "(" + lexer.getLineNumber() + ":" + lexer.getCurrentPos() + ")");
+  }
+
+  private Value parseValue() {
+    Token t = next();
+    switch (t.getType()) {
+      case Number:
+        return new DoubleValue(t.getValue());
+        
+      case String:
+        return new StringValue(t.getValue());
+        
+      case ArrayStart:
+        back();
+        return parseArray();
+        
+      case ObjectStart:
+        back();
+        return new JSONObjectValue(parseObject());
+        
+      case Identificator:
+        back();
+        return parseIdentificator();
+        
+      default:
+        error("Unexpected token '" + t.getValue() + "' during parsing JSON value");
+        return null;
+    }
+    
+  }
+
+  /**
+   * Parse Choice Generator call
+   * @param cgName - name of called Choice Generator.
+   * @return parsed object with info about Choice Generator call
+   */
+  private CGCall parseCGCall(String cgName) {
+    
+    CGCall parsedCG = new CGCall(cgName);
+    Token t = next();
+
+    if (t.getType() != Token.Type.CGCallParamsEnd) {
+      back();
+      while (true) {
+        Value v = parseValue();
+        parsedCG.addParam(v);
+
+        t = next();
+        if (t.getType() == Token.Type.CGCallParamsEnd) {
+          back();
+          break;
+        }
+        back();
+        consume(Token.Type.Comma);
+      }
+    } else {
+      back();
+    }
+
+    consume(Token.Type.CGCallParamsEnd);
+
+    return parsedCG;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/NullValue.java b/src/main/gov/nasa/jpf/util/json/NullValue.java
new file mode 100644 (file)
index 0000000..6535d05
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * Null value from JSON document
+ * @author Ivan Mushketik
+ */
+class NullValue implements Value {
+
+  public NullValue() {
+  }
+
+  @Override
+  public String getString() {
+    return null;
+  }
+
+  @Override
+  public Double getDouble() {
+    return null;
+  }
+
+  @Override
+  public Boolean getBoolean() {
+    return null;
+  }
+
+  @Override
+  public JSONObject getObject() {
+    return null;
+  }
+
+  @Override
+  public Value[] getArray() {
+    return null;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/json/StringValue.java b/src/main/gov/nasa/jpf/util/json/StringValue.java
new file mode 100644 (file)
index 0000000..21eb98b
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * String value from JSON document
+ * @author Ivan Mushketik
+ */
+public class StringValue extends  AbstractValue {
+
+  String value;
+
+  public StringValue(String str) {
+    super(str);
+    value = str;
+  }
+
+  @Override
+  public String getString() {
+    return value;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/json/Token.java b/src/main/gov/nasa/jpf/util/json/Token.java
new file mode 100644 (file)
index 0000000..6bad431
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * Token that is generated by JSONLexer.
+ * @author Ivan Mushketik
+ */
+public class Token {
+
+  // Types of JSON tokens
+  public enum Type {
+    DocumentEnd,
+    ObjectStart, ObjectEnd,
+    ArrayStart, ArrayEnd,
+    CGCallParamsStart, CGCallParamsEnd,
+    Comma,
+    KeyValueSeparator,
+    Number,
+    String,
+    Identificator};
+
+  private Type type;
+  // Representation of token in input stream
+  private String value;
+
+  public Token(Type type, String value) {
+    this.type = type;
+    this.value = value;
+  }
+
+  public Type getType() {
+    return type;
+  }
+
+  public String getValue() {
+    return value;
+  }
+
+  @Override
+  public boolean equals(Object obj) {
+    if (obj instanceof Token) {
+      Token token = (Token) obj;
+
+      if (token.type != this.type) {
+        return false;
+      }
+      if (token.value == null && this.value == null) {
+        return true;
+      }
+      if (token.value != null && this.value != null) {
+        return token.value.equals(this.value);
+      }
+
+      return false;
+    }
+
+    return false;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+
+    sb.append("Token (");
+    sb.append(type);
+    sb.append(", '");
+    sb.append(value);
+    sb.append("')");
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/json/Value.java b/src/main/gov/nasa/jpf/util/json/Value.java
new file mode 100644 (file)
index 0000000..a9121f5
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+/**
+ * Value that was read from JSON document. ("key" : value).
+ * Value class has methods to return string, double or boolean value.
+ * Derived class should throw exceptions if they can't convert value read from
+ * JSON document to the requested one.
+ * @author Ivan Mushketik
+ */
+public interface  Value {
+  /**
+   * Get string value.
+   * @return string value read from JSON document
+   */
+  public String getString();
+
+  /**
+   * Get double value.
+   * @return double value read from JSON document
+   */
+  public Double getDouble();
+
+  /**
+   * Get JSON object.
+   * @return JSON object value read from JSON document
+   */
+  public JSONObject getObject();
+
+  /**
+   * Get array value.
+   * @return array value read from JSON document
+   */
+  public Value[] getArray();
+
+  /**
+   * Get boolean value.
+   * @return boolean value read from JSON document.
+   */
+  public Boolean getBoolean();
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/Alternative.java b/src/main/gov/nasa/jpf/util/script/Alternative.java
new file mode 100644 (file)
index 0000000..03bfc25
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+/**
+ * ScriptElement that represents an alternative between choices. At runtime,
+ * this usually gets translated into a ChoiceGenerator instance, to specify
+ * an event context that should facilitate state space exploration
+ */
+public class Alternative extends ScriptElementContainer {
+
+  Alternative (ScriptElement parent, int line) {
+    super(parent, line);
+  }
+
+  @Override
+  public String toString() {
+    return toString("ANY");
+  }
+
+  @Override
+  public void process (ElementProcessor p) {
+    p.process(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/ESParser.java b/src/main/gov/nasa/jpf/util/script/ESParser.java
new file mode 100644 (file)
index 0000000..4a62a93
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.Reader;
+import java.io.StreamTokenizer;
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * generic parser for event scripts
+ *
+ * <2do> this is still awfully hardwired to StringExpander
+ */
+
+public class ESParser {
+
+  /**** our keywords ****/
+  final public static String K_REPEAT = "REPEAT";
+  final public static String K_ANY = "ANY";
+  final public static String K_SECTION = "SECTION";
+
+  String file;
+  StreamTokenizer scanner;
+
+  boolean done = false;
+
+  EventFactory eventFactory;
+
+  /******************* utilities *****************************************/
+
+  public class Exception extends java.lang.Exception {
+    Exception(String details) {
+      super("parse error: " + details + ", found: " + scanner);
+    }
+    Exception(String msg, String param) {
+      super(msg + ' ' + param);
+    }
+  }
+
+  public static class DefaultEventFactory implements EventFactory {
+    @Override
+       public Event createEvent (ScriptElement parent, String id, List<String> args, int line) {
+      return new Event(parent, id, args.toArray(new String[args.size()]), line);
+    }
+  }
+
+  public ESParser (String fname, EventFactory eFact) throws Exception {
+
+    eventFactory = eFact != null ? eFact : new DefaultEventFactory();
+
+    try {
+      file = fname;
+      FileReader r = new FileReader(fname);
+
+      scanner = createScanner(r);
+      scanner.nextToken(); // 1 symbol lookahead
+
+    } catch (FileNotFoundException fnfx) {
+      throw new Exception("file not found:", fname);
+    } catch (IOException iox) {
+      throw new Exception("error reading: ", fname);
+    }
+  }
+
+  public ESParser (String fname) throws Exception {
+    this(fname, new DefaultEventFactory());
+  }
+
+  public ESParser (String name, Reader r) throws Exception {
+    this(name, r, new DefaultEventFactory());
+  }
+
+  public ESParser (String name, Reader r, EventFactory eFact) throws Exception {
+    eventFactory = eFact;
+
+    try {
+      file = name;
+
+      scanner = createScanner(r);
+      scanner.nextToken(); // 1 symbol lookahead
+
+    } catch (IOException iox) {
+      throw new Exception("error reading: ", name);
+    }
+  }
+
+  StreamTokenizer createScanner (Reader r) {
+    StreamTokenizer s = new StreamTokenizer(r);
+
+    // disable number parsing, since it doesn't work in the context of string expansion
+    // and we also would have to preserve the number type (int or double)
+    s.ordinaryChars('0','9');
+    s.wordChars('0','9');
+    //s.wordChars('"', '"');
+
+    // those are used to expand events
+    s.wordChars('[','[');
+    s.wordChars(']',']');
+    s.wordChars('|','|');
+    s.wordChars('-','-');
+    s.wordChars('<','<');
+    s.wordChars('>','>');
+
+    // those can be part of Event IDs
+    s.wordChars('_','_');
+    s.wordChars('#', '#');
+    s.wordChars('*','*');
+    s.wordChars('@','@');
+    s.wordChars('$','$');
+    s.wordChars(':',':');
+    s.wordChars('~','~');
+    s.wordChars('!', '!');
+
+    s.quoteChar('"');
+
+    s.slashSlashComments(true);
+    s.slashStarComments(true);
+
+    s.whitespaceChars(',', ',');
+    s.whitespaceChars(';', ';');
+
+    return s;
+  }
+
+  void nextToken() throws Exception {
+    try {
+      if (scanner.nextToken() == StreamTokenizer.TT_EOF) {
+        done = true;
+      }
+    } catch (IOException iox) {
+      throw new Exception("could not read input", iox.toString());
+    }
+  }
+
+  void match (char c) throws Exception {
+    if (scanner.ttype == /*(int)*/c) {
+      nextToken();
+    } else {
+      throw new Exception("char '" + c + "' expected");
+    }
+  }
+
+  boolean isMatch (char c) throws Exception {
+    if (scanner.ttype == c) {
+      nextToken();
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  boolean isMatch (String word) throws Exception {
+    if (scanner.ttype == StreamTokenizer.TT_WORD) {
+      if (scanner.sval.equals(word)) {
+        nextToken();
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  String matchKeyword (String key) throws Exception {
+    String s = matchWord();
+    if (!s.equals(key)) {
+      throw new Exception("expected keyword: " + key);
+    }
+    return s;
+  }
+
+  String matchWord () throws Exception {
+    if (scanner.ttype == StreamTokenizer.TT_WORD) {
+      String s = scanner.sval;
+      nextToken();
+      return s;
+    } else {
+      throw new Exception("id or keyword expected");
+    }
+  }
+
+  boolean isInt (String s) {
+    int c = s.charAt(0);
+    return ((c >='0') && (c <= '9'));
+  }
+
+  int isIntMatch(int defaultValue) throws Exception {
+    if ((scanner.ttype == StreamTokenizer.TT_WORD) && isInt(scanner.sval)) {
+      int n = Integer.parseInt(scanner.sval);
+      nextToken();
+      return n;
+    } else {
+      return defaultValue;
+    }
+  }
+
+  int matchIntNumber () throws Exception {
+    // Ok, this isn't too effective since our scanner doesn't parse numbers
+    if ((scanner.ttype == StreamTokenizer.TT_WORD) && isInt(scanner.sval)) {
+      int n = Integer.parseInt(scanner.sval);
+      nextToken();
+      return n;
+    } else {
+      throw new Exception("number expected");
+    }
+  }
+
+  /******************* the recursive descend parser *********************/
+
+  /************************************** grammar ***********************
+  script ::= {section | sequence}.
+  section ::= 'SECTION' ID {',' ID} '{' {sequence} '}'.
+  sequence ::= iteration | selection | event.
+  iteration ::= 'REPEAT' [NUM] '{' {sequence} '}'.
+  selection ::= 'ANY' '{' {event} '}'.
+  event ::= ID ['(' [parameter {',' parameter}] ')'].
+  parameter ::= STRING
+
+  all parameters are treated as strings, but string literals preserve the double quotes
+  (we need to preserve the token because of argument expansion and numeric type)
+
+  the event ID can contain '#', '.', ':', '@', '$' '/' and '*' chars (e.g. for further
+  parsing of targets etc.)
+
+
+  ***********************************************************************/
+
+
+  public Script parse() throws Exception {
+    Script s = new Script();
+
+    while (!done) {
+      if (isMatch(K_SECTION)) {
+        section(s);
+      } else {
+        sequence(s);
+      }
+    }
+
+    return s;
+  }
+
+  protected void section (ScriptElementContainer parent) throws Exception {
+    //matchKeyword(K_SECTION);
+    ArrayList<String> ids = new ArrayList<String>();
+
+    String id = matchWord();
+    ids.add(id);
+
+    while (isMatch(',')) {
+      id = matchWord();
+      ids.add(id);
+    }
+
+    Section sec = new Section(parent, ids, scanner.lineno());
+    parent.add(sec);
+
+    match('{');
+    while (!done && (scanner.ttype != '}')) {
+      sequence(sec);
+    }
+    match('}');
+
+
+  }
+
+  protected void sequence (ScriptElementContainer parent) throws Exception {
+    if (isMatch(K_REPEAT)) {
+      repetition(parent);
+    } else if (isMatch(K_ANY)) {
+        alternative(parent);
+    } else {
+      if (scanner.ttype == StreamTokenizer.TT_WORD) {
+        event(parent);
+      } else {
+        if (scanner.ttype == StreamTokenizer.TT_EOF){
+          done = true; // empty sequence
+        } else {
+          throw new Exception("repetition, alternative or event expected");
+        }
+      }
+    }
+  }
+
+
+  protected void repetition (ScriptElementContainer parent) throws Exception {
+    //matchKeyword(K_REPEAT);
+    int n = isIntMatch(-1); // default is unbounded
+
+    Repetition r = new Repetition(parent, n, scanner.lineno());
+    parent.add(r);
+
+    match('{');
+    while (!done && (scanner.ttype != '}')) {
+      sequence(r);
+    }
+    match('}');
+  }
+
+
+  protected void alternative (ScriptElementContainer parent) throws Exception {
+    //matchKeyword(K_ANY);
+
+    Alternative a = new Alternative(parent, scanner.lineno());
+    parent.add(a);
+
+    match('{');
+    while (!done && (scanner.ttype != '}')) {
+      event(a);
+    }
+    match('}');
+  }
+
+
+  protected void event (ScriptElementContainer parent) throws Exception {
+    String id = matchWord();
+
+    ArrayList<String> args = new ArrayList<String>();
+    if (isMatch('(')) {
+      while (!isMatch(')')) {
+
+        if (scanner.ttype == StreamTokenizer.TT_WORD) {
+          args.add(scanner.sval);
+        } else if (scanner.ttype == '"'){ // string literal
+          args.add( "\"" + scanner.sval + '"');
+        }
+
+        nextToken();
+      }
+    }
+
+    Event e = eventFactory.createEvent(parent, id, args, scanner.lineno());
+    parent.add(e);
+  }
+
+  /********** test functions ************/
+
+
+  public static void tokenize (String fname) {
+    try {
+      ESParser parser = new ESParser(fname);
+      StreamTokenizer s = parser.scanner;
+
+      while (s.ttype != StreamTokenizer.TT_EOF) {
+        switch (s.ttype) {
+          case StreamTokenizer.TT_WORD:
+            System.out.println("WORD: " + s.sval);
+            break;
+
+          case StreamTokenizer.TT_NUMBER:
+            System.out.println("NUM:  " + s.nval);
+            break;
+
+          default:
+            char c = (char)s.ttype;
+            if (c == '"'){ // string literal
+              System.out.println("STRING: \"" + s.sval + '"');
+            } else {
+              System.out.println("CHAR: " + (char)s.ttype);
+            }
+        }
+        s.nextToken();
+      }
+    } catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  static void showScript (String fname){
+    try {
+      ESParser parser = new ESParser(fname);
+      Script script = parser.parse();
+
+      PrintWriter pw = new PrintWriter(System.out, true);
+      pw.println("------------------ script AST:");
+      script.dump(pw);
+
+      pw.println("------------------ generated CG sequence:");
+
+      StringSetGenerator p = new StringSetGenerator();
+      script.process(p);
+
+      LinkedHashMap<String,ArrayList<CG>> sections = p.getSections();
+      for (Map.Entry<String,ArrayList<CG>> e : sections.entrySet()) {
+        ArrayList<CG> queue = e.getValue();
+        System.out.println(e.getKey() + " {");
+        for (CG cg : queue) {
+          System.out.print("  ");
+          System.out.println(cg);
+        }
+        System.out.println("}");
+        System.out.println();
+      }
+
+ /** this only shows the last section
+      List<CG> queue = p.getCGQueue();
+      for (CG cg : queue) {
+        System.out.println(cg);
+      }
+**/
+
+    } catch (Throwable t){
+      t.printStackTrace();
+    }
+
+  }
+
+  public static void main(String[] args) {
+    //tokenize(args[0]);
+    showScript(args[0]);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/script/ElementProcessor.java b/src/main/gov/nasa/jpf/util/script/ElementProcessor.java
new file mode 100644 (file)
index 0000000..abdf270
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+public interface ElementProcessor {
+  void process (Section s);
+  void process (Event e);
+  void process (Alternative a);
+  void process (Repetition r);
+}
diff --git a/src/main/gov/nasa/jpf/util/script/Event.java b/src/main/gov/nasa/jpf/util/script/Event.java
new file mode 100644 (file)
index 0000000..fd6218a
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+
+import gov.nasa.jpf.util.StringExpander;
+import java.util.ArrayList;
+import java.util.List;
+
+public class Event extends ScriptElement {
+
+  public static final String NONE = "NONE";
+
+  protected String id;
+  protected String[] arguments;
+
+
+  public Event(ScriptElement parent, String id, String[] args, int line) {
+    super(parent, line);
+    this.id = id;
+
+    if ((args != null) && (args.length > 0)){
+      arguments = args.clone();
+    }
+  }
+
+  public boolean isNone() {
+    return (NONE.equals(id));
+  }
+
+  public static boolean isNone (String id) {
+    return (NONE.equals(id));
+  }
+
+  public String getId() {
+    return id;
+  }
+
+  @Override
+  public int getLine() {
+    return line;
+  }
+
+  @Override
+  public String toString() {
+    if (arguments == null) {
+      return id;
+    } else {
+      StringBuilder sb = new StringBuilder(id);
+
+      sb.append('(');
+      for (int i=0; i<arguments.length; i++) {
+        if (i > 0) {
+          sb.append(',');
+        }
+        sb.append(arguments[i]);
+      }
+      sb.append(')');
+
+      return sb.toString();
+    }
+  }
+
+  public String[] getArguments() {
+    return arguments;
+  }
+
+  public void setArguments (String[] args) {
+    arguments = args;
+  }
+
+  @Override
+  public void process (ElementProcessor p) {
+    p.process(this);
+  }
+
+  String[] expandArgument (String a) {
+    ArrayList<String> list = new ArrayList<String>();
+
+    StringExpander ex = new StringExpander(a);
+    List<String> l = ex.expand();
+    list.addAll(l);
+
+    return list.toArray(new String[list.size()]);
+  }
+
+  /**
+   * this is an interesting little exercise since we have to cover all
+   * combinations of parameter values, which would normally be a simple set
+   * of nested loops, only that the number of parameters is a variable itself
+   */
+  public List<Event> expand () {
+    StringExpander ex = new StringExpander(id);
+    List<String> ids = ex.expand();
+    ArrayList<Event> list = new ArrayList<Event>();
+
+    if (arguments != null) {
+      String[] a = new String[arguments.length];
+      String[][] args = new String[arguments.length][];
+      int[] argIdx = new int[args.length];
+
+      for (int i=0; i<args.length; i++) {
+        args[i] = expandArgument(arguments[i]);
+      }
+
+      int n = args.length-1;
+
+      for (String id : ids) {
+        int i;
+        for (i=0; i<=n; i++) { // reset arg indices
+          argIdx[i] = 0;
+        }
+
+        for (i=n; ;) {
+          if (argIdx[i] >= args[i].length){ // all choices at this level exhausted
+            // increment next lower level(s), reset level(s) above
+            int l;
+            for (l=i-1; l >= 0; l--) {
+              argIdx[l]++;
+              argIdx[l+1] = 0;
+              if (argIdx[l] < args[l].length) {
+                break;
+              }
+            }
+            if (l < 0) {
+              break; // done, do next id
+            } else {
+              i = n; // restart from top level
+            }
+
+          } else { // got a new combination
+            for (int k=0; k<args.length; k++) {
+              a[k] = args[k][argIdx[k]];
+            }
+            Event ee = new Event(parent, id, a, line);
+            list.add(ee);
+            argIdx[i]++;
+          }
+        }
+      }
+
+    } else { // no parameter variation, but we still might have expanded ids
+      if (ids.size() == 1) {
+        list.add(this);
+      } else {
+        for (String id : ids) {
+          list.add( new Event(parent, id, arguments, line));
+        }
+      }
+    }
+
+    return list;
+  }
+
+  public Object[] getConcreteArguments () {
+    if (arguments == null) {
+      return null;
+    }
+    if (arguments.length == 0) {
+      return new Object[0];
+    }
+
+    Object[] a = new Object[arguments.length];
+    for (int i=0; i<arguments.length; i++) {
+      a[i] = getConcreteArgument(arguments[i]);
+    }
+
+    return a;
+  }
+
+  Object getConcreteArgument (String s) {
+    char c = s.charAt(0);
+
+    if (c == '"' || c == '\'') { // String literal
+      return s.substring(1,s.length()-1);
+
+    } else if (Character.isDigit(c) || c == '-' || c == '+') { // ints and doubles
+      if (s.indexOf('.') >=0) {
+        return Double.parseDouble(s);
+      } else {
+        return Integer.parseInt(s);
+      }
+      
+    } else if (s.equals("true")) { // boolean
+      return Boolean.TRUE;
+    } else if (s.equals("false")) {
+      return Boolean.FALSE;
+      
+    } else if (c == '@'){ // variable
+      return s;
+      
+    } else { // not supported
+      throw new IllegalArgumentException("unsupported event argument type of value=" + s);
+    }
+  }
+
+  /**
+   * variations over boolean lists are quite easy to produce :)
+   */
+  public static Object[][] getBooleanArgVariations (int nArgs) {
+    int n = 1<<nArgs;
+    Object[][] args = new Object[n][];
+
+    for (int i=0; i<n; i++) {
+      args[i] = new Boolean[nArgs];
+      for (int j=0; j<nArgs; j++) {
+        args[i][j] = ((i & (1<<j)) != 0) ? Boolean.TRUE : Boolean.FALSE;
+      }
+    }
+
+    return args;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/EventFactory.java b/src/main/gov/nasa/jpf/util/script/EventFactory.java
new file mode 100644 (file)
index 0000000..e5d9577
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import java.util.List;
+
+/**
+ * factory class used to create concrete Event instances
+ */
+public interface EventFactory {
+
+  Event createEvent(ScriptElement parent, String id, List<String> args, int line);
+}
diff --git a/src/main/gov/nasa/jpf/util/script/EventGenerator.java b/src/main/gov/nasa/jpf/util/script/EventGenerator.java
new file mode 100644 (file)
index 0000000..a4e2faa
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+
+/**
+ * abstract ChoiceGenerator root for Event based generators
+ */
+public abstract class EventGenerator<T> extends ChoiceGeneratorBase<T> {
+
+  protected EventGenerator (String id){
+    super(id);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/script/EventGeneratorFactory.java b/src/main/gov/nasa/jpf/util/script/EventGeneratorFactory.java
new file mode 100644 (file)
index 0000000..d44ffd6
--- /dev/null
@@ -0,0 +1,308 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.DynamicObjectArray;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.SystemState;
+
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+/**
+ * abstract root for backtrackable event generator factories
+ *
+ * <2do> - we don't support backtracking for sections yet! needs to be implemented for
+ * state charts
+ */
+public abstract class EventGeneratorFactory extends ListenerAdapter
+                                         implements ElementProcessor, Iterable<EventGenerator> {
+
+  static final String DEFAULT = "default";
+
+  // helper class to store our internal state. For a simple script based system,
+  // storing the 'cur' index (into the queue) would do, but the queue might have been
+  // generated dynamically, so we need some container to store both
+  static class Memento {
+    ArrayList<EventGenerator> queue;
+    int cur; // cursor into queue
+
+    Memento (EventGeneratorFactory fact) {
+      queue = fact.queue;
+      cur = fact.cur;
+    }
+
+    void restore (EventGeneratorFactory fact) {
+      fact.queue = queue;
+      fact.cur = cur;
+    }
+  }
+
+
+  // <2do> this is lame - if we really want 'instructions' in the queue, rather
+  // than data (== CGs), then we should have a queue of general EventOp entries
+  // this is only used for unbounded REPEATs so far
+  // <2do> the nextCG is currently unconditionally reset in getNextChoiceGenerator()
+  // so we have to make sure we don't jump back if the jump target state was already
+  // visited, which we have to store in the Jump
+  static class Loop extends EventGenerator {
+
+    int startPos, endPos;
+
+    Loop (String id, int startPos,  int endPos){
+      super(id);
+      
+      this.startPos = startPos;
+      this.endPos = endPos;
+    }
+
+    int getStartPos() {
+      return startPos;
+    }
+
+    //--- those are all dummies - this isn't really a choice
+    @Override
+       public void advance() {}
+
+    @Override
+       public Class getChoiceType() {
+      return null;
+    }
+
+    @Override
+       public Object getNextChoice() {
+      return null;
+    }
+
+    @Override
+       public int getProcessedNumberOfChoices() {
+      return 0;
+    }
+
+    @Override
+       public int getTotalNumberOfChoices() {
+      return 0;
+    }
+
+    @Override
+       public boolean hasMoreChoices() {
+      return false;
+    }
+
+    @Override
+       public ChoiceGenerator randomize() {
+      return null;
+    }
+
+    @Override
+       public void reset() {}
+
+  }
+
+  /** the last returned position in the generator stream */
+  protected int cur;
+
+  /** this is where we store the cur positions for backtracking and restoring states */
+  DynamicObjectArray<Memento> states;
+
+  protected String scriptFileName;
+  protected Script script;
+  protected Config conf;
+
+  protected LinkedHashMap<String,ArrayList<EventGenerator>> sections;
+  protected ArrayList<EventGenerator> queue;
+
+  EventFactory efact;
+
+  protected EventGeneratorFactory () {
+    efact = null;
+  }
+
+  protected EventGeneratorFactory (EventFactory efact) {
+    this.efact = efact;
+  }
+
+  protected void init (String fname) throws ESParser.Exception {
+    cur = 0;
+    states = new DynamicObjectArray<Memento>();
+
+    sections = new LinkedHashMap<String,ArrayList<EventGenerator>>();
+    queue = new ArrayList<EventGenerator>();
+    sections.put(DEFAULT, queue);
+
+    ESParser parser= new ESParser(fname, efact);
+    script = parser.parse();
+    scriptFileName = fname;
+
+    script.process(this);
+  }
+
+  @Override
+  public Iterator<EventGenerator> iterator() {
+    return queue.iterator();
+  }
+
+  protected void addLoop (int startPos) {
+    queue.add( new Loop( "loop", startPos, queue.size()-1));
+  }
+
+  public abstract Class<?> getEventType();
+
+  /**
+   * reset the enumeration state of this factory
+   */
+  public void reset () {
+    cur = 0;
+  }
+
+  public String getScriptFileName() {
+    return scriptFileName;
+  }
+
+  public Script getScript() {
+    return script;
+  }
+
+  public boolean hasSection (String id) {
+    return sections.containsKey(id);
+  }
+
+  public ArrayList<EventGenerator> getSection (String id) {
+    return sections.get(id);
+  }
+
+  public ArrayList<EventGenerator> getDefaultSection () {
+    return sections.get(DEFAULT);
+  }
+
+  protected void setQueue (ArrayList<EventGenerator> q) {
+    if (queue != q) {
+      queue = q;
+      cur = 0;
+    }
+  }
+
+  protected EventGenerator getNextEventGenerator() {
+    EventGenerator cg;
+    int n = queue.size();
+
+    if (n == 0) {
+      return null; // nothing to do
+    }
+
+    if (cur < n) {
+      cg = getQueueItem(cur); // might clone the queue item
+
+      // <2do> - this is a BAD hot fix, but it's going away soon!
+      if (cg instanceof Loop) {
+        int tgtPos = ((Loop)cg).getStartPos();
+        cg = queue.get(tgtPos);
+
+        if (!cg.hasMoreChoices()) {
+          for (int i=tgtPos; i<cur; i++) {
+            queue.get(i).reset();
+          }
+        }
+
+        cur = tgtPos;
+      }
+
+      cg.setId(Integer.toString(cur));
+
+      // might be reused if we re-enter a section sequence or REPEAT body, so we have to reset
+      // <2do> - commenting this out leads to premature state matching on model loops
+      // (will be fixed by the revamped environment modeling)
+      //cg.reset();
+
+      cur++;
+      return cg;
+
+    } else {
+      return null; // nothing left
+    }
+  }
+
+  // we encapsulate this because it might require cloning
+  protected EventGenerator getQueueItem (int i) {
+    return queue.get(i);
+  }
+
+
+  public int getTotalNumberOfEvents() {
+    int total=0;
+    int last = 1;
+
+    for (EventGenerator cg : queue) {
+       int level = cg.getTotalNumberOfChoices() * last;
+       total += level;
+       last = level;
+    }
+
+    return total;
+  }
+
+  public void printOn (PrintWriter pw) {
+    for (EventGenerator eg : queue) {
+      pw.println(eg);
+    }
+  }
+
+  /************************************** SearchListener interface **************/
+  /* we need this after a backtrack and restore to determine the next CG to return
+   */
+
+  @Override
+  public void searchStarted (Search search) {
+    cur = 0;
+  }
+
+  @Override
+  public void stateAdvanced (Search search) {
+    int idx = search.getStateId();
+
+    if (idx >= 0) { // <??> why would it be notified for the init state?
+      Memento m = new Memento(this);
+      states.set(idx, m);
+    }
+  }
+
+  @Override
+  public void stateBacktracked (Search search) {
+    Memento m = states.get(search.getStateId());
+    m.restore(this);
+    // nextCg will be re-computed (->getNext), so there is no need to reset
+  }
+
+  @Override
+  public void stateRestored (Search search) {
+    Memento m = states.get(search.getStateId());
+    m.restore(this);
+
+    // nextCg is restored (not re-computed), so we need to reset
+    SystemState ss = search.getVM().getSystemState();
+    ChoiceGenerator cgNext = ss.getNextChoiceGenerator();
+    cgNext.reset();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/Repetition.java b/src/main/gov/nasa/jpf/util/script/Repetition.java
new file mode 100644 (file)
index 0000000..f36e6a8
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+
+public class Repetition extends ScriptElementContainer {
+  int repeatCount;
+
+  class RepetitionIterator extends ScriptElementContainer.SECIterator {
+
+    int count;
+
+    RepetitionIterator () {
+      count = 0;
+      cur = firstChild;
+    }
+
+    @Override
+       public boolean hasNext() {
+      return ((cur != null) || (count<repeatCount) || (repeatCount < 0));
+    }
+
+    @Override
+       public ScriptElement next() {
+      if (cur != null) {
+        ScriptElement ret = cur;
+        cur = cur.nextSibling;
+        return ret;
+      } else {
+        if ((++count < repeatCount) || (repeatCount < 0) ) {
+          ScriptElement ret = firstChild;
+          cur = ret.nextSibling;
+          return ret;
+        } else {
+          return null;
+        }
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("no ScriptElement removal supported");
+    }
+  }
+
+
+  public Repetition (ScriptElement parent, int n, int line) {
+    super(parent, line);
+    repeatCount = n;
+  }
+
+  @Override
+  public SECIterator iterator() {
+    return new RepetitionIterator();
+  }
+
+  @Override
+  public String toString() {
+    return toString("REPEAT " + repeatCount );
+  }
+
+  public int getRepeatCount() {
+    return repeatCount;
+  }
+
+  @Override
+  public void process (ElementProcessor p) {
+    p.process(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/script/Script.java b/src/main/gov/nasa/jpf/util/script/Script.java
new file mode 100644 (file)
index 0000000..520dc89
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+public class Script extends ScriptElementContainer {
+
+  /***** the stuff we need for traversal *****/
+
+  public Script() {
+    super(null, 0);
+  }
+
+  @Override
+  public String toString() {
+    return toString("Script");
+  }
+
+  @Override
+  public void process (ElementProcessor p) {
+    processChildren(p);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/script/ScriptElement.java b/src/main/gov/nasa/jpf/util/script/ScriptElement.java
new file mode 100644 (file)
index 0000000..da0851a
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+public abstract class ScriptElement implements Cloneable {
+  protected ScriptElement parent;
+  protected ScriptElement nextSibling;
+  protected int line;
+
+  ScriptElement (ScriptElement parent, int line){
+    this.parent = parent;
+    this.line = line;
+  }
+
+  public ScriptElement getParent() {
+    return parent;
+  }
+
+  public int getLine() {
+    return line;
+  }
+
+  public ScriptElement getNextSibling() {
+    return nextSibling;
+  }
+
+  void setNextSibling(ScriptElement e) {
+    nextSibling = e;
+  }
+
+  @Override
+  public ScriptElement clone() {
+    try {
+      return (ScriptElement) super.clone();
+    } catch (CloneNotSupportedException cnsx) {
+      return null;
+    }
+  }
+
+  public abstract void process (ElementProcessor proc);
+}
diff --git a/src/main/gov/nasa/jpf/util/script/ScriptElementContainer.java b/src/main/gov/nasa/jpf/util/script/ScriptElementContainer.java
new file mode 100644 (file)
index 0000000..066dfdb
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+
+public abstract class ScriptElementContainer extends ScriptElement implements Iterable <ScriptElement> {
+
+  protected class SECIterator implements Iterator<ScriptElement>, Cloneable {
+
+    SECIterator prev; // to build ad hoc stacks
+    ScriptElement cur;
+
+    SECIterator () {
+      cur = firstChild;
+    }
+
+    @Override
+       public boolean hasNext() {
+      return (cur != null);
+    }
+
+    @Override
+       public ScriptElement next() {
+      if (cur != null) {
+        ScriptElement ret = cur;
+        cur = cur.nextSibling;
+        return ret;
+      } else {
+        return null;
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("no ScriptElement removal supported");
+    }
+
+    public SECIterator getPrev() {
+      return prev;
+    }
+
+    public void setPrev (SECIterator it) {
+      prev = it;
+    }
+
+    @Override
+       public Object clone() {
+      try {
+        // need to deep copy iterators
+        SECIterator it = (SECIterator)super.clone();
+        if (prev != null) {
+          it.prev = (SECIterator) prev.clone();
+        }
+        return it;
+      } catch (CloneNotSupportedException cnsx) {
+        return null; // can't happen, just compiler pleasing
+      }
+    }
+  }
+
+
+  ScriptElement firstChild;
+
+  ScriptElementContainer (ScriptElement parent, int line) {
+    super(parent, line);
+  }
+
+  public ScriptElement getFirstChild () {
+    return firstChild;
+  }
+
+  /**
+   * beware, this sets the nextSibling
+   */
+  public void add (ScriptElement e) {
+    e.nextSibling = null;
+
+    if (firstChild == null) {
+      firstChild = e;
+    } else {
+      ScriptElement p=firstChild;
+      while (p.nextSibling != null) p=p.nextSibling;
+      p.nextSibling = e;
+    }
+  }
+
+  public int getNumberOfChildren() {
+    int n=0;
+    ScriptElement e = firstChild;
+    while (e != null) {
+      n++;
+      e = e.getNextSibling();
+    }
+    return n;
+  }
+
+  @Override
+  public SECIterator iterator () {
+    return new SECIterator();
+  }
+
+  void dump (PrintWriter w, int level, ScriptElement elem) {
+
+    try {
+      while (elem != null) {
+        for (int i=0; i<level; i++) {
+          w.print("  ");
+        }
+        w.print(elem);
+
+        if (elem instanceof ScriptElementContainer) {
+          ScriptElementContainer c = (ScriptElementContainer) elem;
+          w.println(" {");
+          dump(w, level+1, c.getFirstChild());
+          for (int i=0; i<level; i++) {
+            w.print("  ");
+          }
+          w.println("}");
+        } else {
+          w.println();
+        }
+
+        elem = elem.getNextSibling();
+      }
+    }
+    catch (Throwable t) {
+      t.printStackTrace();
+    }
+  }
+
+  protected String toString (String type) {
+    StringBuilder sb = new StringBuilder();
+
+    if (type != null) {
+      sb.append(type);
+      sb.append(' ');
+    }
+
+    sb.append("{");
+    int i=0;
+    for (ScriptElement e = firstChild; e != null; e = e.nextSibling) {
+      if (i++ > 0) {
+        sb.append(',');
+      }
+      sb.append(e);
+    }
+    sb.append("}");
+    return sb.toString();
+  }
+
+  @Override
+  public String toString() {
+    return toString(null);
+  }
+
+  public void dump (PrintWriter w) {
+    dump(w, 0, firstChild);
+  }
+
+  public void processChildren(ElementProcessor p) {
+    for (ScriptElement e = firstChild; e != null; e = e.nextSibling) {
+      e.process(p);
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/util/script/ScriptEnvironment.java b/src/main/gov/nasa/jpf/util/script/ScriptEnvironment.java
new file mode 100644 (file)
index 0000000..ce19e41
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.StateExtensionClient;
+import gov.nasa.jpf.util.StateExtensionListener;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * class representing a statemachine environment that produces SCEventGenerators
+ * from scripts
+ */
+public abstract class ScriptEnvironment<CG extends ChoiceGenerator<?>> 
+         implements StateExtensionClient<ScriptEnvironment<CG>.ActiveSnapshot> {
+
+  static final String DEFAULT = "default";
+
+  //--- just a helper tuple
+  static class ActiveSequence implements Cloneable {
+    String stateName;
+    Section section;
+    SequenceInterpreter intrp;
+
+    public ActiveSequence (String stateName, Section section, SequenceInterpreter intrp) {
+      this.stateName = stateName;
+      this.section = section;
+      this.intrp = intrp;
+    }
+
+    @Override
+       public Object clone() {
+      try {
+        ActiveSequence as = (ActiveSequence) super.clone();
+        as.intrp = (SequenceInterpreter) intrp.clone();
+        return as;
+      } catch (CloneNotSupportedException nonsense) {
+        return null; // we are a Cloneable, so we don't get here
+      }
+    }
+
+    public boolean isDone() {
+      return intrp.isDone();
+    }
+  }
+
+  //--- our state extension - we need this mostly for cloning (deep copy)
+  class ActiveSnapshot implements Cloneable {
+    ActiveSequence[] actives;
+
+    ActiveSnapshot () {
+      actives = new ActiveSequence[0];
+    }
+
+    ActiveSnapshot (ActiveSequence[] as) {
+      actives = as;
+    }
+
+    public ActiveSequence get (String stateName) {
+      for (ActiveSequence as : actives) {
+        if (as.stateName.equals(stateName)) {
+          return as;
+        }
+      }
+      return null;
+    }
+
+    @Override
+       public Object clone() {
+      try {
+        ActiveSnapshot ss = (ActiveSnapshot)super.clone();
+        for (int i=0; i<actives.length; i++) {
+          ActiveSequence as = actives[i];
+          ss.actives[i] = (ActiveSequence)as.clone();
+        }
+        return ss;
+      } catch (CloneNotSupportedException nonsense) {
+        return null; // we are a Cloneable, so we don't get here
+      }
+    }
+
+    ActiveSnapshot advance (String[] activeStates, BitSet isReEntered) {
+      ActiveSequence[] newActives = new ActiveSequence[activeStates.length];
+
+      //--- carry over the persisting entries
+      for (int i=0; i<activeStates.length; i++) {
+        String sn = activeStates[i];
+        for (ActiveSequence as : actives) {
+          if (as.stateName.equals(sn) ) {
+            // we could use isReEntered to determine if we want to restart sequences
+            // <2do> how do we factor this out as policy?
+            newActives[i] = (ActiveSequence)as.clone();
+          }
+        }
+      }
+
+      //--- add the new ones
+      int skipped = 0;
+      nextActive:
+      for (int i=0; i<activeStates.length; i++) {
+        if (newActives[i] == null) {
+          // get the script section
+          Section sec = getSection(activeStates[i]);
+          if (sec != null) {
+
+            // check if that section is already processed by another active state, in which case we skip
+            for (int j=0; j<newActives.length; j++) {
+              if (newActives[j] != null && newActives[j].section == sec) {
+                skipped++;
+                continue nextActive;
+              }
+            }
+
+            // check if it was processed by a prev state (superstate section by a
+            // common parent of a new and an old state - this is the common case
+            for (int j=0; j<actives.length; j++) {
+              // <2do> how do we handle state re-entering?
+              if (actives[j].section == sec) {
+                ActiveSequence as = new ActiveSequence(activeStates[i], sec, actives[j].intrp);
+                newActives[i] = as;
+                continue nextActive;
+              }
+            }
+
+            // it's a new one
+            ActiveSequence as = new ActiveSequence(activeStates[i], sec,
+                                                   new SequenceInterpreter(sec));
+            newActives[i] = as;
+
+          } else { // sec == null : we didn't find any sequence for this state
+            skipped++;
+          }
+        }
+      }
+
+      //--- compress if we skipped any active states
+      if (skipped > 0) {
+        int n = activeStates.length - skipped;
+        ActiveSequence[] na = new ActiveSequence[n];
+        for (int i=0, j=0; j<n; i++) {
+          if (newActives[i] != null) {
+            na[j++] = newActives[i];
+          }
+        }
+        newActives = na;
+      }
+
+      return new ActiveSnapshot(newActives);
+    }
+  }
+
+  //--- start of ScriptEnvronment
+
+  String scriptName;
+  Reader scriptReader;
+  Script script;
+  ActiveSnapshot cur;
+
+  HashMap<String,Section> sections = new HashMap<String,Section>();
+  Section defaultSection;
+
+  //--- initialization
+  public ScriptEnvironment (String fname) throws FileNotFoundException {
+    this( fname, new FileReader(fname));
+  }
+
+  public ScriptEnvironment (String name, Reader r) {
+    this.scriptName = name;
+    this.scriptReader = r;
+  }
+
+  public void parseScript () throws ESParser.Exception {
+    ESParser parser= new ESParser(scriptName, scriptReader);
+    script = parser.parse();
+
+    initSections();
+
+    cur = new ActiveSnapshot();
+  }
+
+  void initSections() {
+    Section defSec = new Section(script, DEFAULT);
+
+    for (ScriptElement e : script) {
+
+      if (e instanceof Section) {
+        Section sec = (Section)e;
+        List<String> secIds = sec.getIds();
+        if (secIds.size() > 0) {
+          for (String id : secIds) {
+            sections.put(id, (Section)sec.clone()); // clone to guarantee different identities
+          }
+        } else {
+          sections.put(secIds.get(0), sec);
+        }
+      } else { // add copy to default sequence
+        defSec.add(e.clone());
+      }
+    }
+
+    if (defSec.getNumberOfChildren() > 0) {
+      defaultSection = defSec;
+    }
+  }
+
+  Section getSection (String id) {
+    Section sec = null;
+
+    while (id != null) {
+      sec = sections.get(id);
+      if (sec != null) {
+        return sec;
+      }
+
+      int idx = id.lastIndexOf('.');
+      if (idx > 0) {
+        id = id.substring(0, idx); // ?? do we really want this recursive? that's policy
+      } else {
+        id = null;
+      }
+    }
+
+    return defaultSection;
+  }
+
+  void addExpandedEvent(ArrayList<Event> events, Event se) {
+    for (Event e : se.expand()) {
+      if (!events.contains(e)) {
+        events.add(e);
+      }
+    }
+  }
+
+  static final String[] ACTIVE_DEFAULT = { DEFAULT };
+
+  public CG getNext (String id) {
+    return getNext(id, ACTIVE_DEFAULT, null);
+  }
+
+  public CG getNext (String id, String[] activeStates) {
+    return getNext(id, activeStates, null);
+  }
+
+  // this is our main purpose in life, but there is some policy in here
+  public CG getNext (String id, String[] activeStates, BitSet isReEntered) {
+
+    cur = cur.advance(activeStates, isReEntered);
+
+    ArrayList<Event> events = new ArrayList<Event>(cur.actives.length);
+    for (ActiveSequence as : cur.actives) {
+
+      while (true) {
+        ScriptElement se = as.intrp.getNext();
+        if (se != null) {
+          if (se instanceof Event) {
+            addExpandedEvent(events, (Event)se);
+            break;
+          } else if (se instanceof Alternative) {
+            for (ScriptElement ase : (Alternative)se) {
+              if (ase instanceof Event) {
+                addExpandedEvent(events, (Event)ase);
+              }
+            }
+            break;
+          } else {
+            // get next event
+          }
+        } else {
+          break; // process next active sequence
+        }
+      }
+    }
+
+    return createCGFromEvents(id, events);
+  }
+
+  protected abstract CG createCGFromEvents(String id, List<Event> events);
+
+  //--- StateExtension interface
+  @Override
+  public ActiveSnapshot getStateExtension() {
+    return cur;
+  }
+
+  @Override
+  public void restore(ActiveSnapshot stateExtension) {
+    cur = stateExtension;
+  }
+
+  @Override
+  public void registerListener(JPF jpf) {
+    StateExtensionListener<ActiveSnapshot> sel = new StateExtensionListener(this);
+    jpf.addSearchListener(sel);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/Section.java b/src/main/gov/nasa/jpf/util/script/Section.java
new file mode 100644 (file)
index 0000000..126dd90
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import gov.nasa.jpf.util.Misc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * this script element is just a way to do logical partitioning of scripts
+ * and doesn't bear any additional info than just an id. It is an optional element
+ */
+public class Section extends ScriptElementContainer {
+
+  ArrayList<String> ids = new ArrayList<String>();
+
+  public Section (ScriptElement parent, String id) {
+    super(parent,0);
+    this.ids.add(id);
+  }
+
+  public Section (ScriptElement parent, List<String> ids, int line) {
+    super(parent, line);
+    this.ids.addAll(ids);
+  }
+
+  public List<String> getIds() {
+    return ids;
+  }
+
+  public boolean containsId (String id) {
+    return ids.contains(id);
+  }
+
+  @Override
+  public String toString() {
+    return super.toString( Misc.toString(ids, "SECTION ", ",", null));
+  }
+
+  @Override
+  public void process (ElementProcessor proc) {
+    proc.process(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/util/script/SequenceInterpreter.java b/src/main/gov/nasa/jpf/util/script/SequenceInterpreter.java
new file mode 100644 (file)
index 0000000..9c91289
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import gov.nasa.jpf.util.script.ScriptElementContainer.SECIterator;
+
+import java.io.StringReader;
+
+/**
+ * an interpreter that walks a ScriptElementContainer hierarchy, returning
+ * Events and Alternatives while expanding loops
+ */
+public class SequenceInterpreter implements Cloneable {
+
+  ScriptElementContainer.SECIterator top;
+
+  public SequenceInterpreter (ScriptElementContainer seq) {
+    top = seq.iterator();
+  }
+
+  void push (SECIterator it) {
+    it.prev = top;
+    top = it;
+  }
+
+  SECIterator pop () {
+    if (top != null) {
+      top = top.getPrev();
+    }
+    return top;
+  }
+
+  public ScriptElement getNext() {
+    if (top != null) {
+      ScriptElement e = top.next();
+      if (e != null) {
+        if ((e instanceof ScriptElementContainer) && !(e instanceof Alternative) ) {
+          push( ((ScriptElementContainer)e).iterator());
+          return getNext();
+        } else {
+          return e;
+        }
+      } else {
+        pop();
+        return (top != null) ? getNext() : null;
+      }
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public Object clone() {
+    // has to deep copy all iterators
+
+    try {
+      SequenceInterpreter si = (SequenceInterpreter) super.clone();
+      if (top != null) {
+        si.top = (SECIterator)top.clone();
+      }
+      return si;
+    } catch (CloneNotSupportedException nonsense) {
+      return null; // we are a Cloneable, so we don't get here
+    }
+  }
+
+  public boolean isDone() {
+    return (top == null);
+  }
+
+  //---- test driver
+  public static void main (String[] args) {
+    //String s = "";
+    //String s = "a; b; c ";
+    //String s = "REPEAT 2 { e1, REPEAT 1 { r1, r2 }, e2 }";
+    //String s = "x, ANY {a1,a2}, y";
+    String s = "REPEAT 2 { start, ANY {a1,a2}, REPEAT 2 {r}, end }";
+
+
+    StringReader r = new StringReader(s);
+
+    try {
+      ESParser parser = new ESParser("test", r);
+      Script script = parser.parse();
+
+      SequenceInterpreter si = new SequenceInterpreter(script);
+
+      for (ScriptElement e = si.getNext(); e != null; e = si.getNext()) {
+        System.out.println(e);
+      }
+
+    } catch (Throwable t){
+      t.printStackTrace();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/script/StringSetGenerator.java b/src/main/gov/nasa/jpf/util/script/StringSetGenerator.java
new file mode 100644 (file)
index 0000000..fea2aa4
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.script;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+
+class CG {}
+
+class SingleChoice extends CG {
+  Event event;
+
+  SingleChoice (Event e) {
+    event = e;
+  }
+  @Override
+  public String toString() {
+    return event.toString();
+  }
+}
+
+class SetChoice extends CG {
+  ArrayList<Event> choices = new ArrayList<Event>();
+
+  public void add(Event e) {
+    choices.add(e);
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append('{');
+    int i=0, n = choices.size();
+    for (Event e : choices) {
+      sb.append(e);
+      if (++i < n) sb.append(',');
+    }
+    sb.append('}');
+    return sb.toString();
+  }
+}
+
+/**
+ * that's mostly a test class to see what a script would be expanded to w/o
+ * having any side effects in the ElementProcessor
+ */
+public class StringSetGenerator implements ElementProcessor {
+  LinkedHashMap<String,ArrayList<CG>> sections;
+  ArrayList<CG> queue;
+  
+  StringSetGenerator() {
+    sections = new LinkedHashMap<String,ArrayList<CG>>();
+    queue = new ArrayList<CG>();
+    sections.put("default", queue);
+  }
+  
+  @Override
+  public void process (Section sec) {
+    queue = new ArrayList<CG>();    
+    sec.processChildren(this);
+    
+    for (String id : sec.getIds()) {
+      sections.put(id,queue);      
+    }
+  }
+  
+  @Override
+  public void process (Event e) {
+    for (Event ee : e.expand()) {
+      queue.add( new SingleChoice(ee));
+    }    
+  }
+
+  @Override
+  public void process (Alternative a) {
+    SetChoice cg = new SetChoice();
+    for (ScriptElement e = a.getFirstChild(); e != null; e = e.getNextSibling()) {
+      if (e instanceof Event) {
+        for (Event ee : ((Event)e).expand()) {
+          cg.add(ee);
+        }
+      }
+    }
+    queue.add(cg);
+  }
+  
+
+  @Override
+  public void process (Repetition r) {
+    int n = r.getRepeatCount();
+    for (int i=0; i<n; i++) {
+      r.processChildren(this);
+    }
+  }
+  
+  public LinkedHashMap<String,ArrayList<CG>> getSections () {
+    return sections;
+  }
+
+  public List<CG> getCGQueue() {
+    return queue;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestJPF.java b/src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestJPF.java
new file mode 100644 (file)
index 0000000..2346e71
--- /dev/null
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.test;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.DirectCallStackFrame;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.ArrayList;
+
+/**
+ * native peer for our test class root
+ */
+public class JPF_gov_nasa_jpf_util_test_TestJPF extends NativePeer {
+
+  ClassInfo testClass;
+  MethodInfo testClassCtor;
+
+  MethodInfo[] testMethods = null;
+  int index = 0;
+  int testObjRef = MJIEnv.NULL;
+
+  boolean done;
+
+  private static void pushDirectCallFrame(MJIEnv env, MethodInfo mi, int objRef) {
+    ThreadInfo ti = env.getThreadInfo();
+
+    DirectCallStackFrame frame = mi.createDirectCallStackFrame(ti, 0);
+    frame.setReferenceArgument( 0, objRef, null);
+    ti.pushFrame(frame);
+  }
+
+  private boolean initializeTestMethods(MJIEnv env, String[] selectedTests) {
+    if (selectedTests != null && selectedTests.length > 0) {
+      testMethods = new MethodInfo[selectedTests.length];
+      int i = 0;
+      for (String test : selectedTests) {
+        MethodInfo mi = testClass.getMethod(test + "()V", false);
+        if (mi != null && mi.isPublic() && !mi.isStatic()) {
+          testMethods[i++] = mi;
+        } else {
+          env.throwException("java.lang.RuntimeException",
+                  "no such test method: public void " + test + "()");
+          return false;
+        }
+      }
+    } else { // collect all public void test..() methods
+      ArrayList<MethodInfo> list = new ArrayList<MethodInfo>();
+      for (MethodInfo mi : testClass) {
+        if (mi.getName().startsWith("test") && mi.isPublic() && !mi.isStatic() &&
+                mi.getSignature().equals("()V")) {
+          list.add(mi);
+        }
+      }
+      testMethods = list.toArray(new MethodInfo[list.size()]);
+    }
+
+    return true;
+  }
+
+  //--- our exported native methods
+
+  public JPF_gov_nasa_jpf_util_test_TestJPF () {
+    done = false;
+    index = 0;
+    testObjRef = MJIEnv.NULL;
+    testMethods = null;
+    testClass = null;
+    testClassCtor = null;
+  }
+
+  @MJI
+  public void $init____V (MJIEnv env, int objRef){
+    // nothing
+  }
+
+  @MJI
+  public void runTestsOfThisClass___3Ljava_lang_String_2__V (MJIEnv env, int clsObjRef,
+                                                                    int selectedTestsRef) {
+    ThreadInfo ti = env.getThreadInfo();
+
+    if (!done) {
+      if (testMethods == null) {
+        StackFrame frame = env.getCallerStackFrame(); // the runTestsOfThisClass() caller
+
+        testClass = frame.getClassInfo();
+        testClassCtor = testClass.getMethod("<init>()V", true);
+
+        String[] selectedTests = env.getStringArrayObject(selectedTestsRef);
+        if (initializeTestMethods(env, selectedTests)) {
+          env.repeatInvocation();
+        }
+
+      } else { // this is re-executed
+        if (testObjRef == MJIEnv.NULL) { // create a new test object
+          testObjRef = env.newObject(testClass);
+
+          if (testClassCtor != null) {
+            pushDirectCallFrame(env, testClassCtor, testObjRef);
+            env.repeatInvocation();
+          }
+
+        } else { // enter the next test
+          if (testMethods != null && (index < testMethods.length)) {
+            MethodInfo miTest = testMethods[index++];
+            pushDirectCallFrame(env, miTest, testObjRef);
+
+            if (index < testMethods.length) {
+              testObjRef = MJIEnv.NULL;
+            } else {
+              done = true;
+            }
+
+            env.repeatInvocation();
+          }
+        }
+      }
+    }
+  }
+
+  @MJI
+  public int createAndRunJPF__Ljava_lang_StackTraceElement_2_3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef, int a1, int a2){
+    // don't get recursive
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public int getProperty__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int keyRef){
+    String key = env.getStringObject(keyRef);
+    String val = env.getConfig().getString(key);
+    
+    if (val != null){
+      return env.newString(val);
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+  
+  /**
+   * if any of our methods are executed, we know that we already run under JPF
+   */
+  @MJI
+  public boolean isJPFRun____Z (MJIEnv env, int clsObjRef){
+    return true;
+  }
+
+  @MJI
+  public boolean isJUnitRun____Z (MJIEnv env, int clsObjRef){
+    return false;
+  }
+
+  @MJI
+  public boolean isRunTestRun____Z (MJIEnv env, int clsObjRef){
+    return false;
+  }
+
+
+  // we need to override these so that the actual test code gets executed
+  // if we fail to intercept, the bytecode will actually start JPF
+  @MJI
+  public int noPropertyViolation___3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef, int jpfArgsRef){
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean verifyNoPropertyViolation___3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public boolean verifyAssertionErrorDetails__Ljava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                  int detailsRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public boolean verifyAssertionError___3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public int unhandledException__Ljava_lang_String_2Ljava_lang_String_2_3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef,
+                                  int xClassNameRef, int detailsRef, int jpfArgsRef){
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean verifyUnhandledException__Ljava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                  int xClassNameRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public boolean verifyUnhandledExceptionDetails__Ljava_lang_String_2Ljava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                  int xClassNameRef, int detailsRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public int propertyViolation__Ljava_lang_Class_2_3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef,
+                                  int propClsRef, int jpfArgsRef){
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean verifyPropertyViolation__Lgov_nasa_jpf_util_TypeRef_2_3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                  int propClsRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public int jpfException__Ljava_lang_Class_2_3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef,
+                                  int xClsRef, int jpfArgsRef){
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean verifyJPFException__Lgov_nasa_jpf_util_TypeRef_2_3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                  int xClsRef, int jpfArgsRef){
+    return true;
+  }
+
+  @MJI
+  public int deadlock___3Ljava_lang_String_2__Lgov_nasa_jpf_JPF_2 (MJIEnv env, int clsObjRef, int jpfArgsRef){
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean verifyDeadlock___3Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef, int jpfArgsRef){
+    return true;
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestMultiProcessJPF.java b/src/main/gov/nasa/jpf/util/test/JPF_gov_nasa_jpf_util_test_TestMultiProcessJPF.java
new file mode 100644 (file)
index 0000000..322fe18
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.test;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * This is a native peer for multiprocess test class root
+ */
+public class JPF_gov_nasa_jpf_util_test_TestMultiProcessJPF 
+  extends JPF_gov_nasa_jpf_util_test_TestJPF {
+
+  @MJI
+  public int getProcessId____I (MJIEnv env, int objRef) {
+    return ThreadInfo.getCurrentThread().getApplicationContext().getId();
+  }
+
+  @MJI
+  public static boolean mpVerifyAssertionErrorDetails__ILjava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int rString1, int rString2) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyAssertionError__I_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyNoPropertyViolation__I_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyUnhandledExceptionDetails__ILjava_lang_String_2Ljava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int clsRef, int details, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyUnhandledException__ILjava_lang_String_2_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int clsRef, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyJPFException__ILgov_nasa_jpf_util_TypeRef_2_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int typeRef, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyPropertyViolation__ILgov_nasa_jpf_util_TypeRef_2_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int typeRef, int argsRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean mpVerifyDeadlock__I_3Ljava_lang_String_2__Z (MJIEnv env, int objRef, int numOfPrc, int argsRef) {
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/test/TestJPF.java b/src/main/gov/nasa/jpf/util/test/TestJPF.java
new file mode 100644 (file)
index 0000000..f395152
--- /dev/null
@@ -0,0 +1,1181 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.test;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Error;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFShell;
+import gov.nasa.jpf.Property;
+import gov.nasa.jpf.annotation.FilterField;
+import gov.nasa.jpf.tool.RunTest;
+import gov.nasa.jpf.util.DevNullPrintStream;
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.JPFSiteUtils;
+import gov.nasa.jpf.util.Reflection;
+import gov.nasa.jpf.vm.ExceptionInfo;
+import gov.nasa.jpf.vm.NoUncaughtExceptionsProperty;
+import gov.nasa.jpf.vm.NotDeadlockedProperty;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * base class for JPF unit tests. TestJPF mostly includes JPF invocations
+ * that check for occurrence or absence of certain execution results
+ *
+ * This class can be used in two modes:
+ *
+ * <ol>
+ * <li> wrapping a number of related tests for different SuTs into one class
+ * (suite) that calls the various JPF runners with complete argument lists
+ * (as in JPF.main(String[]args)) </li>
+ *
+ * <li> derive a class from TestJPF that uses the "..This" methods, which in
+ * turn use reflection to automatically append the test class and method to the
+ * JPF.main argument list (based on the calling class / method names). Note that
+ * you have to obey naming conventions for this to work:
+ *
+ * <li> the SuT class has to be the same as the test class without "Test", e.g.
+ * "CastTest" -> "Cast" </li>
+ *
+ * <li> the SuT method has to have the same name as the @Test method that
+ * invokes JPF, e.g. "CastTest {.. @Test void testArrayCast() ..}" ->
+ * "Cast {.. void testArrayCast()..} </li>
+ * </ol>
+ */
+public abstract class TestJPF implements JPFShell  {
+  static PrintStream out = System.out;
+
+  public static final String UNNAMED_PACKAGE = "";
+  public static final String SAME_PACKAGE = null;
+
+  //--- those are only used outside of JPF execution
+  @FilterField protected static boolean globalRunDirectly, globalShowConfig;
+
+  @FilterField protected static boolean runDirectly; // don't run test methods through JPF, invoke it directly
+  @FilterField protected static boolean stopOnFailure; // stop as soon as we encounter a failed test or error
+  @FilterField protected static boolean showConfig; // for debugging purposes
+  @FilterField protected static boolean showConfigSources; // for debugging purposes  
+  @FilterField protected static boolean hideSummary;
+  
+  @FilterField protected static boolean quiet; // don't show test output
+  
+  @FilterField protected String sutClassName;
+
+  static class GlobalArg {
+    String key;
+    String val;
+
+    GlobalArg (String k, String v){
+      key = k;
+      val = v;
+    }
+  }
+
+  // it seems wrong to pull globalArgs here instead of setting it from
+  // RunTest, but RunTest has to make sure TestJPF is loaded through the
+  // JPFClassLoader, i.e. cannot directly reference this class.
+
+  @FilterField static ArrayList<GlobalArg> globalArgs;
+
+  protected static ArrayList<GlobalArg> getGlobalArgs() {
+    // NOTE - this is only set if we execute tests from build.xml
+    Config globalConf = RunTest.getConfig();
+    if (globalConf != null){
+      ArrayList<GlobalArg> list = new ArrayList<GlobalArg>();
+
+      //--- the "test.<key>" specs
+      String[] testKeys = globalConf.getKeysStartingWith("test.");
+      if (testKeys.length > 0){
+        for (String key : testKeys){
+          String val = globalConf.getString(key);
+          // <2do> this is a hack to avoid the problem of not being able to store
+          // empty/nil/null values in the global config (they are removed during global config init)
+          if (val.equals("REMOVE")){
+            val = null;
+          }
+          
+          key = key.substring(5);
+          
+          list.add(new GlobalArg(key,val));
+        }
+      }
+
+      return list;
+    }
+
+    return null;
+  }
+
+  static {
+    if (!isJPFRun()){
+      globalArgs = getGlobalArgs();
+    }
+  }
+
+  //--- internal methods
+
+  public static void fail (String msg, String[] args, String cause){
+    StringBuilder sb = new StringBuilder();
+
+    sb.append(msg);
+    if (args != null){
+      for (String s : args){
+        sb.append(s);
+        sb.append(' ');
+      }
+    }
+
+    if (cause != null){
+      sb.append(':');
+      sb.append(cause);
+    }
+
+    fail(sb.toString());
+  }
+
+  public static void fail (){
+    throw new AssertionError();
+  }
+
+  public static void fail (String msg){
+    throw new AssertionError(msg);
+  }
+
+  public void report (String[] args) {
+    if (!quiet){
+      out.print("  running jpf with args:");
+
+      for (int i = 0; i < args.length; i++) {
+        out.print(' ');
+        out.print(args[i]);
+      }
+
+      out.println();
+    }
+  }
+
+  /**
+   * compute the SuT class name for a given JUnit test class: remove
+   * optionally ending "..Test", and replace package (if specified)
+   * 
+   * @param testClassName the JUnit test class
+   * @param sutPackage optional SuT package name (without ending '.', null
+   * os SAME_PACKAGE means same package, "" or UNNAMED_PACKAGE means unnamed package)
+   * @return main class name of system under test
+   */
+  protected static String getSutClassName (String testClassName, String sutPackage){
+
+    String sutClassName = testClassName;
+
+    int i = sutClassName.lastIndexOf('.');
+    if (i >= 0){  // testclass has a package
+
+      if (sutPackage == null){   // use same package
+        // nothing to do
+      } else if (sutPackage.length() > 0) { // explicit sut package
+        sutClassName = sutPackage + sutClassName.substring(i);
+
+      } else { // unnamed sut package
+        sutClassName = sutClassName.substring(i+1);
+      }
+
+    } else { // test class has no package
+      if (sutPackage == null || sutPackage.length() == 0){   // use same package
+        // nothing to do
+      } else { // explicit sut package
+        sutClassName = sutPackage + '.' + sutClassName;
+      }
+    }
+
+    if (sutClassName.endsWith("JPF")) {
+      sutClassName = sutClassName.substring(0, sutClassName.length() - 3);
+    }
+
+    return sutClassName;
+  }
+
+  // we can't set the sutClassName only from main() called methods (like
+  // runTestsOfThisClass()) since main() doesn't get called if this is executed
+  // by Ant (via <junit> task)
+  // the default ctor is always executed
+  public TestJPF () {
+    sutClassName = getSutClassName(getClass().getName(), SAME_PACKAGE);
+  }
+
+
+
+  //------ the API to be used by subclasses
+
+  /**
+   * to be used from default ctor of derived class if the SuT is in a different
+   * package
+   * @param sutClassName the qualified SuT class name to be checked by JPF
+   */
+  protected TestJPF (String sutClassName){
+    this.sutClassName = sutClassName;
+  }
+
+  public static boolean isJPFRun () {
+    return false;
+  }
+
+  public static boolean isJUnitRun() {
+    // intercepted by native peer if this runs under JPF
+    Throwable t = new Throwable();
+    t.fillInStackTrace();
+
+    for (StackTraceElement se : t.getStackTrace()){
+      if (se.getClassName().startsWith("org.junit.")){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public static boolean isRunTestRun() {
+    // intercepted by native peer if this runs under JPF
+    Throwable t = new Throwable();
+    t.fillInStackTrace();
+
+    for (StackTraceElement se : t.getStackTrace()){
+      if (se.getClassName().equals("gov.nasa.jpf.tool.RunTest")){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  protected static void getOptions (String[] args){
+    runDirectly = globalRunDirectly;
+    showConfig = globalShowConfig;
+
+    // hideSummary and stopOnFailure only make sense as global options anyways
+
+    if (args != null){   
+      for (int i=0; i<args.length; i++){
+        String a = args[i];
+        if (a != null){
+          if (a.length() > 0){
+            if (a.charAt(0) == '-'){
+              a = a.substring(1);
+              
+              if (a.equals("d")){
+                runDirectly = true;
+              } else if (a.equals("s") || a.equals("show")){
+                showConfig = true;
+              } else if (a.equals("l") || a.equals("log")){
+                showConfigSources = true;
+              } else if (a.equals("q") || a.equals("quiet")){
+                quiet = true;                
+              } else if (a.equals("x")){
+                stopOnFailure = true;
+              } else if (a.equals("h")){
+                hideSummary = true;
+              }
+              args[i] = null;  // set it consumed
+
+            } else {
+              break; // done, this is a test method
+            }
+          }
+        }
+      }
+    }
+  }
+
+  protected static boolean hasExplicitTestMethods(String[] args){
+    for (String a : args){
+      if (a != null){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected static List<Method> getMatchingMethods(Class<? extends TestJPF> testCls,
+          int setModifiers, int unsetModifiers, String[] annotationNames){
+    List<Method> list = new ArrayList<Method>();
+    
+    for (Method m : testCls.getMethods()){
+      if (isMatchingMethod(m, setModifiers, unsetModifiers, annotationNames)){
+        list.add(m);
+      }
+    }
+    
+    return list;
+  }
+
+  protected static boolean isMatchingMethod(Method m, int setModifiers, int unsetModifiers, String[] annotationNames) {
+    int mod = m.getModifiers();
+    if (((mod & setModifiers) != 0) && ((mod & unsetModifiers) == 0)) {
+      if (m.getParameterTypes().length == 0) {
+        if (annotationNames != null){
+          Annotation[] annotations = m.getAnnotations();
+          for (int i = 0; i < annotations.length; i++) {
+            String annotType = annotations[i].annotationType().getName();
+            for (int j = 0; j < annotationNames.length; j++) {
+              if (annotType.equals(annotationNames[j])) {
+                return true;
+              }
+            }
+          }
+        } else {
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  protected static List<Method> getContextMethods(Class<? extends TestJPF> testCls, 
+                                                  int setModifiers, int unsetModifiers, String annotation){
+    String[] annotations = {annotation};
+
+    List<Method> list = new ArrayList<Method>();
+    for (Method m : testCls.getMethods()){
+      if (isMatchingMethod(m, setModifiers, unsetModifiers, annotations)){
+        list.add(m);
+      }
+    }
+    return list;
+  }
+
+  protected static List<Method> getBeforeMethods(Class<? extends TestJPF> testCls){
+    return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.Before");
+  }
+
+  protected static List<Method> getAfterMethods(Class<? extends TestJPF> testCls){
+    return getContextMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, "org.junit.After");
+  }
+
+  protected static List<Method> getBeforeClassMethods(Class<? extends TestJPF> testCls){
+    return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.BeforeClass");
+  }
+  
+  protected static List<Method> getAfterClassMethods(Class<? extends TestJPF> testCls){
+    return getContextMethods(testCls, Modifier.PUBLIC | Modifier.STATIC, 0, "org.junit.AfterClass");
+  }
+  
+  protected static boolean haveTestMethodSpecs( String[] args){
+    if (args != null && args.length > 0){
+      for (int i=0; i<args.length; i++){
+        if (args[i] != null){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  protected static List<Method> getTestMethods(Class<? extends TestJPF> testCls, String[] args){
+    String[] testAnnotations = {"org.junit.Test", "org.testng.annotations.Test"};
+
+    if (haveTestMethodSpecs( args)){ // test methods specified as arguments
+      List<Method> list = new ArrayList<Method>();
+
+      for (String test : args){
+        if (test != null){
+
+          try {
+            Method m = testCls.getMethod(test);
+
+            if (isMatchingMethod(m, Modifier.PUBLIC, Modifier.STATIC, null /*testAnnotations*/ )){
+              list.add(m);
+            } else {
+              throw new RuntimeException("test method must be @Test annotated public instance method without arguments: " + test);
+            }
+
+          } catch (NoSuchMethodException x) {
+            throw new RuntimeException("method: " + test
+                    + "() not in test class: " + testCls.getName(), x);
+          }
+        }
+      }
+      
+      return list;
+
+    } else { // no explicit test method specification, get all matches
+      return getMatchingMethods(testCls, Modifier.PUBLIC, Modifier.STATIC, testAnnotations);
+    }
+  }
+
+
+  protected static void reportTestStart(String mthName){
+    if (!quiet){
+      System.out.println();
+      System.out.print("......................................... testing ");
+      System.out.print(mthName);
+      System.out.println("()");
+    }
+  }
+
+  protected static void reportTestInitialization(String mthName){
+    if (!quiet){
+      System.out.print(".... running test initialization: ");
+      System.out.print(mthName);
+      System.out.println("()");
+    }
+  }
+
+  protected static void reportTestCleanup(String mthName){
+    if (!quiet){
+      System.out.print(".... running test cleanup: ");
+      System.out.print(mthName);
+      System.out.println("()");
+    }
+  }
+
+  protected static void reportTestFinished(String msg){
+    if (!quiet){
+      System.out.print("......................................... ");
+      System.out.println(msg);
+    }
+  }
+
+  protected static void reportResults(String clsName, int nTests, int nFailures, int nErrors, List<String> results){
+    System.out.println();
+    System.out.print("......................................... execution of testsuite: " + clsName);
+    if (nFailures > 0 || nErrors > 0){
+      System.out.println(" FAILED");
+    } else if (nTests > 0) {
+      System.out.println(" SUCCEEDED");
+    } else {
+      System.out.println(" OBSOLETE");
+    }
+
+    if (!quiet){
+      if (results != null) {
+        int i = 0;
+        for (String result : results) {
+          System.out.print(".... [" + ++i + "] ");
+          System.out.println(result);
+        }
+      }
+    }
+
+    System.out.print(".........................................");
+    System.out.println(" tests: " + nTests + ", failures: " + nFailures + ", errors: " + nErrors);
+  }
+
+  
+  static void invoke (Method m, Object testObject) throws IllegalAccessException, InvocationTargetException  {
+    PrintStream sysOut = null;
+    
+    try {
+      if (quiet){
+        sysOut = System.out;
+        System.setOut( new DevNullPrintStream());
+      }
+      
+      m.invoke( testObject);
+      
+    } finally {
+      if (quiet){
+        System.setOut( sysOut);
+      }
+    }
+  }
+  
+  /**
+   * this is the main test loop if this TestJPF instance is executed directly
+   * or called from RunTest. It is *not* called if this is executed from JUnit
+   */
+  public static void runTests (Class<? extends TestJPF> testCls, String... args){
+    int nTests = 0;
+    int nFailures = 0;
+    int nErrors = 0;
+    String testMethodName = null;
+    List<String> results = null;
+
+    getOptions(args);
+    globalRunDirectly = runDirectly;
+    globalShowConfig = showConfig;
+    boolean globalStopOnFailure = stopOnFailure;
+
+    try {
+      List<Method> testMethods = getTestMethods(testCls, args);
+      results = new ArrayList<String>(testMethods.size());
+
+      // check if we have JUnit style housekeeping methods (initialization and
+      // cleanup should use the same mechanisms as JUnit)
+            
+      List<Method> beforeClassMethods = getBeforeClassMethods(testCls);
+      List<Method> afterClassMethods = getAfterClassMethods(testCls);
+            
+      List<Method> beforeMethods = getBeforeMethods(testCls);
+      List<Method> afterMethods = getAfterMethods(testCls);
+
+      for (Method initMethod : beforeClassMethods) {
+        reportTestInitialization(initMethod.getName());
+        initMethod.invoke(null);
+      }
+            
+      for (Method testMethod : testMethods) {
+        testMethodName = testMethod.getName();
+        String result = testMethodName;
+        try {
+          Object testObject = testCls.newInstance();
+
+          nTests++;
+          reportTestStart( testMethodName);
+
+          // run per test initialization methods
+          for (Method initMethod : beforeMethods){
+            reportTestInitialization( initMethod.getName());
+            invoke( initMethod, testObject);
+          }
+
+          // now run the test method itself
+          invoke( testMethod, testObject);
+          result += ": Ok";
+
+          // run per test initialization methods
+          for (Method cleanupMethod : afterMethods){
+            reportTestCleanup( cleanupMethod.getName());
+            invoke( cleanupMethod, testObject);
+          }
+
+        } catch (InvocationTargetException x) {
+          Throwable cause = x.getCause();
+          cause.printStackTrace();
+          if (cause instanceof AssertionError) {
+            nFailures++;
+            reportTestFinished("test method failed with: " + cause.getMessage());
+            result += ": Failed";
+          } else {
+            nErrors++;
+            reportTestFinished("unexpected error while executing test method: " + cause.getMessage());
+            result += ": Error";
+          }
+
+          if (globalStopOnFailure){
+            break;
+          }
+        }
+        
+        results.add(result);
+        reportTestFinished(result);
+      }
+      
+      for (Method cleanupMethod : afterClassMethods) {
+        reportTestCleanup( cleanupMethod.getName());
+        cleanupMethod.invoke(null);
+      }
+
+
+    //--- those exceptions are unexpected and represent unrecoverable test harness errors
+    } catch (InvocationTargetException x) {
+      Throwable cause = x.getCause();
+      cause.printStackTrace();
+      nErrors++;
+      reportTestFinished("TEST ERROR: @BeforeClass,@AfterClass method failed: " + x.getMessage());
+      
+    } catch (InstantiationException x) {
+      nErrors++;
+      reportTestFinished("TEST ERROR: cannot instantiate test class: " + x.getMessage());
+    } catch (IllegalAccessException x) { // can't happen if getTestMethods() worked
+      nErrors++;
+      reportTestFinished("TEST ERROR: default constructor or test method not public: " + testMethodName);
+    } catch (IllegalArgumentException x) {  // can't happen if getTestMethods() worked
+      nErrors++;
+      reportTestFinished("TEST ERROR: illegal argument for test method: " + testMethodName);
+    } catch (RuntimeException rx) {
+      nErrors++;
+      reportTestFinished("TEST ERROR: " + rx.toString());
+    }
+
+    if (!hideSummary){
+      reportResults(testCls.getName(), nTests, nFailures, nErrors, results);
+    }
+
+    if (nErrors > 0 || nFailures > 0){
+      if (isRunTestRun()){
+        // we need to reportTestFinished this test has failed
+        throw new RunTest.Failed();
+      }
+    }
+  }
+
+  static String getProperty(String key){
+    // intercepted by peer
+    return null;
+  }
+  
+  /**
+   * this is the JPF entry method in case there is no main() in the test class
+   * 
+   * <2do> we should support test method arguments here
+   */
+  static void runTestMethod(String args[]) throws Throwable {
+    String testClsName = getProperty("target");
+    String testMthName = getProperty("target.test_method");
+    
+    Class<?> testCls = Class.forName(testClsName);
+    Object target = testCls.newInstance();
+    
+    Method method = testCls.getMethod(testMthName);
+
+    try {
+      method.invoke(target);
+    } catch (InvocationTargetException e) {
+      throw e.getCause(); 
+    }
+  }
+
+  /**
+   * NOTE: this needs to be called from the concrete test class, typically from
+   * its main() method, otherwise we don't know the name of the class we have
+   * to pass to JPF
+   */
+  protected static void runTestsOfThisClass (String[] testMethods){
+    // needs to be at the same stack level, so we can't delegate
+    Class<? extends TestJPF> testClass = Reflection.getCallerClass(TestJPF.class);
+    runTests(testClass, testMethods);
+  }
+
+  /**
+   * needs to be broken up into two methods for cases that do additional
+   * JPF initialization (jpf-inspector)
+   *
+   * this is called from the various verifyX() methods (i.e. host VM) to
+   * start JPF, it is never executed under JPF
+   */
+  protected JPF createAndRunJPF (StackTraceElement testMethod, String[] args) {
+    JPF jpf = createJPF( testMethod, args);
+    if (jpf != null){
+      jpf.run();
+    }
+    return jpf;
+  }
+
+  /**
+   * this is never executed under JPF
+   */
+  protected JPF createJPF (StackTraceElement testMethod, String[] args) {
+    JPF jpf = null;
+    
+    Config conf = new Config(args);
+
+    // --- add global args (if we run under RunTest)
+    if (globalArgs != null) {
+      for (GlobalArg ga : globalArgs) {
+        String key = ga.key;
+        String val = ga.val;
+        if (val != null){
+          conf.put(key, val);
+        } else {
+          conf.remove(key);
+        }
+      }
+    }
+
+    setTestTargetKeys(conf, testMethod);
+    
+    // --- initialize the classpath from <projectId>.test_classpath
+    String projectId = JPFSiteUtils.getCurrentProjectId();
+    if (projectId != null) {
+      String testCp = conf.getString(projectId + ".test_classpath");
+      if (testCp != null) {
+        conf.append("classpath", testCp, ",");
+      }
+    }
+
+    // --- if we have any specific test property overrides, do so
+    conf.promotePropertyCategory("test.");
+
+    getOptions(args);
+
+    if (showConfig || showConfigSources) {
+      PrintWriter pw = new PrintWriter(System.out, true);
+      if (showConfigSources) {
+        conf.printSources(pw);
+      }
+
+      if (showConfig) {
+        conf.print(pw);
+      }
+      pw.flush();
+    }
+
+    jpf = new JPF(conf);
+
+    return jpf;
+  }
+
+  protected void setTestTargetKeys(Config conf, StackTraceElement testMethod) {
+    conf.put("target.entry", "runTestMethod([Ljava/lang/String;)V");
+    conf.put("target", testMethod.getClassName());
+    conf.put("target.test_method", testMethod.getMethodName());
+  }
+
+  //--- the JPFShell interface
+  @Override
+  public void start(String[] testMethods){
+    Class<? extends TestJPF> testClass = getClass(); // this is an instance method
+    runTests(testClass, testMethods);
+  }
+
+  protected StackTraceElement getCaller(){
+    StackTraceElement[] st = (new Throwable()).getStackTrace();
+    return st[2];
+  }
+  
+  protected StackTraceElement setTestMethod (String clsName, String mthName){
+    return new StackTraceElement( clsName, mthName, null, -1);
+  }
+  
+  protected StackTraceElement setTestMethod (String mthName){
+    return new StackTraceElement( getClass().getName(), mthName, null, -1);
+  }
+  
+  
+  //--- the JPF run test methods
+
+  /**
+   * run JPF expecting a AssertionError in the SuT
+   * @param args JPF main() arguments
+   */
+  protected JPF assertionError (StackTraceElement testMethod, String... args){
+    return unhandledException( testMethod, "java.lang.AssertionError", null, args );    
+  }
+  protected JPF assertionError (String... args) {
+    return unhandledException( getCaller(), "java.lang.AssertionError", null, args );
+  }
+  
+  protected JPF assertionErrorDetails (StackTraceElement testMethod, String details, String... args) {
+    return unhandledException( testMethod, "java.lang.AssertionError", details, args );
+  }
+  protected JPF assertionErrorDetails (String details, String... args) {
+    return unhandledException( getCaller(), "java.lang.AssertionError", details, args );
+  }
+  protected boolean verifyAssertionErrorDetails (String details, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      unhandledException( getCaller(), "java.lang.AssertionError", details, args);
+      return false;
+    }
+  }
+  protected boolean verifyAssertionError (String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      unhandledException( getCaller(), "java.lang.AssertionError", null, args);
+      return false;
+    }
+  }
+
+  /**
+   * run JPF expecting no SuT property violations 
+   */
+  protected JPF noPropertyViolation (StackTraceElement testMethod, String... args) {
+    JPF jpf = null;
+
+    report(args);
+
+    try {
+      jpf = createAndRunJPF( testMethod, args);
+    } catch (Throwable t) {
+      // we get as much as one little hickup and we declare it failed
+      t.printStackTrace();
+      fail("JPF internal exception executing: ", args, t.toString());
+      return jpf;
+    }
+
+    List<Error> errors = jpf.getSearchErrors();
+    if ((errors != null) && (errors.size() > 0)) {
+      fail("JPF found unexpected errors: " + (errors.get(0)).getDescription());
+    }
+
+    return jpf;
+  }
+  
+  protected JPF noPropertyViolation (String... args) {
+    return noPropertyViolation( getCaller(), args);    
+  }
+  
+  protected boolean verifyNoPropertyViolation (String...args){
+    if (runDirectly) {
+      return true;
+    } else {
+      noPropertyViolation( getCaller(), args);
+      return false;
+    }
+  }
+
+  /**
+   * NOTE: this uses the exception class name because it might be an
+   * exception type that is only known to JPF (i.e. not in the native classpath)
+   *
+   * @param xClassName name of the exception base type that is expected
+   * @param details detail message of the expected exception
+   * @param args JPF arguments
+   */
+  protected JPF unhandledException (StackTraceElement testMethod, String xClassName, String details, String... args) {
+    JPF jpf = null;
+
+    report(args);
+
+    try {
+      jpf = createAndRunJPF(testMethod, args);
+    } catch (Throwable t) {
+      t.printStackTrace();
+      fail("JPF internal exception executing: ", args, t.toString());
+      return jpf;
+    }
+
+    Error error = jpf.getLastError();
+    if (error != null){
+      Property errorProperty = error.getProperty();
+      if (errorProperty instanceof NoUncaughtExceptionsProperty){ 
+        ExceptionInfo xi = ((NoUncaughtExceptionsProperty)errorProperty).getUncaughtExceptionInfo();
+        String xn = xi.getExceptionClassname();
+        if (!xn.equals(xClassName)) {
+          fail("JPF caught wrong exception: " + xn + ", expected: " + xClassName);
+        }
+
+        if (details != null) {
+          String gotDetails = xi.getDetails();
+          if (gotDetails == null) {
+            fail("JPF caught the right exception but no details, expected: " + details);
+          } else {
+            if (!gotDetails.endsWith(details)) {
+              fail("JPF caught the right exception but the details were wrong: " + gotDetails + ", expected: " + details);
+            }
+          }
+        }
+      } else { // error not a NoUncaughtExceptionsProperty
+        fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName));        
+      }
+    } else { // no error
+      fail("JPF failed to catch exception executing: ", args, ("expected " + xClassName));
+    }
+    
+    return jpf;
+  }
+  
+  protected JPF unhandledException (String xClassName, String details, String... args) {
+    return unhandledException( getCaller(), xClassName, details, args);
+  }
+
+    
+  protected boolean verifyUnhandledExceptionDetails (String xClassName, String details, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      unhandledException( getCaller(), xClassName, details, args);
+      return false;
+    }
+  }
+  protected boolean verifyUnhandledException (String xClassName, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      unhandledException( getCaller(), xClassName, null, args);
+      return false;
+    }
+  }
+
+
+  /**
+   * run JPF expecting it to throw an exception
+   * NOTE - xClassName needs to be the concrete exception, not a super class
+   * @param args JPF main() arguments
+   */
+  protected JPF jpfException (StackTraceElement testMethod, Class<? extends Throwable> xCls, String... args) {
+    JPF jpf = null;
+    Throwable exception = null;
+
+    report(args);
+
+    try {
+      jpf = createAndRunJPF( testMethod, args);
+    } catch (JPF.ExitException xx) {
+      exception = xx.getCause();
+    } catch (Throwable x) {
+      exception = x;
+    }
+
+    if (exception != null){
+      if (!xCls.isAssignableFrom(exception.getClass())){
+        fail("JPF produced wrong exception: " + exception + ", expected: " + xCls.getName());
+      }
+    } else {
+      fail("JPF failed to produce exception, expected: " + xCls.getName());
+    }
+
+    return jpf;
+  }
+  
+  protected JPF jpfException (Class<? extends Throwable> xCls, String... args) {
+    return jpfException( getCaller(), xCls, args);
+  }  
+  
+  protected boolean verifyJPFException (TypeRef xClsSpec, String... args){
+    if (runDirectly) {
+      return true;
+
+    } else {
+      try {
+        Class<? extends Throwable> xCls = xClsSpec.asNativeSubclass(Throwable.class);
+
+        jpfException( getCaller(), xCls, args);
+
+      } catch (ClassCastException ccx){
+        fail("not a property type: " + xClsSpec);
+      } catch (ClassNotFoundException cnfx){
+        fail("property class not found: " + xClsSpec);
+      }
+      return false;
+    }
+  }
+
+  
+  
+  /**
+   * run JPF expecting a property violation of the SuT
+   * @param args JPF main() arguments
+   */
+  protected JPF propertyViolation (StackTraceElement testMethod, Class<? extends Property> propertyCls, String... args ){
+    JPF jpf = null;
+
+    report(args);
+
+    try {
+      jpf = createAndRunJPF( testMethod, args);
+    } catch (Throwable t) {
+      t.printStackTrace();
+      fail("JPF internal exception executing: ", args, t.toString());
+    }
+
+    List<Error> errors = jpf.getSearchErrors();
+    if (errors != null) {
+      for (Error e : errors) {
+        if (propertyCls == e.getProperty().getClass()) {
+          return jpf; // success, we got the sucker
+        }
+      }
+    }
+
+    fail("JPF failed to detect error: " + propertyCls.getName());
+    return jpf;
+  }
+  
+  protected JPF propertyViolation (Class<? extends Property> propertyCls, String... args ){
+    return propertyViolation( getCaller(), propertyCls, args);
+  }
+  
+  protected boolean verifyPropertyViolation (TypeRef propertyClsSpec, String... args){
+    if (runDirectly) {
+      return true;
+
+    } else {
+      try {
+        Class<? extends Property> propertyCls = propertyClsSpec.asNativeSubclass(Property.class);
+        propertyViolation( getCaller(), propertyCls, args);
+
+      } catch (ClassCastException ccx){
+        fail("not a property type: " + propertyClsSpec);
+      } catch (ClassNotFoundException cnfx){
+        fail("property class not found: " + propertyClsSpec);
+      }
+      return false;
+    }
+  }
+
+
+  /**
+   * run JPF expecting a deadlock in the SuT
+   * @param args JPF main() arguments
+   */
+  protected JPF deadlock (String... args) {
+    return propertyViolation( getCaller(), NotDeadlockedProperty.class, args );
+  }
+  
+  protected boolean verifyDeadlock (String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      propertyViolation( getCaller(), NotDeadlockedProperty.class, args);
+      return false;
+    }
+  }
+    
+  // these are the org.junit.Assert APIs, but we don't want org.junit to be
+  // required to run tests
+
+  public static void assertEquals(String msg, Object expected, Object actual){
+    if (expected == null && actual == null) { 
+      return; 
+    }
+    
+    if (expected != null && expected.equals(actual)) {
+      return; 
+    }
+    
+    fail(msg);
+  }
+
+  public static void assertEquals(Object expected, Object actual){
+    assertEquals("", expected, actual); 
+  }
+
+  public static void assertEquals(String msg, int expected, int actual){
+    if (expected != actual) {
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(int expected, int actual){    
+    assertEquals("expected != actual : " + expected + " != " + actual, expected, actual);
+  }  
+
+  public static void assertEquals(String msg, long expected, long actual){
+    if (expected != actual) {
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(long expected, long actual){    
+      assertEquals("expected != actual : " + expected + " != " + actual,
+                   expected, actual);
+  }
+
+  public static void assertEquals(double expected, double actual){
+    if (expected != actual){
+      fail("expected != actual : " + expected + " != " + actual);
+    }
+  }
+
+  public static void assertEquals(String msg, double expected, double actual){
+    if (expected != actual){
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual){
+    if (expected != actual){
+      fail("expected != actual : " + expected + " != " + actual);
+    }
+  }
+
+  public static void assertEquals(String msg, float expected, float actual){
+    if (expected != actual){
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(String msg, double expected, double actual, double delta){
+    if (Math.abs(expected - actual) > delta) {
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(double expected, double actual, double delta){    
+    assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta,
+                 expected, actual, delta);
+  }
+
+  public static void assertEquals(String msg, float expected, float actual, float delta){
+    if (Math.abs(expected - actual) > delta) {
+      fail(msg);
+    }
+  }
+
+  public static void assertEquals(float expected, float actual, float delta){    
+      assertEquals("Math.abs(expected - actual) > delta : " + "Math.abs(" + expected + " - " + actual + ") > " + delta,
+                   expected, actual, delta);
+  }
+
+  public static void assertArrayEquals(byte[] expected, byte[] actual){
+    if (((expected == null) != (actual == null)) ||
+        (expected.length != actual.length)){
+      fail("array sizes different");
+    }
+
+    for (int i=0; i<expected.length; i++){
+      if (expected[i] != actual[i]){
+        fail("array element" + i + " different, expected " + expected[i] + ", actual " + actual[i]);
+      }
+    }
+  }
+
+  public static void assertNotNull(String msg, Object o) {
+    if (o == null) {
+      fail(msg);
+    }
+  }
+
+  public static void assertNotNull(Object o){
+    assertNotNull("o == null", o);
+  }
+
+  public static void assertNull(String msg, Object o){
+    if (o != null) {
+      fail(msg);
+    }
+  }
+
+  public static void assertNull(Object o){    
+    assertNull("o != null", o);
+  }
+
+  public static void assertSame(String msg, Object expected, Object actual){
+    if (expected != actual) {
+      fail(msg);
+    }
+  }
+
+  public static void assertSame(Object expected, Object actual){
+    assertSame("expected != actual : " + expected + " != " + actual, expected, actual);
+  }
+
+  public static void assertFalse (String msg, boolean cond){
+    if (cond) {
+      fail(msg);
+    }
+  }
+
+  public static void assertFalse (boolean cond){
+    assertFalse("", cond);
+  }
+
+  public static void assertTrue (String msg, boolean cond){
+    if (!cond) {
+      fail(msg);
+    }
+  }
+
+  public static void assertTrue (boolean cond){
+    assertTrue("", cond);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/util/test/TestMultiProcessJPF.java b/src/main/gov/nasa/jpf/util/test/TestMultiProcessJPF.java
new file mode 100644 (file)
index 0000000..7b34989
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.test;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.Property;
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.vm.NotDeadlockedProperty;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * This is a root class for testing multi-processes code. This forces
+ * JPF to use MultiProcessVM and DistributedSchedulerFactory
+ */
+public abstract class TestMultiProcessJPF extends TestJPF {
+  int numOfPrc;
+
+  @Override
+  protected void setTestTargetKeys(Config conf, StackTraceElement testMethod) {
+    conf.put("target.entry", "runTestMethod([Ljava/lang/String;)V");
+    conf.put("target.replicate", Integer.toString(numOfPrc));
+    conf.put("target", testMethod.getClassName());
+    conf.put("target.test_method", testMethod.getMethodName());
+    conf.put("vm.class", "gov.nasa.jpf.vm.MultiProcessVM");
+    conf.put("vm.scheduler_factory.class", "gov.nasa.jpf.vm.DistributedSchedulerFactory");
+  }
+
+  protected native int getProcessId();
+
+  protected boolean mpVerifyAssertionErrorDetails (int prcNum, String details, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      unhandledException( getCaller(), "java.lang.AssertionError", details, args);
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyAssertionError (int prcNum, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      unhandledException( getCaller(), "java.lang.AssertionError", null, args);
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyNoPropertyViolation (int prcNum, String...args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      noPropertyViolation(getCaller(), args);
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyUnhandledExceptionDetails (int prcNum, String xClassName, String details, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      unhandledException( getCaller(), xClassName, details, args);
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyUnhandledException (int prcNum, String xClassName, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      unhandledException( getCaller(), xClassName, null, args);
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyJPFException (int prcNum, TypeRef xClsSpec, String... args){
+    if (runDirectly) {
+      return true;
+
+    } else {
+      numOfPrc = prcNum;
+      try {
+        Class<? extends Throwable> xCls = xClsSpec.asNativeSubclass(Throwable.class);
+
+        jpfException( getCaller(), xCls, args);
+
+      } catch (ClassCastException ccx){
+        fail("not a property type: " + xClsSpec);
+      } catch (ClassNotFoundException cnfx){
+        fail("property class not found: " + xClsSpec);
+      }
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyPropertyViolation (int prcNum, TypeRef propertyClsSpec, String... args){
+    if (runDirectly) {
+      return true;
+
+    } else {
+      numOfPrc = prcNum;
+      try {
+        Class<? extends Property> propertyCls = propertyClsSpec.asNativeSubclass(Property.class);
+        propertyViolation( getCaller(), propertyCls, args);
+
+      } catch (ClassCastException ccx){
+        fail("not a property type: " + propertyClsSpec);
+      } catch (ClassNotFoundException cnfx){
+        fail("property class not found: " + propertyClsSpec);
+      }
+      return false;
+    }
+  }
+
+  protected boolean mpVerifyDeadlock (int prcNum, String... args){
+    if (runDirectly) {
+      return true;
+    } else {
+      numOfPrc = prcNum;
+      propertyViolation( getCaller(), NotDeadlockedProperty.class, args);
+      return false;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/AbstractRestorer.java b/src/main/gov/nasa/jpf/vm/AbstractRestorer.java
new file mode 100644 (file)
index 0000000..4432abc
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+public abstract class AbstractRestorer<Saved> implements StateRestorer<Saved>, KernelState.ChangeListener {
+
+  protected Saved cached = null;
+
+  VM vm;
+  protected KernelState ks = null;
+
+  
+  @Override
+  public void attach(VM vm) {
+    this.vm = vm;
+    this.ks = vm.getKernelState();
+  }
+  
+  @Override
+  public Saved getRestorableData() {
+    if (cached == null) {
+      cached = computeRestorableData();
+      ks.pushChangeListener(this);
+    }
+    return cached;
+  }
+  
+  @Override
+  public void restore (Saved data) {
+    doRestore(data);
+    if (cached == null) {
+      ks.pushChangeListener(this);
+    } else {
+      // invariant says we're already waiting for changes
+    }
+    cached = data;
+  }
+
+  @Override
+  public void kernelStateChanged (KernelState same) {
+    cached = null;
+  }
+  
+  protected abstract Saved computeRestorableData();
+  protected abstract void doRestore(Saved data);
+}
diff --git a/src/main/gov/nasa/jpf/vm/AbstractSerializer.java b/src/main/gov/nasa/jpf/vm/AbstractSerializer.java
new file mode 100644 (file)
index 0000000..975ec62
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.Misc;
+import java.util.List;
+
+
+
+public abstract class AbstractSerializer
+                 implements StateSerializer, KernelState.ChangeListener {
+  // INVARIANT: non-null iff registered for changes to KernelState
+  protected int[] cached = null;
+
+  protected VM vm;
+  protected KernelState ks = null;
+
+  // optional list of native state holders that contribute to storingData computation
+  protected NativeStateHolder[] nativeStateHolders = new NativeStateHolder[0];
+
+  @Override
+  public void attach(VM vm) {
+    this.vm = vm;
+    this.ks = vm.getKernelState();
+  }
+
+  public int getCurrentStateVectorLength() {
+    return cached.length;
+  }
+
+  @Override
+  public int[] getStoringData() {
+    if (cached == null) {
+      cached = computeStoringData();
+      ks.pushChangeListener(this);
+    }
+    return cached;
+  }
+
+  @Override
+  public void kernelStateChanged (KernelState same) {
+    cached = null;
+  }
+
+  
+  @Override
+  public void addNativeStateHolder (NativeStateHolder nsh){
+    nativeStateHolders = Misc.appendUniqueElement(nativeStateHolders, nsh);
+  }
+  
+  protected abstract int[] computeStoringData();
+}
diff --git a/src/main/gov/nasa/jpf/vm/AbstractTypeAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/AbstractTypeAnnotationInfo.java
new file mode 100644 (file)
index 0000000..b9e46af
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * abstract AnnotationInfo base for Java 8 type annotations
+ */
+public abstract class AbstractTypeAnnotationInfo extends AnnotationInfo {
+  
+  protected int targetType;   // see section 3.2 of JSR 308 - constants defined in .jvm.ClassFile
+  protected short[] typePath; // the type path for compound type annotations as per 3.4 of JSR 308
+  
+  protected AbstractTypeAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath) {
+    super(base);
+    
+    this.targetType = targetType;
+    this.typePath = typePath;
+  }
+  
+  // <2do> add typePath query
+  
+  public int getTargetType(){
+    return targetType;
+  }
+  
+  protected void addArgs(StringBuilder sb){
+    // nothing here
+  }
+  
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getName());
+    sb.append( '{');
+    
+    sb.append( "targetType:");
+    sb.append( Integer.toHexString(targetType));
+    
+    sb.append( ",name:");
+    sb.append( name);
+    
+    if (typePath != null){
+      sb.append( ",path:");
+      for (int i=0; i<typePath.length;i++){
+        int e = typePath[i];
+        sb.append('(');
+        sb.append( Integer.toString((e>>8) & 0xff));
+        sb.append( Integer.toString(e & 0xff));
+        sb.append(')');
+      }
+    }
+    
+    addArgs(sb); // overridden by subclasses
+    sb.append( '}');    
+    
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/AllRunnablesSyncPolicy.java b/src/main/gov/nasa/jpf/vm/AllRunnablesSyncPolicy.java
new file mode 100644 (file)
index 0000000..1f9f600
--- /dev/null
@@ -0,0 +1,345 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet;
+
+/**
+ * a Scheduler implementation base class that allows filtering of runnable threads and
+ * implements SyncPolicy without backtracking or empty transitions, i.e. choice sets that
+ * include all runnable threads
+ */
+public class AllRunnablesSyncPolicy implements SyncPolicy {
+
+  protected VM vm;
+  protected boolean breakSingleChoice;
+  protected boolean breakLockRelease;
+  protected boolean breakNotify;
+  protected boolean breakSleep;
+  protected boolean breakYield;
+  protected boolean breakPriority;
+  
+  public AllRunnablesSyncPolicy (Config config){
+    breakSingleChoice = config.getBoolean("cg.break_single_choice", false);    
+    breakLockRelease = config.getBoolean("cg.break_lock_release", true);
+    breakNotify = config.getBoolean("cg.break_notify", true);
+    breakSleep = config.getBoolean("cg.break_sleep", true);
+    breakYield = config.getBoolean("cg.break_yield", true);
+    breakPriority = config.getBoolean("cg.break_priority", true);
+  }
+  
+  
+  //--- internal methods
+
+  /**
+   * this is the main policy method that can be overridden by subclasses, e.g. by filtering
+   * out the highest priority runnables, or by ordering according to priority
+   * 
+   * Default behavior is to first try to find runnables within the provided ApplicationContext,
+   * and fall back to any runnable if there are none in this context
+   * 
+   * this includes threads that are in operations that can timeout
+   */
+  protected ThreadInfo[] getTimeoutRunnables (ApplicationContext appCtx){
+    ThreadList tlist = vm.getThreadList();
+    
+    if (tlist.hasProcessTimeoutRunnables(appCtx)){
+      return tlist.getProcessTimeoutRunnables(appCtx);
+    } else {
+      return tlist.getTimeoutRunnables();
+    }
+  }
+
+    
+  protected ChoiceGenerator<ThreadInfo> getRunnableCG (String id, ThreadInfo tiCurrent){
+    ApplicationContext appCtx = tiCurrent.getApplicationContext();
+    ThreadInfo[] choices = getTimeoutRunnables(appCtx);
+    
+    if (choices.length == 0){
+      return null;
+    }
+    
+    if ((choices.length == 1) && (choices[0] == tiCurrent) && !tiCurrent.isTimeoutWaiting()){ // no context switch
+      if (!breakSingleChoice){
+        return null;
+      }
+    }
+    
+    ChoiceGenerator<ThreadInfo> cg = new ThreadChoiceFromSet( id, choices, true);
+    
+    if(!vm.getThreadList().hasProcessTimeoutRunnables(appCtx)) {
+      GlobalSchedulingPoint.setGlobal(cg);
+    }
+    
+    return cg;
+  }
+  
+  protected boolean setNextChoiceGenerator (ChoiceGenerator<ThreadInfo> cg){
+    if (cg != null){
+      return vm.getSystemState().setNextChoiceGenerator(cg); // listeners could still remove CGs
+    } else {
+      return false;
+    }
+  }
+  
+  /**
+   * set a runnable CG that is optional if we are in a atomic section 
+   */
+  protected boolean setNonBlockingCG (String id, ThreadInfo tiCurrent){
+    if (!tiCurrent.isFirstStepInsn() || tiCurrent.isEmptyTransitionEnabled()) {
+      if (vm.getSystemState().isAtomic()) {
+        return false;
+      } else {
+        return setNextChoiceGenerator(getRunnableCG(id, tiCurrent));
+      }
+      
+    } else {
+      return false;  // no empty transitions
+    }
+  }
+  
+  protected static ChoiceGenerator<ThreadInfo> blockedWithoutChoice = 
+          new ThreadChoiceFromSet("BLOCKED_NO_CHOICE", new ThreadInfo[0], true);
+  
+  /**
+   * set a runnable CG that would break a atomic section because it requires
+   * a context switch
+   */
+  protected boolean setBlockingCG (String id, ThreadInfo tiCurrent){
+    if (!tiCurrent.isFirstStepInsn() || tiCurrent.isEmptyTransitionEnabled()) {
+      if (vm.getSystemState().isAtomic()) {
+        vm.getSystemState().setBlockedInAtomicSection();
+      }
+      
+      ChoiceGenerator<ThreadInfo> cg = getRunnableCG(id, tiCurrent);
+      if (cg == null){ // make sure we don't mask a deadlock
+        if (vm.getThreadList().hasLiveThreads()){
+          cg = blockedWithoutChoice;
+        }
+      }
+      
+      return setNextChoiceGenerator(cg);
+      
+    } else {
+      return false;  // no empty transitions
+    }
+  }
+    
+  /**
+   * set a runnable CG that only breaks a atomic section if the blocking thread
+   * is the currently executing one
+   */
+  protected boolean setMaybeBlockingCG (String id, ThreadInfo tiCurrent, ThreadInfo tiBlock){
+    if (tiCurrent == tiBlock){
+      return setBlockingCG( id, tiCurrent);
+    } else {
+      return setNonBlockingCG( id, tiCurrent);
+    }
+  }
+  
+  
+  //--- SyncPolicy interface
+  
+  //--- initialization
+  @Override
+  public void initializeSyncPolicy (VM vm, ApplicationContext appCtx){
+    this.vm  = vm;
+  }
+  
+  @Override
+  public void initializeThreadSync (ThreadInfo tiCurrent, ThreadInfo tiNew){
+  }
+    
+  //--- locks
+  @Override
+  public boolean setsBlockedThreadCG (ThreadInfo ti, ElementInfo ei){
+    return setBlockingCG( BLOCK, ti);
+  }
+  
+  @Override
+  public boolean setsLockAcquisitionCG (ThreadInfo ti, ElementInfo ei){
+    return setNonBlockingCG( LOCK, ti);
+  }
+  
+  @Override
+  public boolean setsLockReleaseCG (ThreadInfo ti, ElementInfo ei, boolean didUnblock){
+    if (breakLockRelease){
+      if (didUnblock){
+        return setNonBlockingCG( RELEASE, ti);
+      }
+    }
+    
+    return false;
+  }
+  
+  //--- thread termination
+  @Override
+  public boolean setsTerminationCG (ThreadInfo ti){
+    return setBlockingCG( TERMINATE, ti);
+  }
+  
+  //--- java.lang.Object APIs
+  @Override
+  public boolean setsWaitCG (ThreadInfo ti, long timeout){
+    return setBlockingCG( WAIT, ti);
+  }
+  
+  @Override
+  public boolean setsNotifyCG (ThreadInfo ti, boolean didNotify){
+    if (breakNotify){
+      if (didNotify){
+        return setNonBlockingCG( NOTIFY, ti);
+      }
+    }
+    
+    return false;
+  }
+  
+  @Override
+  public boolean setsNotifyAllCG (ThreadInfo ti, boolean didNotify){
+    if (breakNotify){
+      if (didNotify){
+        return setNonBlockingCG( NOTIFYALL, ti);
+      }
+    }
+    
+    return false;
+  }
+  
+    
+  //--- the java.lang.Thread APIs
+  @Override
+  public boolean setsStartCG (ThreadInfo tiCurrent, ThreadInfo tiStarted){
+    return setNonBlockingCG( START, tiCurrent);
+  }
+  
+  @Override
+  public boolean setsYieldCG (ThreadInfo ti){
+    if (breakYield){
+      return setNonBlockingCG( YIELD, ti);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public boolean setsPriorityCG (ThreadInfo ti){
+    if (breakPriority){
+      return setNonBlockingCG( PRIORITY, ti);    
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public boolean setsSleepCG (ThreadInfo ti, long millis, int nanos){
+    if (breakSleep){
+      return setNonBlockingCG( SLEEP, ti);
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public boolean setsSuspendCG (ThreadInfo tiCurrent, ThreadInfo tiSuspended){
+    return setMaybeBlockingCG( SUSPEND, tiCurrent, tiSuspended);      
+  }
+  
+  @Override
+  public boolean setsResumeCG (ThreadInfo tiCurrent, ThreadInfo tiResumed){
+    return setNonBlockingCG( RESUME, tiCurrent);
+  }
+  
+  @Override
+  public boolean setsJoinCG (ThreadInfo tiCurrent, ThreadInfo tiJoin, long timeout){
+    return setBlockingCG( JOIN, tiCurrent);      
+  }
+  
+  @Override
+  public boolean setsStopCG (ThreadInfo tiCurrent, ThreadInfo tiStopped){
+    return setMaybeBlockingCG( STOP, tiCurrent, tiStopped);
+  }
+  
+  @Override
+  public boolean setsInterruptCG (ThreadInfo tiCurrent, ThreadInfo tiInterrupted){
+    if (tiInterrupted.isWaiting()){
+      return setNonBlockingCG( INTERRUPT, tiCurrent);
+    } else {
+      return false;
+    }
+  }
+  
+  
+  //--- sun.misc.Unsafe
+  @Override
+  public boolean setsParkCG (ThreadInfo ti, boolean isAbsTime, long timeout){
+    return setBlockingCG( PARK, ti);
+  }
+  
+  @Override
+  public boolean setsUnparkCG (ThreadInfo tiCurrent, ThreadInfo tiUnparked){
+    // if the unparked thread is not blocked there is no point
+    if (tiUnparked.isBlocked()){
+      return setNonBlockingCG( UNPARK, tiCurrent);
+    } else {
+      return false;
+    }
+  }
+
+  
+  //--- system scheduling events
+  
+  /**
+   * this one has to be guaranteed to set a CG
+   */
+  @Override
+  public void setRootCG (){
+    ThreadInfo[] runnables = vm.getThreadList().getTimeoutRunnables();
+    ChoiceGenerator<ThreadInfo> cg = new ThreadChoiceFromSet( ROOT, runnables, true);
+    vm.getSystemState().setMandatoryNextChoiceGenerator( cg, "no ROOT choice generator");
+  }
+  
+  
+  //--- gov.nasa.jpf.vm.Verify
+  @Override
+  public boolean setsBeginAtomicCG (ThreadInfo ti){
+    return setNonBlockingCG( BEGIN_ATOMIC, ti);
+  }
+  
+  @Override
+  public boolean setsEndAtomicCG (ThreadInfo ti){
+    return setNonBlockingCG( END_ATOMIC, ti);
+  }
+  
+  //--- ThreadInfo reschedule request
+  @Override
+  public boolean setsRescheduleCG (ThreadInfo ti, String reason){
+    return setNonBlockingCG( reason, ti);
+  }
+  
+  //--- FinalizerThread
+  @Override
+  public boolean setsPostFinalizeCG (ThreadInfo tiFinalizer){
+    // the finalizer is already waiting at this point, i.e. it's not runnable anymore
+    return setBlockingCG( POST_FINALIZE, tiFinalizer);
+  }
+  
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/Allocation.java b/src/main/gov/nasa/jpf/vm/Allocation.java
new file mode 100644 (file)
index 0000000..28f6d56
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.OATHash;
+
+/**
+ * helper class for search global object id (SGOID) computation. This
+ * captures both allocation context and count.
+ * 
+ * NOTE: this is used as a key for associative arrays, but we do
+ * allow destructive updates via init() in order to enable key
+ * caching for lookups that don't lead to new entries. 
+ * THE CALLER HAS TO MAKE SURE init() IS NEVER CALLED ON A STORED KEY !!
+ */
+public class Allocation {
+  
+  final AllocationContext context;
+  final int count;
+  final int hash;
+  
+  public Allocation (AllocationContext context, int count){
+    this.context = context;
+    this.count = count;
+    this.hash = OATHash.hash(context.hashCode(), count);
+  }
+  
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof Allocation) {
+      Allocation other = (Allocation)o;
+      
+      if (other.hash == hash) {
+        if (other.context.equals(context)) {
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  @Override
+  public int hashCode() {
+    return hash;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/AllocationContext.java b/src/main/gov/nasa/jpf/vm/AllocationContext.java
new file mode 100644 (file)
index 0000000..c48f9df
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * abstract type that captures the context (class, thread id and callstack)
+ * of an allocation
+ * 
+ * Used for SGOID computation
+ */
+public interface AllocationContext {
+  
+  AllocationContext extend (ClassInfo ci, int anchor);
+}
diff --git a/src/main/gov/nasa/jpf/vm/AnnotationInfo.java b/src/main/gov/nasa/jpf/vm/AnnotationInfo.java
new file mode 100644 (file)
index 0000000..4c36338
--- /dev/null
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import java.util.HashMap;
+
+/**
+ * the JPF internal representation for Java Annotations
+ * 
+ * AnnotationInfos represent a separate type system. While we could have used normal ClassInfos
+ * (Java annotations are just restricted interfaces with some syntactic sugar), we keep this separate because
+ * ClassInfos would be overkill. Besides, our runtime behavior differs in that we synchronously load
+ * annotation class files when we encounter them during normal ClassInfo construction (i.e. we parse recursively),
+ * whereas a normal JVM only loads them once they are referenced. The reason why we deviate is that
+ * annotations are used more often in tools than via reflection within the SUT, i.e. they most likely will
+ * be read either by JPF or by listeners, so we want them as soon as possible to avoid additional class state.
+ * This also means we do not faithfully model ClassNotFoundExceptions on annotations due to reflection
+ * calls within the SUT, but that seems less important than having them available during ClassInfo construction.
+ * This mostly matters because of default values and inherited class annotations.
+ * 
+ * AnnotationInfo serves as the concrete type of declaration annotations, and as the base for
+ * type annotations, holding all the info that comes from the annotation class file. In the first
+ * case, AnnotationInfo instances can be shared if there are no explicit values. Sharing does not
+ * make sense for type annotations which need to store site specific target info (from the classfile).
+ * 
+ * Note - AnnotationInfos loaded by the same ClassLoader that do not have explicitly set values are shared
+ * between annotated objects
+ * 
+ */
+public class AnnotationInfo implements Cloneable {
+
+  // NOTE - never modify an Entry object since it might be shared between
+  // different instances of the same annotation type
+  public static class Entry implements Cloneable {
+    String key;
+    Object value;
+    
+    public String getKey() {
+      return key;
+    }
+
+    public Object getValue() {
+      return value;
+    }
+    
+    public Entry (String key, Object value){
+      this.key = key;
+      this.value = value;
+    }
+    
+    @Override
+       public Entry clone(){
+      try {
+        return (Entry) super.clone();
+      } catch (CloneNotSupportedException cnsx){
+        throw new JPFException("AnnotationInfo.Entry clone() failed");
+      }
+    }
+  }
+  
+  public static class EnumValue {
+    String eClassName;
+    String eConst;
+    
+    EnumValue (String clsName, String constName){
+      eClassName = clsName;
+      eConst = constName;
+    }
+    public String getEnumClassName(){
+      return eClassName;
+    }
+    public String getEnumConstName(){
+      return eConst;
+    }
+    @Override
+       public String toString(){
+      return eClassName + '.' + eConst;
+    }
+  }
+
+  public static class ClassValue {
+    String name;
+
+    ClassValue (String cn){
+      name = cn;
+    }
+
+    public String getName(){
+      return name;
+    }
+    @Override
+       public String toString(){
+      return name;
+    }
+  }
+
+  static final Entry[] NONE = new Entry[0];
+  
+  // we have to jump through a lot of hoops to handle default annotation parameter values
+  // this is not ideal, since it causes the classfile to be re-read if the SUT
+  // uses annotation reflection (which creates a ClassInfo), but this is rather
+  // exotic, so we save some time by not creating a ClassInfo (which would hold
+  // the default vals as method annotations) and directly store the default values here
+
+  static HashMap<String, AnnotationAttribute> annotationAttributes = new HashMap<String, AnnotationAttribute>();
+
+  public static class AnnotationAttribute {
+    Entry[] defaultEntries;
+    boolean isInherited;
+
+    AnnotationAttribute (Entry[] defaultEntries, boolean isInherited) {
+      this.defaultEntries = defaultEntries;
+      this.isInherited = isInherited;
+    }
+  }
+  
+  public static Object getEnumValue(String eType, String eConst){
+    return new EnumValue( Types.getClassNameFromTypeName(eType), eConst);
+  }
+
+  public static Object getClassValue(String type){
+    return new ClassValue( Types.getClassNameFromTypeName(type));
+  }  
+  
+  protected String name;
+  protected Entry[] entries;
+  protected boolean isInherited = false;
+    
+  /**
+   * this records if the associated class file has been loaded. If it isn't resolved yet,
+   * we don't know about default values, hence we need to check before retrieving field values
+   * that have not been explicitly set. Note this is search global and hence does not need to
+   * be state managed since we only check for default values, i.e. there are no side effects.
+   * Loading has to happen with the right ClassLoader though
+   */
+  protected ClassLoaderInfo classLoader; // set once it is resolved (i.e. the corresponding classfile is read)
+  
+  
+  public AnnotationInfo (String name, ClassLoaderInfo classLoader, AnnotationParser parser) throws ClassParseException {
+    this.name = name;
+    this.classLoader = classLoader;
+    
+    parser.parse(this);
+  }
+  
+  /**
+   * this is the base ctor for AbstractTypeAnnotationInfos, which add additional
+   * target information from the classfile
+   */
+  protected AnnotationInfo (AnnotationInfo exemplar){
+    this.name = exemplar.name;
+    this.classLoader = exemplar.classLoader;
+    this.entries = exemplar.entries;
+    this.isInherited = exemplar.isInherited;
+  }
+  
+  //--- the init API used by AnnotationParsers
+  public void setName (String name) throws ClassParseException {
+    if (!this.name.equals(name)){
+      throw new ClassParseException("wrong annotation name in classfile, expected " + this.name + ", found " + name);
+    }
+  }
+
+  public void setEntries (Entry[] entries){
+    this.entries = entries;
+  }
+  
+  public void setInherited (boolean isInherited){
+    this.isInherited = isInherited;
+  }
+  
+  
+  public AnnotationInfo (String name, Entry[] entries, boolean isInherited){
+    this.name = name;
+    this.entries = entries;
+    this.isInherited = isInherited;
+  }
+
+
+  public boolean isInherited (){
+    return this.isInherited;
+  }
+  
+  public ClassLoaderInfo getClassLoaderInfo(){
+    return classLoader;
+  }
+
+  public String getName() {
+    return name;
+  }
+  
+  protected AnnotationInfo cloneFor (ClassLoaderInfo cl){
+    try {
+      AnnotationInfo ai = (AnnotationInfo) clone();
+      
+      // <2do> once we support class/enum values we have to clone these too
+      
+      ai.classLoader = cl;
+      
+      return ai;
+      
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("AnnotationInfo cloneFor() failed");
+    }
+  }
+  
+  /**
+   * this returns a clone that can be used to explicitly set values.
+   * NOTE - Entry instances are still shared, i.e. to change values we have to create and set
+   * new Entry instances
+   */
+  public AnnotationInfo cloneForOverriddenValues(){
+    try {
+      AnnotationInfo ai = (AnnotationInfo) clone();
+      ai.entries = entries.clone();
+      return ai;
+      
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("AnnotationInfo cloneFor() failed");
+    }    
+  }
+  
+  public void setClonedEntryValue (String key, Object newValue){
+    for (int i=0; i<entries.length; i++){
+      if (entries[i].getKey().equals(key)){
+        entries[i] = new Entry( key, newValue);
+        return;
+      }
+    }    
+  }
+  
+  public Entry[] getEntries() {
+    return entries;
+  }
+  
+  /**
+   * this is the common getter that should trigger parsing the corresponding class file 
+   */
+  public Object getValue (String key){    
+    for (int i=0; i<entries.length; i++){
+      if (entries[i].getKey().equals(key)){
+        return entries[i].getValue();
+      }
+    }
+    return null;
+  }
+
+  
+  // convenience method for single-attribute annotations
+  public Object value() {
+    return getValue("value");
+  }
+  
+  public String valueAsString(){
+    Object v = value();
+    return (v != null) ? v.toString() : null;
+  }
+  
+  public String getValueAsString (String key){
+    Object v = getValue(key);
+    return (v != null) ? v.toString() : null;
+  }
+  
+  public String[] getValueAsStringArray() {
+    String a[] = null; 
+    Object v = value();
+    if (v != null && v instanceof Object[]) {
+      Object[] va = (Object[])v;
+      a = new String[va.length];
+      for (int i=0; i<a.length; i++) {
+        if (va[i] != null) {
+          a[i] = va[i].toString();
+        }
+      }
+    }
+    
+    return a;    
+  }
+  
+  public String[] getValueAsStringArray (String key) {
+    // <2do> not very efficient
+    String a[] = null; 
+    Object v = getValue(key);
+    if (v != null && v instanceof Object[]) {
+      Object[] va = (Object[])v;
+      a = new String[va.length];
+      for (int i=0; i<a.length; i++) {
+        if (va[i] != null) {
+          a[i] = va[i].toString();
+        }
+      }
+    }
+    
+    return a;
+  }
+  
+  public <T> T getValue (String key, Class<T> type){
+    Object v = getValue(key);
+    if (type.isInstance(v)){
+      return (T)v;
+    } else {
+      return null;
+    }
+  }
+  
+  public boolean getValueAsBoolean (String key){
+    Object v = getValue(key);
+    if (v instanceof Boolean){
+      return ((Boolean)v).booleanValue();
+    } else {
+      throw new JPFException("annotation element @" + name + '.' + key + "() not a boolean: " + v);
+    }
+  }
+  
+  public int getValueAsInt (String key){
+    Object v = getValue(key);
+    if (v instanceof Integer){
+      return ((Integer)v).intValue();
+    } else {
+      throw new JPFException("annotation element @" + name + '.' + key + "() not an int: " + v);
+    }
+  }
+
+  public long getValueAsLong (String key){
+    Object v = getValue(key);
+    if (v instanceof Long){
+      return ((Long)v).longValue();
+    } else {
+      throw new JPFException("annotation element @" + name + '.' + key + "() not a long: " + v);
+    }
+  }
+
+  public float getValueAsFloat (String key){
+    Object v = getValue(key);
+    if (v instanceof Float){
+      return ((Float)v).floatValue();
+    } else {
+      throw new JPFException("annotation element @" + name + '.' + key + "() not a float: " + v);
+    }
+  }
+  
+  public double getValueAsDouble (String key){
+    Object v = getValue(key);
+    if (v instanceof Double){
+      return ((Double)v).doubleValue();
+    } else {
+      throw new JPFException("annotation element @" + name + '.' + key + "() not a double: " + v);
+    }
+  }
+  
+  public String asString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append('@');
+    sb.append(name);
+    sb.append('[');
+    for (int i=0; i<entries.length; i++){
+      if (i > 0){
+        sb.append(',');
+      }
+      sb.append(entries[i].getKey());
+      sb.append('=');
+      sb.append(entries[i].getValue());
+    }
+    sb.append(']');
+    
+    return sb.toString();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/AnnotationParser.java b/src/main/gov/nasa/jpf/vm/AnnotationParser.java
new file mode 100644 (file)
index 0000000..9cfecbb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a parser for annotation class files
+ */
+public interface AnnotationParser {
+
+  void parse (AnnotationInfo ai) throws ClassParseException;
+}
diff --git a/src/main/gov/nasa/jpf/vm/ApplicationContext.java b/src/main/gov/nasa/jpf/vm/ApplicationContext.java
new file mode 100644 (file)
index 0000000..4adc42c
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.SystemAttribute;
+import gov.nasa.jpf.util.IntTable;
+
+/**
+ * auxiliary class that captures the main entry and classloader context
+ * of applications
+ */
+public class ApplicationContext implements SystemAttribute {
+
+  final int id;
+  final String mainClassName;
+  final String mainEntry;
+  final String[] args;
+  final String host;
+  
+  final SystemClassLoaderInfo sysCl;
+  MethodInfo miEntry;
+  
+  FinalizerThreadInfo finalizerThread;
+  IntTable<String> internStrings;
+  
+  ApplicationContext (int id, String mainClassName, String mainEntry, String[] args, String host, SystemClassLoaderInfo sysCl){
+    this.id = id;
+    this.mainClassName = mainClassName;
+    this.mainEntry = mainEntry;
+    this.args = args;
+    this.host = host;
+    this.sysCl = sysCl;
+    this.internStrings = new IntTable<String>(8);
+  }
+  
+  void setEntryMethod (MethodInfo miEntry){
+    this.miEntry = miEntry;
+  }
+  
+  MethodInfo getEntryMethod(){
+    return miEntry;
+  }
+  
+  public int getId(){
+    return id;
+  }
+  
+  public String getMainClassName(){
+    return mainClassName;
+  }
+  
+  public String getHost() {
+    return host;
+  }
+  
+  public String[] getArgs(){
+    return args;
+  }
+  
+  public SystemClassLoaderInfo getSystemClassLoader(){
+    return sysCl;
+  }
+  
+  public FinalizerThreadInfo getFinalizerThread() {
+    return finalizerThread;
+  }
+  
+  public void setFinalizerThread(ThreadInfo ti) {
+    finalizerThread = (FinalizerThreadInfo)ti;
+  }
+  
+  public IntTable<String> getInternStrings() {
+    return internStrings;
+  }
+  
+  @Override
+  public String toString(){
+    StringBuffer sb = new StringBuffer();
+    sb.append("ApplicationContext {mainClassName=");
+    sb.append(mainClassName);
+    sb.append(",mainEntry=");
+    sb.append(mainEntry);
+    sb.append(",host=");
+    sb.append(host);
+    
+    sb.append(",args=[");
+    for (int i=0; i<args.length; i++){
+      if (i>0) sb.append(',');
+      sb.append(args[i]);
+    }
+    sb.append("], miMain=");
+    if (miEntry != null){
+      sb.append(miEntry.getFullName());
+    } else {
+      sb.append("null");
+    }
+    sb.append('}');
+    
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ArrayAccess.java b/src/main/gov/nasa/jpf/vm/ArrayAccess.java
new file mode 100644 (file)
index 0000000..74dd67c
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * CG attribute to store array references of aload/astore operations
+ */
+public class ArrayAccess {
+  int aref;        // the array reference
+  int index;       // the index of the reference
+  boolean isRead;
+
+  public ArrayAccess (int aref, int index, boolean isRead){
+    this.aref = aref;
+    this.index = index;
+    this.isRead = isRead;
+  }
+
+  public int getAref() {
+    return aref;
+  }
+
+  public int getIndex() {
+    return index;
+  }
+
+  public boolean isRead() {
+    return isRead;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ArrayFields.java b/src/main/gov/nasa/jpf/vm/ArrayFields.java
new file mode 100644 (file)
index 0000000..971a075
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+import java.io.PrintStream;
+
+
+
+/**
+ *a Field (data value) store for array objects
+ */
+public abstract class ArrayFields extends Fields {
+
+  int getNumberOfFieldsOrElements () {
+    return arrayLength(); // we have no fields
+  }
+
+  public abstract int arrayLength ();
+
+  @Override
+  public abstract int getHeapSize ();
+
+  @Override
+  public boolean isReferenceArray(){
+    return false;
+  }
+
+  public int getNumberOfFields() {
+    // has none
+    return 0;
+  } 
+  
+  public abstract void copyElements (ArrayFields src, int srcPos, int dstPos, int len);
+  
+  public void printElements( PrintStream ps, int max){
+    int len = arrayLength();
+    if (max < 0) max = len;
+    int i;
+    for (i=0; i<max; i++){
+      if (i>0){
+        ps.print(',');
+      }
+      printValue(ps, i);
+    }
+    if (i < len){
+      ps.print("...");
+    }
+  }  
+  
+  protected abstract void printValue (PrintStream ps, int idx);
+  
+  public abstract Object getValues();
+
+  @Override
+  public boolean getBooleanValue (int pos) {
+  // overridden by subclass
+      throw new JPFException( "not a boolean[]");
+  }
+  @Override
+  public byte getByteValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a byte[]");
+  }
+  @Override
+  public char getCharValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a char[]");
+  }
+  @Override
+  public short getShortValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a short[]");
+  }
+  @Override
+  public int getIntValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not an int[]");
+  }
+  @Override
+  public long getLongValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a long[]");
+  }
+  @Override
+  public float getFloatValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a float[]");
+  }
+  @Override
+  public double getDoubleValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a double[]");
+  }
+  @Override
+  public int getReferenceValue (int pos) {
+    // overridden by subclass
+    throw new JPFException( "not a reference array");
+  }
+
+  @Override
+  public void setBooleanValue (int pos, boolean newValue) {
+    // overridden by subclass
+    throw new JPFException( "not a boolean[]");
+  }
+  @Override
+  public void setByteValue (int pos, byte newValue) {
+    // overridden by subclass
+    throw new JPFException( "not a byte[]");
+  }
+  @Override
+  public void setCharValue (int pos, char newValue) {
+    // overridden by subclass
+    throw new JPFException( "not a char[]");
+  }
+  @Override
+  public void setShortValue (int pos, short newValue) {
+    // overridden by subclass
+    throw new JPFException( "not a short[]");
+  }
+  @Override
+  public void setIntValue (int pos, int newValue) {
+    // overridden by subclass
+    throw new JPFException( "not an int[]");
+  }
+  @Override
+  public void setFloatValue (int pos, float newValue){
+    // overridden by subclass
+    throw new JPFException( "not a float[]");
+  }
+  @Override
+  public void setLongValue (int pos, long newValue) {
+    // overridden by subclass
+    throw new JPFException( "not a long[]");
+  }
+  @Override
+  public void setDoubleValue (int pos, double newValue){
+    // overridden by subclass
+    throw new JPFException( "not a double[]");
+  }
+  @Override
+  public void setReferenceValue (int pos, int newValue){
+    // overridden by subclass
+    throw new JPFException( "not a reference array");
+  }
+
+
+  public boolean[] asBooleanArray () {
+    // overridden by subclass
+    throw new JPFException("not a boolean[]");
+  }
+  public byte[] asByteArray () {
+    // overridden by subclass
+    throw new JPFException("not a byte[]");
+  }
+  public char[] asCharArray () {
+    // overridden by subclass
+    throw new JPFException("not a char[]");
+  }
+  public char[] asCharArray (int offset, int length) {
+    // overridden by subclass
+    throw new JPFException("not a char[]");
+  }
+  public short[] asShortArray () {
+    // overridden by subclass
+    throw new JPFException("not a short[]");
+  }
+  public int[] asIntArray () {
+    // overridden by subclass
+    throw new JPFException("not a int[]");
+  }
+  public int[] asReferenceArray () {
+    // overridden by subclass
+    throw new JPFException("not a reference array");
+  }
+  public long[] asLongArray () {
+    // overridden by subclass
+    throw new JPFException("not a long[]");
+  }
+  public float[] asFloatArray () {
+    // overridden by subclass
+    throw new JPFException("not a float[]");
+  }
+  public double[] asDoubleArray () {
+    // overridden by subclass
+    throw new JPFException("not a double[]");
+  }
+
+  @Override
+  public int[] asFieldSlots() {
+    throw new JPFException("array has no field slots");
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/ArrayIndexOutOfBoundsExecutiveException.java b/src/main/gov/nasa/jpf/vm/ArrayIndexOutOfBoundsExecutiveException.java
new file mode 100644 (file)
index 0000000..3b4897a
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+/**
+ * @author flerda
+ *
+ * <2do> Check usage! If this is used to intercept AIOBX in the target app,
+ * this is a BAD example of using exceptions for general control flow.
+ * If this is used for internal AIOBX then it should be just a JPFException
+ */
+@SuppressWarnings("serial")
+public class ArrayIndexOutOfBoundsExecutiveException extends JPFException {
+  private Instruction i;
+
+  public ArrayIndexOutOfBoundsExecutiveException (Instruction i) {
+    super("array index out of bounds");
+    this.i = i;
+  }
+
+  public ArrayIndexOutOfBoundsExecutiveException (Instruction i, String msg) {
+    super(msg);
+    this.i = i;
+  }
+
+  public Instruction getInstruction () {
+    return i;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ArrayOffset.java b/src/main/gov/nasa/jpf/vm/ArrayOffset.java
new file mode 100644 (file)
index 0000000..0a956d9
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * data encapsulation for backtracking. It's actually a Stream (offset being
+ * incremented by get()) so it probably should be renamed
+ */
+public final class ArrayOffset {
+  public int[] data;
+  int          offset;
+
+  public ArrayOffset (int[] d) {
+    data = d;
+    offset = 0;
+  }
+
+  public void advance(int n) {
+    offset += n;
+  }
+  
+  public int get () {
+    return data[offset++];
+  }
+
+  public int peek () {
+    return data[offset];
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/AtomicData.java b/src/main/gov/nasa/jpf/vm/AtomicData.java
new file mode 100644 (file)
index 0000000..e7c7681
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+
+
+/**
+ * helper object to store per thread information about atomic line
+ * execution
+ * <2do> check if we can't do this less expensive. atomic-lines is not
+ * the default anymore
+ */
+public class AtomicData {
+  /**
+   * The method in which the line step started.
+   */
+  public MethodInfo currentMethod;
+
+  /**
+   * The line at which the line step started.
+   */
+  public int line;
+
+  /**
+   * Set to true if we still are in the same method in which we were
+   * when the line step started.
+   */
+  public boolean inSameMethod;
+
+  @Override
+  public Object clone () {
+    AtomicData a = new AtomicData();
+
+    a.currentMethod = currentMethod;
+    a.line = line;
+    a.inSameMethod = inSameMethod;
+
+    return a;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o == null) {
+      return false;
+    }
+
+    if (!(o instanceof AtomicData)) {
+      return false;
+    }
+
+    AtomicData a = (AtomicData) o;
+
+    if (currentMethod != a.currentMethod) {
+      return false;
+    }
+
+    if (line != a.line) {
+      return false;
+    }
+
+    if (inSameMethod != a.inSameMethod) {
+      return false;
+    }
+
+    return true;
+  }
+
+  /**
+   * Computes a hash code with the object data.
+   */
+  public void hash (HashData hd) {
+    hd.add(line);
+    hd.add(inSameMethod ? 1 : 0);
+  }
+
+  /**
+   * Returns a hash code for the object.
+   */
+  @Override
+  public int hashCode () {
+    HashData hd = new HashData();
+
+    hash(hd);
+
+    return hd.getValue();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/Attributor.java b/src/main/gov/nasa/jpf/vm/Attributor.java
new file mode 100644 (file)
index 0000000..474d8b7
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface to configure various method and field attributes at class
+ * load time
+ */
+public interface Attributor {
+  void setAttributes (ClassInfo ci);
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/Backtracker.java b/src/main/gov/nasa/jpf/vm/Backtracker.java
new file mode 100644 (file)
index 0000000..9754944
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+public interface Backtracker {
+  static interface RestorableState {};
+  
+  void attach(VM vm);
+  
+  // the backtracking interface (depth first search only)
+  boolean backtrack ();
+  void pushSystemState (); 
+  void pushKernelState ();
+  
+  // the general restore interface
+  RestorableState getRestorableState();
+  void restoreState (RestorableState bs);
+}
diff --git a/src/main/gov/nasa/jpf/vm/BooleanArrayFields.java b/src/main/gov/nasa/jpf/vm/BooleanArrayFields.java
new file mode 100644 (file)
index 0000000..b899afc
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for boolean[] objects
+ */
+public class BooleanArrayFields extends ArrayFields {
+
+  boolean[] values;
+
+  public BooleanArrayFields (int length) {
+    values = new boolean[length];
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    BooleanArrayFields a = (BooleanArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+  
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx] ? 't' : 'f');
+  }
+
+  @Override
+  public boolean[] asBooleanArray() {
+    return values;
+  }
+
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {
+    return values.length * 4;
+  }
+
+  /**
+   * we check for type and equal element values
+   */
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof BooleanArrayFields) {
+      BooleanArrayFields other = (BooleanArrayFields)o;
+
+      boolean[] v = values;
+      boolean[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public BooleanArrayFields clone(){
+    BooleanArrayFields f = (BooleanArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  // for serialization
+  @Override
+  public void appendTo(IntVector v) {
+    v.appendPacked(values);
+  }
+
+  @Override
+  public boolean getBooleanValue (int pos) {
+    return values[pos];
+  }
+
+  @Override
+  public void setBooleanValue (int pos, boolean v) {
+    values[pos] = v;
+  }
+
+  @Override
+  public void hash (HashData hd) {
+    boolean[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/BooleanChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/BooleanChoiceGenerator.java
new file mode 100644 (file)
index 0000000..381212b
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * a pretty simple ChoiceGenerator that returns a boolean
+ * there is not much use in having a CG type interface (such as
+ * IntChoiceGenerator) since there is hardly a need for a generic type hierarchy
+ * of BooleanChoiceGenerator subtypes - what else can you do with true/false
+ */
+public class BooleanChoiceGenerator extends ChoiceGeneratorBase<Boolean> {
+
+  // do we evaluate [false, true] or [true, false]
+  protected boolean falseFirst = true;
+
+  protected int count = -1;
+  protected boolean next;
+  
+  public BooleanChoiceGenerator(Config conf, String id) {
+    super(id);
+
+    falseFirst = conf.getBoolean("cg.boolean.false_first", true);
+    next = falseFirst;
+  }
+
+  public BooleanChoiceGenerator (String id) {
+    super(id);
+    next = falseFirst;
+  }
+
+  public BooleanChoiceGenerator( String id, boolean falseFirst ){
+    super(id);
+    
+    this.falseFirst = falseFirst;
+    next = falseFirst;
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    return !isDone && (count < 1);
+  }
+
+  @Override
+  public Boolean getNextChoice () {
+    return next ? Boolean.TRUE : Boolean.FALSE;
+  }
+  
+  @Override
+  public Class<Boolean> getChoiceType() {
+    return Boolean.class;
+  }
+
+  @Override
+  public void advance () {
+    if (count < 1) {
+      count++;
+      next = !next;
+    }
+  }
+  
+  @Override
+  public Boolean getChoice (int idx){
+    if (idx == 0){
+      return falseFirst ? Boolean.FALSE : Boolean.TRUE;
+    } else if (idx == 1){
+      return falseFirst ? Boolean.TRUE : Boolean.FALSE;      
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+  @Override
+  public void reset () {
+    count = -1;
+    next = falseFirst;
+
+    isDone = false;
+  }
+  
+  @Override
+  public int getTotalNumberOfChoices () {
+    return 2;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return (count+1);
+  }
+  
+  // that is pretty stupid, but for the sake of consistency we make it available
+  Boolean[] getChoices(){
+    Boolean[] vals = new Boolean[2];
+    vals[0] = !falseFirst;
+    vals[1] = falseFirst;
+    
+    return vals;
+  }
+
+  // not much use to support reordering, we just have two elements so reverse() will do
+  
+  public boolean isFalseFirst(){
+    return falseFirst;
+  }
+  
+  /**
+   *  note this should only be called before the first advance since it resets
+   *  the enumeration state 
+   */
+  public void reverse(){
+    falseFirst = !falseFirst;
+    reset();
+  }
+  
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append('[');
+    sb.append("[id=\"");
+    sb.append(id);
+    sb.append('"');
+
+    sb.append(",isCascaded:");
+    sb.append(isCascaded);
+
+    sb.append(",{");
+
+    if (count < 0){
+      sb.append(!next);
+      sb.append(',');
+      sb.append(next);
+    } else if (count == 0) {
+      sb.append(MARKER);
+      sb.append(next);
+      sb.append(',');
+      sb.append(!next);
+    } else {
+      sb.append(!next);
+      sb.append(',');
+      sb.append(MARKER);
+      sb.append(next);
+    }
+    sb.append("}]");
+    return sb.toString();
+  }
+  
+  @Override
+  public BooleanChoiceGenerator randomize () {
+    next = random.nextBoolean();
+    return this;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/BooleanFieldInfo.java b/src/main/gov/nasa/jpf/vm/BooleanFieldInfo.java
new file mode 100644 (file)
index 0000000..6b36817
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * fieldinfo for slots holding booleans
+ */
+public class BooleanFieldInfo extends SingleSlotFieldInfo {
+
+  boolean init=false;
+
+  public BooleanFieldInfo (String name, int modifiers) {
+     super(name, "Z", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Integer){
+      cv = constValue;
+      init = ((Integer)constValue).intValue() == 1;
+      
+    } else {
+      throw new JPFException("illegal boolean ConstValue=" + constValue);
+    }
+  }
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setBooleanValue(storageOffset, init);
+  }
+
+  @Override
+  public boolean isBooleanField() {
+    return true;
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    return new Boolean(i != 0);
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    boolean b = f.getBooleanValue(storageOffset);
+    return Boolean.toString(b);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java b/src/main/gov/nasa/jpf/vm/BootstrapMethodInfo.java
new file mode 100644 (file)
index 0000000..fbb6bf8
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *
+ * For now, this is only used to capture boostrap methods for lambda expression,
+ * which link the method representing the lambda body to a single abstract method 
+ * (SAM) declared in a functional interface. References to bootstrap methods are
+ * provided by the invokedynamic bytecode instruction. 
+ */
+public class BootstrapMethodInfo {
+  
+  int lambdaRefKind;
+  
+  // method capturing lambda body to be linked to the function method of function object
+  MethodInfo lambdaBody;
+  
+  // class containing lamabda expression
+  ClassInfo enclosingClass;
+  
+  // descriptor of a SAM declared within the functional interface   
+  String samDescriptor;
+  
+  public BootstrapMethodInfo(int lambdaRefKind, ClassInfo enclosingClass, MethodInfo lambdaBody, String samDescriptor) {
+    this.lambdaRefKind = lambdaRefKind;
+    this.enclosingClass = enclosingClass;
+    this.lambdaBody = lambdaBody;
+    this.samDescriptor = samDescriptor;
+  }
+  
+  @Override
+  public String toString() {
+    return "BootstrapMethodInfo[" + enclosingClass.getName() + "." + lambdaBody.getBaseName() + 
+        "[SAM descriptor:" + samDescriptor + "]]";
+  }
+  
+  public MethodInfo getLambdaBody() {
+    return lambdaBody;
+  }
+  
+  public String getSamDescriptor() {
+    return samDescriptor;
+  }
+  
+  public int getLambdaRefKind () {
+    return lambdaRefKind;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/BoxObjectCacheManager.java b/src/main/gov/nasa/jpf/vm/BoxObjectCacheManager.java
new file mode 100644 (file)
index 0000000..f0f498a
--- /dev/null
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ *         Cache management implementation for the types Boolean, Byte,
+ *         Character, Short, Integer, Long. The references to the caches are in
+ *         the class classes/gov/nasa/jpf/BoxObjectCaches.
+ * 
+ *         All the caches, except Boolean, are initialized on the first
+ *         invocation of valueOf(), and they all exempt from garbage collection.
+ * 
+ *         NOTE: All classes obtained from getResolvedClassInfo in
+ *         BoxObjectCacheManager are safe, and there is no need to check if they
+ *         are initialized. The wrappers and BoxObjectCaches are initialized in
+ *         VM.intialize(), and there are no clinit for array classes.
+ *         
+ *         NOTE: the initXCache allocations are system allocations, whereas the
+ *         valueOfX() allocations are rooted in SUT code
+ */
+public class BoxObjectCacheManager {
+  private static final String MODEL_CLASS = "gov.nasa.jpf.BoxObjectCaches";
+  private static final int ANCHOR = BoxObjectCacheManager.class.getName().hashCode();  
+  
+  // cache default bounds
+  private static int defLow = -128;
+  private static int defHigh = 127;
+
+  public static int valueOfBoolean (ThreadInfo ti, boolean b) {
+    ClassInfo cls = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Boolean");
+
+    int boolObj;
+    if (b) {
+      boolObj = cls.getStaticElementInfo().getReferenceField("TRUE");
+    } else {
+      boolObj = cls.getStaticElementInfo().getReferenceField("FALSE");
+    }
+
+    return boolObj;
+  }
+
+  // Byte cache bounds
+  private static byte byteLow;
+  private static byte byteHigh;
+
+  public static int initByteCache (ThreadInfo ti) {
+    byteLow = (byte) ti.getVM().getConfig().getInt("vm.cache.low_byte", defLow);
+    byteHigh = (byte) ti.getVM().getConfig().getInt("vm.cache.high_byte", defHigh);
+    int n = (byteHigh - byteLow) + 1;
+    
+    Heap heap = ti.getHeap();
+    ElementInfo eiArray = heap.newSystemArray("Ljava/lang/Byte", n, ti, ANCHOR);
+    int arrayRef = eiArray.getObjectRef();
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Byte");
+    byte val = byteLow;
+    for (int i = 0; i < n; i++) {
+      ElementInfo eiByte = heap.newSystemObject(ci, ti, ANCHOR);
+      eiByte.setByteField("value", val++);
+      eiArray.setReferenceElement(i, eiByte.getObjectRef());
+    }
+
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    cacheClass.getModifiableStaticElementInfo().setReferenceField("byteCache", arrayRef);
+    return arrayRef;
+  }
+
+  public static int valueOfByte (ThreadInfo ti, byte b) {
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    int byteCache = cacheClass.getStaticElementInfo().getReferenceField("byteCache");
+
+    if (byteCache == MJIEnv.NULL) { // initializing the cache on demand
+      byteCache = initByteCache(ti);
+    }
+
+    if (b >= byteLow && b <= byteHigh) { return ti.getElementInfo(byteCache).getReferenceElement(b - byteLow); }
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Byte");
+    ElementInfo eiByte = ti.getHeap().newObject(ci, ti);
+    eiByte.setByteField("value", b);
+    return eiByte.getObjectRef();
+  }
+
+  // Character cache bound
+  private static int charHigh;
+
+  public static int initCharCache (ThreadInfo ti) {
+    charHigh = ti.getVM().getConfig().getInt("vm.cache.high_char", defHigh);
+    int n = charHigh + 1;
+    
+    Heap heap = ti.getHeap();    
+    ElementInfo eiArray = heap.newSystemArray("Ljava/lang/Character", n, ti, ANCHOR);
+    int arrayRef = eiArray.getObjectRef();
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Character");
+    for (int i = 0; i < n; i++) {
+      ElementInfo eiChar = heap.newSystemObject(ci, ti, ANCHOR);
+      eiChar.setCharField("value", (char) i);
+      eiArray.setReferenceElement(i, eiChar.getObjectRef());
+    }
+
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    cacheClass.getModifiableStaticElementInfo().setReferenceField("charCache", arrayRef);
+    return arrayRef;
+  }
+
+  public static int valueOfCharacter (ThreadInfo ti, char c) {
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    int charCache = cacheClass.getStaticElementInfo().getReferenceField("charCache");
+
+    if (charCache == MJIEnv.NULL) { // initializing the cache on demand
+      charCache = initCharCache(ti);
+    }
+
+    if (c >= 0 && c <= charHigh) { return ti.getElementInfo(charCache).getReferenceElement(c); }
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Character");
+    ElementInfo eiChar = ti.getHeap().newObject(ci, ti);
+    eiChar.setCharField("value", c);
+    return eiChar.getObjectRef();
+  }
+
+  // Short cache bounds
+  private static short shortLow;
+
+  private static short shortHigh;
+
+  public static int initShortCache (ThreadInfo ti) {
+    shortLow = (short) ti.getVM().getConfig().getInt("vm.cache.low_short", defLow);
+    shortHigh = (short) ti.getVM().getConfig().getInt("vm.cache.high_short", defHigh);
+    int n = (shortHigh - shortLow) + 1;
+    
+    Heap heap = ti.getHeap();    
+    ElementInfo eiArray = heap.newSystemArray("Ljava/lang/Short", n, ti, ANCHOR);
+    int arrayRef = eiArray.getObjectRef();
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Short");
+    short val = shortLow;
+    for (int i = 0; i < n; i++) {
+      ElementInfo eiShort = heap.newSystemObject(ci, ti, ANCHOR);
+      eiShort.setShortField("value", val++);
+      eiArray.setReferenceElement(i, eiShort.getObjectRef());
+    }
+
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    cacheClass.getModifiableStaticElementInfo().setReferenceField("shortCache", arrayRef);
+    return arrayRef;
+  }
+
+  public static int valueOfShort (ThreadInfo ti, short s) {
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    int shortCache = cacheClass.getStaticElementInfo().getReferenceField("shortCache");
+
+    if (shortCache == MJIEnv.NULL) { // initializing the cache on demand
+      shortCache = initShortCache(ti);
+    }
+
+    if (s >= shortLow && s <= shortHigh) { return ti.getElementInfo(shortCache).getReferenceElement(s - shortLow); }
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Short");
+    ElementInfo eiShort = ti.getHeap().newObject(ci, ti);
+    eiShort.setShortField("value", s);
+    return eiShort.getObjectRef();
+  }
+
+  // Integer cache bounds
+  private static int intLow;
+  private static int intHigh;
+
+  public static int initIntCache (ThreadInfo ti) {
+    intLow = ti.getVM().getConfig().getInt("vm.cache.low_int", defLow);
+    intHigh = ti.getVM().getConfig().getInt("vm.cache.high_int", defHigh);
+    int n = (intHigh - intLow) + 1;
+    
+    Heap heap = ti.getHeap();    
+    ElementInfo eiArray = heap.newSystemArray("Ljava/lang/Integer", n, ti, ANCHOR);
+    int arrayRef = eiArray.getObjectRef();
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Integer");
+    for (int i = 0; i < n; i++) {
+      ElementInfo eiInteger = heap.newSystemObject(ci, ti, ANCHOR);
+      eiInteger.setIntField("value", i + intLow);
+      eiArray.setReferenceElement(i, eiInteger.getObjectRef());
+    }
+
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    cacheClass.getModifiableStaticElementInfo().setReferenceField("intCache", arrayRef);
+    return arrayRef;
+  }
+
+  public static int valueOfInteger (ThreadInfo ti, int i) {
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    int intCache = cacheClass.getStaticElementInfo().getReferenceField("intCache");
+
+    if (intCache == MJIEnv.NULL) { // initializing the cache on demand
+      intCache = initIntCache(ti);
+    }
+
+    if (i >= intLow && i <= intHigh) { return ti.getElementInfo(intCache).getReferenceElement(i - intLow); }
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Integer");
+    ElementInfo eiInteger = ti.getHeap().newObject(ci, ti);
+    eiInteger.setIntField("value", i);
+    return eiInteger.getObjectRef();
+  }
+
+  // Long cache bounds
+  private static int longLow;
+  private static int longHigh;
+
+  public static int initLongCache (ThreadInfo ti) {
+    longLow = ti.getVM().getConfig().getInt("vm.cache.low_long", defLow);
+    longHigh = ti.getVM().getConfig().getInt("vm.cache.high_long", defHigh);
+    int n = (longHigh - longLow) + 1;
+    
+    Heap heap = ti.getHeap();    
+    ElementInfo eiArray = heap.newSystemArray("Ljava/lang/Long", n, ti, ANCHOR);
+    int arrayRef = eiArray.getObjectRef();
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Long");
+    for (int i = 0; i < n; i++) {
+      ElementInfo eiLong = heap.newSystemObject(ci, ti, ANCHOR);
+      eiLong.setLongField("value", i + longLow);
+      eiArray.setReferenceElement(i, eiLong.getObjectRef());
+    }
+
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    cacheClass.getModifiableStaticElementInfo().setReferenceField("longCache", arrayRef);
+    return arrayRef;
+  }
+
+  public static int valueOfLong (ThreadInfo ti, long l) {
+    ClassInfo cacheClass = ClassLoaderInfo.getSystemResolvedClassInfo(MODEL_CLASS);
+    int longCache = cacheClass.getStaticElementInfo().getReferenceField("longCache");
+
+    if (longCache == MJIEnv.NULL) { // initializing the cache on demand
+      longCache = initLongCache(ti);
+    }
+
+    if (l >= longLow && l <= longHigh) { return ti.getElementInfo(longCache).getReferenceElement((int) l - longLow); }
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Long");
+    ElementInfo eiLong = ti.getHeap().newObject(ci, ti);
+    eiLong.setLongField("value", l);
+    return eiLong.getObjectRef();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ByteArrayFields.java b/src/main/gov/nasa/jpf/vm/ByteArrayFields.java
new file mode 100644 (file)
index 0000000..61a1e21
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for byte[] objects
+ */
+public class ByteArrayFields extends ArrayFields {
+
+  byte[] values;
+
+  public ByteArrayFields (int length) {
+    values = new byte[length];
+  }
+
+  @Override
+  public byte[] asByteArray() {
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    ByteArrayFields a = (ByteArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+  
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {
+    return values.length;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof ByteArrayFields) {
+      ByteArrayFields other = (ByteArrayFields)o;
+
+      byte[] v = values;
+      byte[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public ByteArrayFields clone(){
+    ByteArrayFields f = (ByteArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  @Override
+  public void setByteValue (int pos, byte b) {
+    values[pos] = b;
+  }
+
+  @Override
+  public byte getByteValue (int pos) {
+    return values[pos];
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendPacked(values);
+  }
+
+  @Override
+  public void hash(HashData hd) {
+    byte[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/ByteFieldInfo.java b/src/main/gov/nasa/jpf/vm/ByteFieldInfo.java
new file mode 100644 (file)
index 0000000..41f17dd
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * fieldinfo for slots holding bytes
+ */
+public class ByteFieldInfo extends SingleSlotFieldInfo {
+
+  byte init;
+
+  public ByteFieldInfo (String name, int modifiers) {
+     super(name, "B", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Integer){
+      cv = constValue;
+      init = ((Integer)constValue).byteValue();
+
+    } else {
+      throw new JPFException("illegal byte ConstValue=" + constValue);
+    }
+  }
+
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setByteValue(storageOffset, init);
+  }
+
+  @Override
+  public boolean isByteField() {
+    return true;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    byte i = f.getByteValue(storageOffset);
+    return Byte.toString(i);
+  }
+
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    return new Byte((byte)i);
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/BytecodeAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/BytecodeAnnotationInfo.java
new file mode 100644 (file)
index 0000000..8db37fe
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for type instructions such as instanceof, new, cast
+ */
+public class BytecodeAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int offset;
+  
+  public BytecodeAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath, int offset) {
+    super(base, targetType, typePath);
+    
+    this.offset = offset;
+  }
+  
+  public int getOffset(){
+    return offset;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/BytecodeTypeParameterAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/BytecodeTypeParameterAnnotationInfo.java
new file mode 100644 (file)
index 0000000..6153177
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for instructions that take type parameters, such
+ * as invoke insns for generic methods
+ */
+public class BytecodeTypeParameterAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int offset;
+  protected int typeParameterIndex;
+  
+  public BytecodeTypeParameterAnnotationInfo (AnnotationInfo base, 
+                                  int targetType, short[] typePath, int offset, int typeParameterIndex) {
+    super( base, targetType, typePath);
+    
+    this.offset = offset;
+    this.typeParameterIndex = typeParameterIndex;
+  }
+  
+  public int getOffset(){
+    return offset;
+  }
+  
+  public int getTypeParameterIndex(){
+    return typeParameterIndex;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/CharArrayFields.java b/src/main/gov/nasa/jpf/vm/CharArrayFields.java
new file mode 100644 (file)
index 0000000..e99111b
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.PrintUtils;
+
+import java.io.PrintStream;
+
+/**
+ * element values for char[] objects
+ */
+public class CharArrayFields extends ArrayFields {
+
+  char[] values;
+
+  public CharArrayFields (int length) {
+    values = new char[length];
+  }
+
+  @Override
+  public char[] asCharArray(){
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    CharArrayFields a = (CharArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+  
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    PrintUtils.printCharLiteral(ps, values[idx]);
+  }
+  
+  @Override
+  public void printElements( PrintStream ps, int max){
+    PrintUtils.printStringLiteral(ps, values, max);
+  }  
+  
+  @Override
+  public char[] asCharArray (int offset, int length) {
+    char[] result = new char[length];
+    System.arraycopy(values, offset, result, 0, length);
+
+    return result;
+  }
+
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 2;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendPacked(values);
+  }
+
+  @Override
+  public CharArrayFields clone(){
+    CharArrayFields f = (CharArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof CharArrayFields) {
+      CharArrayFields other = (CharArrayFields)o;
+
+      char[] v = values;
+      char[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public char getCharValue(int pos) {
+    return values[pos];
+  }
+
+  @Override
+  public void setCharValue(int pos, char newValue) {
+    values[pos] = newValue;
+  }
+
+  public void setCharValues(char[] v){
+    System.arraycopy(v,0,values,0,v.length);
+  }
+
+  //--- some methods to ease native String operations
+
+  public String asString(int offset, int length) {
+    return new String(values, offset, length);
+  }
+
+  // a special string compare utility
+  public boolean equals (int offset, int length, String s) {
+    char[] v = values;
+
+    if (offset+length > v.length) {
+      return false;
+    }
+
+    for (int i=offset, j=0; j<length; i++, j++) {
+      if (v[i] != s.charAt(j)) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  @Override
+  public void hash(HashData hd) {
+    char[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/CharFieldInfo.java b/src/main/gov/nasa/jpf/vm/CharFieldInfo.java
new file mode 100644 (file)
index 0000000..53c10b6
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * fieldinfo for slots holding chars
+ */
+public class CharFieldInfo extends SingleSlotFieldInfo {
+
+  char init;
+
+  public CharFieldInfo (String name, int modifiers) {
+    super(name, "C", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Integer){
+      cv = constValue;
+      init = (char) ((Integer)constValue).shortValue();
+
+    } else {
+      throw new JPFException("illegal char ConstValue=" + constValue);
+    }
+  }
+
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setCharValue(storageOffset, init);
+  }
+
+  @Override
+  public boolean isCharField() {
+    return true;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    char[] buf = new char[1];
+    buf[0] = f.getCharValue(storageOffset);
+    return new String(buf);
+  }
+
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    return new Character((char)i);
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/CheckExtendTransition.java b/src/main/gov/nasa/jpf/vm/CheckExtendTransition.java
new file mode 100644 (file)
index 0000000..be232e4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.SystemAttribute;
+
+/**
+ * system attribute to dynamically mark ChoiceGenerators for transition extension checks
+ */
+public class CheckExtendTransition implements SystemAttribute {
+  
+  static final CheckExtendTransition singleton = new CheckExtendTransition();
+  
+  public static void mark (ChoiceGenerator<?> cg){
+    cg.addAttr(singleton);
+  }
+  
+  public static boolean isMarked (ChoiceGenerator<?> cg){
+    return (cg != null) && cg.hasAttr(CheckExtendTransition.class);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/ChoiceGenerator.java
new file mode 100644 (file)
index 0000000..925a10d
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.ObjectList;
+
+import java.util.Comparator;
+
+/**
+ * generic interface for configurable choice generators
+ */
+public interface ChoiceGenerator<T> extends Cloneable {
+
+  //--- the basic ChoiceGenerator interface, mostly used by SystemState
+
+  T getNextChoice();
+
+  Class<T> getChoiceType();
+
+  boolean hasMoreChoices();
+
+  /**
+   * to be called before the first advance(). Can be used in implementors to
+   * initialize choices from context (similar to what listeners can do from
+   * choiceGeneratorSet() notifications)
+   */
+  void setCurrent();
+  
+  /**
+   * advance to the next choice. This is the only method that really
+   * advances our enumeration
+   */
+  void advance();
+
+  void advance(int nChoices);
+
+  void select(int nChoice);
+
+  boolean isDone();
+
+  void setDone();
+
+  boolean isProcessed();
+
+  /**
+   *  this has to reset the CG to its initial state, which includes resetting
+   * 'isDone'
+   */
+  void reset();
+
+  int getTotalNumberOfChoices();
+
+  int getProcessedNumberOfChoices();
+
+  
+  // choice getters. Note that not all CGs need to support them since
+  // there is no requirement that CGs compute finite choice sets upon creation
+  
+  T getChoice(int i);
+  T[] getAllChoices();
+  T[] getProcessedChoices();
+  T[] getUnprocessedChoices();
+  
+  ChoiceGenerator<?> getPreviousChoiceGenerator();
+
+  int getNumberOfParents();
+  
+  /**
+   * turn the order of choices random (if it isn't already). Only
+   * drawback of this generic method (which might be a decorator
+   * factory) is that our type layer (e.g. IntChoiceGenerator)
+   * has to guarantee type safety. But hey - this is the first case where
+   * we can use covariant return types!
+   *
+   * NOTES:
+   * - this method may alter this ChoiceGenerator and return that or return
+   * a new "decorated" version.
+   * - random data can be read from the "Random random" field in this class.
+   */
+  ChoiceGenerator<T> randomize();
+
+  ChoiceGenerator<?> clone() throws CloneNotSupportedException;
+
+  ChoiceGenerator<?> deepClone() throws CloneNotSupportedException; 
+  
+  String getId();
+
+  int getIdRef();
+
+  void setIdRef(int idRef);
+
+  void setId(String id);
+
+  boolean isSchedulingPoint();
+
+  //--- the getters and setters for the CG creation info
+  void setThreadInfo(ThreadInfo ti);
+
+  ThreadInfo getThreadInfo();
+
+  void setInsn(Instruction insn);
+
+  Instruction getInsn();
+
+  void setContext(ThreadInfo tiCreator);
+
+  void setStateId (int stateId);
+  
+  int getStateId ();
+  
+  String getSourceLocation();
+
+  boolean supportsReordering();
+  
+  /**
+   * reorder according to a user provided comparator
+   * @returns instance to reordered CG of same choice type, 
+   * null if not supported by particular CG subclass
+   * 
+   * Note: this should only be called before the first advance, since it
+   * can reset the CG enumeration status
+   */
+  ChoiceGenerator<T> reorder (Comparator<T> comparator);
+  
+  void setPreviousChoiceGenerator(ChoiceGenerator<?> cg);
+
+  
+  void setCascaded();
+
+  boolean isCascaded();
+
+  <T extends ChoiceGenerator<?>> T getPreviousChoiceGeneratorOfType(Class<T> cls);
+
+  /**
+   * returns the prev CG if it was registered for the same insn
+   */
+  ChoiceGenerator<?> getCascadedParent();
+
+  /**
+   * return array with all cascaded parents and this CG, in registration order
+   */
+  ChoiceGenerator<?>[] getCascade();
+
+  /**
+   * return array with all parents and this CG, in registration order
+   */
+  ChoiceGenerator<?>[] getAll();
+
+  /**
+   * return array with all CGs (including this one) of given 'cgType', in registration order
+   */
+  <C extends ChoiceGenerator<?>> C[] getAllOfType(Class<C> cgType);
+
+
+  //--- the generic attribute API
+  boolean hasAttr();
+
+  boolean hasAttr(Class<?> attrType);
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  Object getAttr();
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  void setAttr(Object a);
+
+  void addAttr(Object a);
+
+  void removeAttr(Object a);
+
+  void replaceAttr(Object oldAttr, Object newAttr);
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  <A> A getAttr(Class<A> attrType);
+
+  <A> A getNextAttr(Class<A> attrType, Object prev);
+
+  ObjectList.Iterator attrIterator();
+
+  <A> ObjectList.TypedIterator<A> attrIterator(Class<A> attrType);
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/ChoiceGeneratorBase.java b/src/main/gov/nasa/jpf/vm/ChoiceGeneratorBase.java
new file mode 100644 (file)
index 0000000..335b7ce
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.ObjectList;
+
+import java.lang.reflect.Array;
+import java.util.Comparator;
+import java.util.Random;
+
+/**
+ * abstract root class for configurable choice generators
+ */
+public abstract class ChoiceGeneratorBase<T> implements ChoiceGenerator<T> {
+
+  /**
+   * choice randomization policies, which can be set from JPF configuration
+   */
+  static enum ChoiceRandomizationPolicy {
+    VAR_SEED,    // randomize choices using a different seed for every JPF run 
+    FIXED_SEED,  // randomize choices using a fixed seed for each JPF run (reproducible, seed can be specified as cg.seed)
+    NONE         // don't randomize choices
+  };
+  
+  static ChoiceRandomizationPolicy randomization;
+  
+  // the marker for the current choice used in String conversion
+  public static final char MARKER = '>';
+  protected static Random random = new Random(42);
+  
+  
+  // want the id to be visible to subclasses outside package
+  protected String id;
+  
+  // for subsequent access, there is no need to translate a JPF String object reference
+  // into a host VM String anymore (we just need it for creation to look up
+  // the class if this is a named CG)
+  protected int idRef;
+  
+  // used to cut off further choice enumeration
+  protected boolean isDone;
+  
+  // we keep a linked list of CG's
+  protected ChoiceGenerator<?> prev;
+
+  // the instruction that created this CG
+  protected Instruction insn;
+
+  // the state id of the state in which the CG was created
+  protected int stateId;
+  
+  // and the thread that executed this insn
+  protected ThreadInfo ti;
+
+  // free attributes (set on demand)
+  protected Object attr;
+
+  // answer if this is a cascaded CG, i.e. we had more than one registered
+  // within the same transition. Note this is NOT set for the last CG registered
+  protected boolean isCascaded;
+
+  // in case this is initialized from a VM context
+  public static void init(Config config) {
+
+    randomization = config.getEnum("cg.randomize_choices", 
+                                   ChoiceRandomizationPolicy.values(), ChoiceRandomizationPolicy.NONE);
+
+    // if the randomize_choices is set to random then we need to 
+    // pick the seed based on the system time. 
+
+    if (randomization == ChoiceRandomizationPolicy.VAR_SEED) {
+      random.setSeed(System.currentTimeMillis());
+    } else if (randomization == ChoiceRandomizationPolicy.FIXED_SEED){
+      long seed = config.getLong("cg.seed", 42);
+      random.setSeed( seed);
+    }
+  }
+  
+  public static boolean useRandomization() {
+    return (randomization != ChoiceRandomizationPolicy.NONE);
+  }
+
+  /**
+   *  don't use this since it is not safe for cascaded ChoiceGenerators
+   * (we need the 'id' to be as context specific as possible)
+   */
+  @Deprecated
+  protected ChoiceGeneratorBase() {
+    id = "?";
+  }
+
+  protected ChoiceGeneratorBase(String id) {
+    this.id = id;
+  }
+
+  @Override
+  public ChoiceGeneratorBase<?> clone() throws CloneNotSupportedException {
+    return (ChoiceGeneratorBase<?>)super.clone();
+  }
+
+  @Override
+  public ChoiceGenerator<?> deepClone() throws CloneNotSupportedException {
+    ChoiceGenerator<?> clone = (ChoiceGenerator<?>) super.clone();
+    // we need to deep copy the parent CG
+    if (prev != null){
+      clone.setPreviousChoiceGenerator( prev.deepClone());
+    }
+    return clone;
+  }
+  
+  @Override
+  public String getId() {
+    return id;
+  }
+
+  @Override
+  public int getIdRef() {
+    return idRef;
+  }
+
+  @Override
+  public void setIdRef(int idRef) {
+    this.idRef = idRef;
+  }
+
+  @Override
+  public void setId(String id) {
+    this.id = id;
+  }
+
+  @Override
+  public boolean isSchedulingPoint() {
+    return false;
+  }
+
+  //--- the getters and setters for the CG creation info
+  @Override
+  public void setThreadInfo(ThreadInfo ti) {
+    this.ti = ti;
+  }
+
+  @Override
+  public ThreadInfo getThreadInfo() {
+    return ti;
+  }
+
+  @Override
+  public void setInsn(Instruction insn) {
+    this.insn = insn;
+  }
+
+  @Override
+  public Instruction getInsn() {
+    return insn;
+  }
+
+  @Override
+  public void setContext(ThreadInfo tiCreator) {
+    ti = tiCreator;
+    insn = tiCreator.getPC();
+  }
+  
+  @Override
+  public void setStateId(int stateId){
+    this.stateId = stateId;
+
+    if (isCascaded){
+      getCascadedParent().setStateId(stateId);
+    }
+  }
+  
+  @Override
+  public int getStateId(){
+    return stateId;
+  }
+
+  @Override
+  public String getSourceLocation() {
+    return insn.getSourceLocation();
+  }
+
+  @Override
+  public boolean supportsReordering(){
+    return false;
+  }
+  
+  /**
+   * reorder according to a user provided comparator
+   * @returns instance to reordered CG of same choice type, 
+   * null if not supported by particular CG subclass
+   * 
+   * Note: this should only be called before the first advance, since it
+   * can reset the CG enumeration status
+   */
+  @Override
+  public ChoiceGenerator<T> reorder (Comparator<T> comparator){
+    return null;
+  }
+  
+  @Override
+  public void setPreviousChoiceGenerator(ChoiceGenerator<?> cg) {
+    prev = cg;
+  }
+
+  @Override
+  public void setCascaded() {
+    isCascaded = true;
+  }
+
+  @Override
+  public boolean isCascaded() {
+    return isCascaded;
+  }
+
+  @Override
+  public <C extends ChoiceGenerator<?>> C getPreviousChoiceGeneratorOfType(Class<C> cls) {
+    ChoiceGenerator<?> cg = prev;
+
+    while (cg != null) {
+      if (cls.isInstance(cg)) {
+        return (C) cg;
+      }
+      cg = cg.getPreviousChoiceGenerator();
+    }
+    return null;
+  }
+
+  /**
+   * returns the prev CG if it was registered for the same insn
+   */
+  @Override
+  public ChoiceGenerator<?> getCascadedParent() {
+    if (prev != null) {
+      if (prev.isCascaded()) {
+        return prev;
+      }
+    }
+
+    return null;
+  }
+
+  /**
+   * return array with all cascaded parents and this CG, in registration order
+   */
+  @Override
+  public ChoiceGenerator<?>[] getCascade() {
+    int n = 0;
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getCascadedParent()) {
+      n++;
+    }
+
+    ChoiceGenerator<?>[] a = new ChoiceGenerator<?>[n];
+
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getCascadedParent()) {
+      a[--n] = cg;
+    }
+
+    return a;
+  }
+
+  /**
+   * return array with all parents and this CG, in registration order
+   */
+  @Override
+  public ChoiceGenerator<?>[] getAll() {
+    int n = 0;
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
+      n++;
+    }
+
+    ChoiceGenerator<?>[] a = new ChoiceGenerator<?>[n];
+
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
+      a[--n] = cg;
+    }
+
+    return a;
+  }
+
+  /**
+   * return array with all CGs (including this one) of given 'cgType', in registration order
+   */
+  @Override
+  public <C extends ChoiceGenerator<?>> C[] getAllOfType(Class<C> cgType) {
+    int n = 0;
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
+      if (cgType.isAssignableFrom(cg.getClass())) {
+        n++;
+      }
+    }
+
+    C[] a = (C[]) Array.newInstance(cgType, n);
+
+    for (ChoiceGenerator<?> cg = this; cg != null; cg = cg.getPreviousChoiceGenerator()) {
+      if (cgType.isAssignableFrom(cg.getClass())) {
+        a[--n] = (C) cg;
+      }
+    }
+
+    return a;
+  }
+
+  @Override
+  public int getNumberOfParents(){
+    int n=0;
+    for (ChoiceGenerator cg = prev; cg != null; cg = cg.getPreviousChoiceGenerator()){
+      n++;
+    }
+    return n;
+  }
+  
+  @Override
+  public void setCurrent(){
+    // nothing, can be overridden by subclasses to do context specific initialization
+    // the first time this CG becomes the current one
+  }
+  
+  // we can't put the advanceForCurrentInsn() here because it has to do
+  // notifications, which are the SystemState responsibility
+  /**
+   * advance n choices
+   * pretty braindead generic solution, but if more speed is needed, we can easily override
+   * in the concrete CGs (it's used for path replay)
+   */
+  @Override
+  public void advance(int nChoices) {
+    while (nChoices-- > 0) {
+      advance();
+    }
+  }
+
+  @Override
+  public void select (int choiceIndex) {
+    reset();
+    advance(choiceIndex+1);
+    setDone();
+  }
+
+  // override this to support explicit CG enumeration from listeners etc.
+  
+  /**
+   * explicit choice enumeration. Override if supported
+   * @return choice value or null if not supported
+   */
+  @Override
+  public T getChoice (int idx){
+    return null;
+  }
+  
+  //--- generic choice set getter implementation
+  // Note - this requires an overloaded getChoice() and can be very slow (depending on CG implementation)
+  
+  @Override
+  public T[] getAllChoices(){
+    int n = getTotalNumberOfChoices();
+    T[] a = (T[]) new Object[n];
+    for (int i=0; i<n; i++){
+      T c = getChoice(i);
+      if (c == null){
+        return null; // CG doesn't support choice enumeration
+      } else {
+        a[i] = c;
+      }
+    }
+    return a;
+  }
+  
+  @Override
+  public T[] getProcessedChoices(){
+    int n = getProcessedNumberOfChoices();
+    T[] a = (T[]) new Object[n];
+    for (int i=0; i<n; i++){
+      T c = getChoice(i);
+      if (c == null){
+        return null; // CG doesn't support choice enumeration
+      } else {
+        a[i] = c;
+      }
+    }
+    return a;    
+  }
+  
+  @Override
+  public T[] getUnprocessedChoices(){
+    int n = getTotalNumberOfChoices();
+    int m = getProcessedNumberOfChoices();
+    T[] a = (T[]) new Object[n];
+    for (int i=m-1; i<n; i++){
+      T c = getChoice(i);
+      if (c == null){
+        return null; // CG doesn't support choice enumeration
+      } else {
+        a[i] = c;
+      }
+    }
+    return a;    
+  }
+  
+  
+  @Override
+  public boolean isDone() {
+    return isDone;
+  }
+
+  @Override
+  public void setDone() {
+    isDone = true;
+  }
+
+  @Override
+  public boolean isProcessed() {
+    return isDone || !hasMoreChoices();
+  }
+
+  //--- the generic attribute API
+  @Override
+  public boolean hasAttr() {
+    return (attr != null);
+  }
+
+  @Override
+  public boolean hasAttr(Class<?> attrType) {
+    return ObjectList.containsType(attr, attrType);
+  }
+
+  public boolean hasAttrValue (Object a){
+    return ObjectList.contains(attr, a);
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  @Override
+  public Object getAttr() {
+    return attr;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  @Override
+  public void setAttr(Object a) {
+    attr = a;
+  }
+
+  @Override
+  public void addAttr(Object a) {
+    attr = ObjectList.add(attr, a);
+  }
+
+  @Override
+  public void removeAttr(Object a) {
+    attr = ObjectList.remove(attr, a);
+  }
+
+  @Override
+  public void replaceAttr(Object oldAttr, Object newAttr) {
+    attr = ObjectList.replace(attr, oldAttr, newAttr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  @Override
+  public <T> T getAttr(Class<T> attrType) {
+    return ObjectList.getFirst(attr, attrType);
+  }
+
+  @Override
+  public <T> T getNextAttr(Class<T> attrType, Object prev) {
+    return ObjectList.getNext(attr, attrType, prev);
+  }
+
+  @Override
+  public ObjectList.Iterator attrIterator() {
+    return ObjectList.iterator(attr);
+  }
+
+  @Override
+  public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType) {
+    return ObjectList.typedIterator(attr, attrType);
+  }
+
+  // -- end attrs --
+  @Override
+  public String toString() {
+    StringBuilder b = new StringBuilder(getClass().getName());
+    b.append(" {id:\"");
+    b.append(id);
+    b.append("\" ,");
+    b.append(getProcessedNumberOfChoices());
+    b.append('/');
+    b.append(getTotalNumberOfChoices());
+    b.append(",isCascaded:");
+    b.append(isCascaded);
+
+    if (attr != null) {
+      b.append(",attrs:[");
+      int i = 0;
+      for (Object a : ObjectList.iterator(attr)) {
+        if (i++ > 1) {
+          b.append(',');
+        }
+        b.append(a);
+      }
+      b.append(']');
+    }
+
+    b.append('}');
+
+    return b.toString();
+  }
+
+  @Override
+  public ChoiceGenerator<?> getPreviousChoiceGenerator() {
+    return prev;
+  }
+
+  // override if there is special choice randomization support
+  @Override
+  public ChoiceGenerator<T> randomize(){
+    return this;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ChoicePoint.java b/src/main/gov/nasa/jpf/vm/ChoicePoint.java
new file mode 100644 (file)
index 0000000..041788b
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StreamTokenizer;
+import java.util.HashMap;
+
+/**
+ * a little helper class that is used to replay previously stored traces
+ * (which are little more than just a list of ChoiceGenerator classnames and
+ * choiceIndex indexes stored in a previous run)
+ */
+public class ChoicePoint {
+  String cgClassName;
+  int choiceIndex;
+  ChoicePoint next, prev;
+
+  ChoicePoint (String cgClassName, int choice, ChoicePoint prev) {
+    this.cgClassName = cgClassName;
+    this.choiceIndex = choice;
+
+    if (prev != null) {
+      this.prev = prev;
+      prev.next = this;
+    }
+  }
+
+  public String getCgClassName() {
+    return cgClassName;
+  }
+
+  public int getChoiceIndex() {
+    return choiceIndex;
+  }
+
+  public ChoicePoint getNext() {
+    return next;
+  }
+
+  public ChoicePoint getPrevious() {
+    return prev;
+  }
+
+  public static void storeTrace (String fileName,
+                                 String sutName, String comment,
+                                 ChoiceGenerator[] trace, boolean verbose) {
+    int i;
+    if (fileName != null) {
+      try {
+        FileWriter fw = new FileWriter(fileName);
+        PrintWriter pw = new PrintWriter(fw);
+
+        if (comment != null){ // might be multi-line
+          pw.print("/* ");
+          pw.print(comment);
+          pw.println(" */");
+        }
+
+        // store the main app class and args here, so that we can do at least some checking
+        pw.print( "application: ");
+        pw.println( sutName);
+
+        // keep a String->id map so that we don't have to store thousands of redundant strings
+        HashMap<String,Integer> map = new HashMap<String,Integer>();
+        int clsId = 0;
+
+        for (i=0; i<trace.length; i++) {
+          String cgClsName = trace[i].getClass().getName();
+
+          pw.print('[');
+          pw.print(i);
+          pw.print("] ");
+
+          Integer ref = map.get(cgClsName);
+          if (ref == null) {
+            pw.print(cgClsName);
+            map.put(cgClsName, clsId++);
+          } else { // us the numeric id instead
+            pw.print('#');
+            pw.print(ref.intValue());
+          }
+
+          pw.print(" ");
+          pw.print( trace[i].getProcessedNumberOfChoices()-1);
+          
+          if (verbose){
+            pw.print("  // ");
+            pw.print(trace[i]);
+          }
+          
+          pw.println();
+        }
+
+        pw.close();
+        fw.close();
+      } catch (Throwable t) {
+        throw new JPFException(t);
+      }
+    }
+  }
+
+  static StreamTokenizer createScanner (String fileName) {
+    StreamTokenizer scanner = null;
+
+    if (fileName == null) {
+      return null;
+    }
+
+    File f = new File(fileName);
+    if (f.exists()) {
+      try {
+        FileReader fr = new FileReader(f);
+        scanner = new StreamTokenizer(fr);
+
+        scanner.slashSlashComments(true);
+        scanner.slashStarComments(true);
+
+        // how silly - there is no better way to turn off parseNumbers()
+        scanner.resetSyntax();
+        scanner.wordChars('a', 'z');
+        scanner.wordChars('A', 'Z');
+        scanner.wordChars(128 + 32, 255);
+        scanner.whitespaceChars(0, ' ');
+        //scanner.commentChar('/');
+        scanner.quoteChar('"');
+        scanner.quoteChar('\'');
+
+        scanner.wordChars('0','9');
+        scanner.wordChars(':', ':');
+        scanner.wordChars('.', '.');
+        scanner.wordChars('#', '#');
+
+        scanner.nextToken();
+      } catch (IOException iox) {
+        throw new JPFException("cannot read tracefile: " + fileName);
+      }
+
+      return scanner;
+    } else {
+      return null;
+    }
+  }
+
+  static void match (StreamTokenizer scanner, String s) throws IOException {
+    if ((scanner.ttype == StreamTokenizer.TT_WORD) && (scanner.sval.equals(s))) {
+      scanner.nextToken();
+    } else {
+      throw new JPFException ("tracefile error - expected " + s + ", got: " + scanner.sval);
+    }
+  }
+
+  static String matchString (StreamTokenizer scanner) throws IOException {
+    if (scanner.ttype == StreamTokenizer.TT_WORD) {
+      String s = scanner.sval;
+      if (s.length() == 0) {
+        throw new JPFException ("tracefile error - non-empty string expected");
+      }
+      scanner.nextToken();
+      return s;
+    } else {
+      throw new JPFException ("tracefile error - word expected, got: " + scanner.sval);
+    }
+  }
+
+  static void matchChar (StreamTokenizer scanner, char c) throws IOException {
+    if (scanner.ttype == c) {
+      scanner.nextToken();
+    } else {
+      throw new JPFException ("tracefile error - char '"
+                              + c + "' expected, got: " + scanner.sval);
+    }
+  }
+
+  static int matchNumber (StreamTokenizer scanner) throws IOException {
+    try {
+      if (scanner.ttype == StreamTokenizer.TT_WORD) {
+        int n = Integer.parseInt(scanner.sval);
+        scanner.nextToken();
+        return n;
+      }
+    } catch (NumberFormatException nfx) {}
+
+    throw new JPFException ("tracefile error - number expected, got: " + scanner.sval);
+  }
+
+  /**
+   * "application:" appName
+   *  {arg}
+   *  "["searchLevel"]" (choiceGeneratorName | '#'cgID) nChoice
+   */
+  public static ChoicePoint readTrace (String fileName, String sutName) {
+    ChoicePoint firstCp = null, cp = null;
+    StreamTokenizer scanner = createScanner(fileName);
+
+    if (scanner == null) {
+      return null;
+    }
+
+    try {
+    match(scanner, "application:");
+    match(scanner, sutName);
+
+    HashMap<String,String> map = new HashMap<String,String>();
+    int clsId = 0;
+
+    while (scanner.ttype != StreamTokenizer.TT_EOF) {
+      matchChar(scanner, '[');
+      matchNumber(scanner);
+      matchChar(scanner, ']');
+
+      String cpClass  = matchString(scanner);
+      if (cpClass.charAt(0) == '#') { // it's a CG class id
+        cpClass = map.get(cpClass);
+        if (cpClass == null) {
+          throw new JPFException("tracefile error - unknown ChoicePoint class id: " + cpClass);
+        }
+      } else {
+        String id = "#" + clsId++;
+        map.put(id, cpClass);
+      }
+
+      int choiceIndex = matchNumber(scanner);
+
+      cp = new ChoicePoint(cpClass, choiceIndex, cp);
+      if (firstCp == null) {
+        firstCp = cp;
+      }
+    }
+    } catch (IOException iox) {
+      throw new JPFException("tracefile read error: " + iox.getMessage());
+    }
+
+    return firstCp;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassChangeException.java b/src/main/gov/nasa/jpf/vm/ClassChangeException.java
new file mode 100644 (file)
index 0000000..89f2346
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * represents a IncompatibleClassChangeError that has to be thrown in the SUT
+ */
+public class ClassChangeException extends RuntimeException {
+  
+  public ClassChangeException (String details){
+    super(details);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassFileContainer.java b/src/main/gov/nasa/jpf/vm/ClassFileContainer.java
new file mode 100644 (file)
index 0000000..c9c7d6a
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * abstract class that represents the source of a classfile, such
+ * as (root) directories and jars
+ */
+public abstract class ClassFileContainer {
+  protected String name;
+  protected String url;
+
+  protected ClassFileContainer(String name, String url) {
+    this.name = name;
+    this.url = url;
+  }
+
+  public String getName() {
+    return name;
+  }
+
+  public String getURL() {
+    return url;
+  }
+
+  public abstract String getClassURL (String clsName);
+
+  protected static void error(String msg) throws ClassParseException {
+    throw new ClassParseException(msg);
+  }
+
+  public abstract ClassFileMatch getMatch (String clsName) throws ClassParseException;
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassFileMatch.java b/src/main/gov/nasa/jpf/vm/ClassFileMatch.java
new file mode 100644 (file)
index 0000000..a5a9b1b
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a lookup match for a given typename in a ClassFileContainer
+ */
+public abstract class ClassFileMatch {
+  public final String typeName;
+  public final String url;
+
+  protected ClassFileMatch (String typeName, String url) {
+    this.typeName = typeName;
+    this.url = url;
+  }
+  
+  public String getClassURL () {
+    return url;
+  }  
+  
+  public abstract ClassFileContainer getContainer();
+
+  // those are here because VM specific subclasses know about the binary format, how to get the data from the
+  // respective container, and what parser to use to transform it
+  public abstract ClassInfo createClassInfo (ClassLoaderInfo loader) throws ClassParseException;
+  public abstract AnnotationInfo createAnnotationInfo (ClassLoaderInfo loader) throws ClassParseException;
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassInfo.java b/src/main/gov/nasa/jpf/vm/ClassInfo.java
new file mode 100644 (file)
index 0000000..d26abed
--- /dev/null
@@ -0,0 +1,2594 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.JPFListener;
+import gov.nasa.jpf.util.ImmutableList;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.LocationSpec;
+import gov.nasa.jpf.util.MethodSpec;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.OATHash;
+import gov.nasa.jpf.util.Source;
+
+import java.io.File;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+import java.util.logging.Level;
+
+
+/**
+ * Describes the VM's view of a java class.  Contains descriptions of the
+ * static and dynamic fields, declaredMethods, and information relevant to the
+ * class.
+ * 
+ * Note that ClassInfos / classes have three different construction/initialization steps:
+ * (1) construction : recursively via ClassLoaderInfo.getResolvedClassInfo -> ClassFileContainer.createClassInfo
+ *     -> ClassInfo ctor -> resolveClass
+ *     this only creates the ClassInfo object, but it is not visible/usable from SUT code yet and hence not
+ *     observable from classLoaded() listeners
+ * (2) registration : create StaticElementInfo and add it to the respective ClassLoaderInfo statics, then create
+ *     the java.lang.Class companion object in the SUT
+ *     this makes the ClassInfo usable from SUT code
+ * (3) initialization : execute clinit (if the class has one)
+ * 
+ * Note that id/uniqueId are NOT set before registration takes place, and registration is not automatically performed since
+ * listeners/peers might create ClassInfos internally (e.g. for inspection), which should not be visible from the SUT or observable
+ * by other listeners.
+ * 
+ * Automatic registration from the ClassInfo ctors would require to pass a ThreadInfo context throughout the whole ClassLoaderInfo/
+ * ClassFileContainer/ClassInfo chain and could lead to false positives for sharedness based POR, which would record this
+ * thread as referencing even if this is a listener/peer internal request
+ */
+public class ClassInfo extends InfoObject implements Iterable<MethodInfo>, GenericSignatureHolder {
+
+  //--- ClassInfo states, in chronological order
+  // note the somewhat strange, decreasing values - >= 0 (=thread-id) means 
+  // we are in clinit
+  // ideally, we would have a separate RESOLVED state, but (a) this is somewhat
+  // orthogonal to REGISTERED, and - more importantly - (b) we need the
+  // superClass instance when initializing our Fields (instance field offsets).
+  // Doing the field initialization during resolveReferencedClass() seems awkward and
+  // error prone (there is not much you can do with an unresolved class then)
+  
+  // not registered or clinit'ed (but cached in loadedClasses)
+  public static final int UNINITIALIZED = -1;
+  // 'REGISTERED' simply means 'sei' is set (we have a StaticElementInfo)
+  // 'INITIALIZING' is any number >=0, which is the thread objRef that executes the clinit
+  public static final int INITIALIZED = -2;
+
+  protected static final String ID_FIELD = "nativeId"; 
+
+  protected static JPFLogger logger = JPF.getLogger("class");
+
+  protected static int nClassInfos; // for statistics
+  
+  protected static Config config;
+
+  /**
+   * ClassLoader that loaded this class.
+   */
+  protected static final ClassLoader thisClassLoader = ClassInfo.class.getClassLoader();  
+  
+  /**
+   * our abstract factory to createAndInitialize object and class fields
+   */
+  protected static FieldsFactory fieldsFactory;
+
+  
+  protected static final FieldInfo[] EMPTY_FIELDINFO_ARRAY = new FieldInfo[0];
+  protected static final String[] EMPTY_STRING_ARRAY = new String[0];
+  protected static final String UNINITIALIZED_STRING = "UNINITIALIZED"; 
+  protected static final Map<String,MethodInfo> NO_METHODS = Collections.emptyMap();
+  protected static final Set<ClassInfo> NO_INTERFACES = new HashSet<ClassInfo>();
+  
+  /**
+   * support to auto-load listeners from annotations
+   */
+  protected static HashSet<String> autoloadAnnotations;
+  protected static HashSet<String> autoloaded;
+
+  /**
+   * Name of the class. e.g. "java.lang.String"
+   * NOTE - this is the expanded name for builtin types, e.g. "int", but NOT
+   * for arrays, which are for some reason in Ldot notation, e.g. "[Ljava.lang.String;" or "[I"
+   */
+  protected String name;
+  
+  /** type erased signature of the class. e.g. "Ljava/lang/String;" */
+  protected String signature;
+
+  /** Generic type signatures of the class as per para. 4.4.4 of the revised VM spec */
+  protected String genericSignature;
+
+  /** The classloader that defined (directly loaded) this class */
+  protected ClassLoaderInfo classLoader;
+  
+  // various class attributes
+  protected boolean      isClass = true;
+  protected boolean      isWeakReference = false;
+  protected boolean      isObjectClassInfo = false;
+  protected boolean      isStringClassInfo = false;
+  protected boolean      isThreadClassInfo = false;
+  protected boolean      isRefClassInfo = false;
+  protected boolean      isArray = false;
+  protected boolean      isEnum = false;
+  protected boolean      isReferenceArray = false;
+  protected boolean      isAbstract = false;
+  protected boolean      isBuiltin = false;
+
+  // that's ultimately where we keep the attributes
+  // <2do> this is currently quite redundant, but these are used in reflection
+  protected int modifiers;
+
+  protected MethodInfo   finalizer = null;
+
+  /** type based object attributes (for GC, partial order reduction and
+   * property checks)
+   */
+  protected int elementInfoAttrs = 0;
+
+  /**
+   * all our declared declaredMethods (we don't flatten, this is not
+   * a high-performance VM)
+   */
+  protected Map<String, MethodInfo> methods;
+
+  /**
+   * our instance fields.
+   * Note these are NOT flattened, idx.e. only contain the declared ones
+   */
+  protected FieldInfo[] iFields;
+
+  /** the storage size of instances of this class (stored as an int[]) */
+  protected int instanceDataSize;
+
+  /** where in the instance data array (int[]) do our declared fields start */
+  protected int instanceDataOffset;
+
+  /** total number of instance fields (flattened, not only declared ones) */
+  protected int nInstanceFields;
+
+  /**
+   * our static fields. Again, not flattened
+   */
+  protected FieldInfo[] sFields;
+
+  /** the storage size of static fields of this class (stored as an int[]) */
+  protected int staticDataSize;
+
+  /**
+   * we only set the superClassName upon creation, it is instantiated into
+   * a ClassInfo by resolveReferencedClass(), which is required to be called before
+   * we can createAndInitialize objects of this type
+   */
+  protected ClassInfo  superClass;
+  protected String superClassName;
+
+  protected String enclosingClassName;
+  protected String enclosingMethodName;
+
+  protected String[] innerClassNames = EMPTY_STRING_ARRAY;
+  protected BootstrapMethodInfo[] bootstrapMethods;
+    
+  /** direct ifcs implemented by this class */
+  protected String[] interfaceNames;
+
+  protected Set<ClassInfo> interfaces = new HashSet<ClassInfo>();
+  
+  /** cache of all interfaceNames (parent interfaceNames and interface parents) - lazy eval */
+  protected Set<ClassInfo> allInterfaces;
+  
+  /** Name of the package. */
+  protected String packageName;
+
+  /** this is only set if the classfile has a SourceFile class attribute */
+  protected String sourceFileName;
+
+  /** 
+   * Uniform resource locater for the class file. NOTE: since for builtin classes
+   * there is no class file assigned is set to the typeName 
+   */ 
+  protected String classFileUrl;
+
+  /** from where the corresponding classfile was loaded (if this is not a builtin) */
+  protected gov.nasa.jpf.vm.ClassFileContainer container;
+
+  
+  /**
+   *  a search global numeric id that is only unique within this ClassLoader namespace. Ids are
+   *  computed by the ClassLoaderInfo/Statics implementation during ClassInfo registration
+   */
+  protected int  id = -1;
+
+  /**
+   * A search global unique id associate with this class, which is comprised of the classLoader id
+   * and the (loader-specific) ClassInfo id. This is just a quick way to do search global checks for equality
+   * 
+   * NOTE - since this is based on the classloader-specific id, it can't be used before the ClassInfo is registered
+   */
+  protected long uniqueId = -1;
+
+  /**
+   * this is the object we use to enter declaredMethods in the underlying VM
+   * (it replaces Reflection)
+   */
+  protected NativePeer nativePeer;
+
+  /** Source file associated with the class.*/
+  protected Source source;
+
+  protected boolean enableAssertions;
+
+  /** actions to be taken when an object of this type is gc'ed */
+  protected ImmutableList<ReleaseAction> releaseActions; 
+          
+  
+  static boolean init (Config config) {
+
+    ClassInfo.config = config;
+    
+    setSourceRoots(config);
+    //buildBCELModelClassPath(config);
+
+    fieldsFactory = config.getEssentialInstance("vm.fields_factory.class",
+                                                FieldsFactory.class);
+
+    autoloadAnnotations = config.getNonEmptyStringSet("listener.autoload");
+    if (autoloadAnnotations != null) {
+      autoloaded = new HashSet<String>();
+
+      if (logger.isLoggable(Level.INFO)) {
+        for (String s : autoloadAnnotations){
+          logger.info("watching for autoload annotation @" + s);
+        }
+      }
+    }
+
+    return true;
+  }
+
+  public static boolean isObjectClassInfo (ClassInfo ci){
+    return ci.isObjectClassInfo();
+  }
+
+  public static boolean isStringClassInfo (ClassInfo ci){
+    return ci.isStringClassInfo();
+  }
+
+  
+   //--- initialization interface towards parsers (which might reside in other packages)
+    
+  protected void setClass(String clsName, String superClsName, int flags, int cpCount) throws ClassParseException {
+    String parsedName = Types.getClassNameFromTypeName(clsName);
+
+    if (name != null && !name.equals(parsedName)){
+      throw new ClassParseException("wrong class name (expected: " + name + ", found: " + parsedName + ')');
+    }
+    name = parsedName;
+    
+    // the enclosingClassName is set on demand since it requires loading enclosing class candidates
+    // to verify their innerClass attributes
+
+    int i = name.lastIndexOf('.');
+    packageName = (i > 0) ? name.substring(0, i) : "";
+
+    modifiers = flags;
+    
+    // annotations are interfaces too (not exposed by Modifier)
+    isClass = ((flags & Modifier.INTERFACE) == 0);
+
+    superClassName = superClsName;
+  }
+
+  public void setInnerClassNames(String[] clsNames) {
+    innerClassNames = clsNames;
+  }
+
+  public void setEnclosingClass (String clsName) {
+    enclosingClassName = clsName;
+  }
+  
+  public void setEnclosingMethod (String mthName){
+    enclosingMethodName = mthName;    
+  }
+
+  public void setInterfaceNames(String[] ifcNames) {
+    interfaceNames = ifcNames;
+  }
+  
+  public void setSourceFile (String fileName){
+    // prepend if we already know the package
+    if (packageName.length() > 0) {
+      // Source will take care of proper separator chars later
+      sourceFileName = packageName.replace('.', '/') + '/' + fileName;
+    } else {
+      sourceFileName = fileName;
+    }
+  }
+
+  public void setFields(FieldInfo[] fields) {
+    if (fields == null){
+      iFields = EMPTY_FIELDINFO_ARRAY;
+      sFields = EMPTY_FIELDINFO_ARRAY;
+      
+    } else { // there are fields, we have to tell them apart
+      int nInstance = 0, nStatic = 0;
+      for (int i = 0; i < fields.length; i++) {
+        if (fields[i].isStatic()) {
+          nStatic++;
+        } else {
+          nInstance++;
+        }
+      }
+
+      FieldInfo[] instanceFields = (nInstance > 0) ? new FieldInfo[nInstance] : EMPTY_FIELDINFO_ARRAY;
+      FieldInfo[] staticFields = (nStatic > 0) ? new FieldInfo[nStatic] : EMPTY_FIELDINFO_ARRAY;
+
+      int iInstance = 0;
+      int iStatic = 0;
+      for (int i = 0; i < fields.length; i++) {
+        FieldInfo fi = fields[i];
+
+        if (fi.isStatic()) {
+          staticFields[iStatic++] = fi;
+        } else {
+          instanceFields[iInstance++] = fi;
+        }
+        
+        processJPFAnnotations(fi);
+      }
+
+      iFields = instanceFields;
+      sFields = staticFields;
+
+      // we can't link the fields yet because we need the superclasses to be resolved
+    }
+  }
+
+  protected void setMethod (MethodInfo mi){
+    mi.linkToClass(this);
+    methods.put( mi.getUniqueName(), mi);
+    processJPFAnnotations(mi);
+  }
+  
+  public void setMethods (MethodInfo[] newMethods) {
+    if (newMethods != null && newMethods.length > 0) {
+      methods = new LinkedHashMap<String, MethodInfo>();
+
+      for (int i = 0; i < newMethods.length; i++) {
+        setMethod( newMethods[i]);
+      }
+    }
+  }
+  protected void processJPFAttrAnnotation(InfoObject infoObj){
+    AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.JPFAttribute");
+    if (ai != null){
+      String[] attrTypes = ai.getValueAsStringArray();
+      if (attrTypes != null){
+        ClassLoader loader = config.getClassLoader();
+
+        for (String clsName : attrTypes){
+          try {
+            Class<?> attrCls = loader.loadClass(clsName);
+            Object attr = attrCls.newInstance(); // needs to have a default ctor
+            infoObj.addAttr(attr);
+            
+          } catch (ClassNotFoundException cnfx){
+            logger.warning("attribute class not found: " + clsName);
+            
+          } catch (IllegalAccessException iax){
+            logger.warning("attribute class has no public default ctor: " + clsName);            
+            
+          } catch (InstantiationException ix){
+            logger.warning("attribute class has no default ctor: " + clsName);            
+          }
+        }
+      }
+    }    
+  }
+
+  protected void processNoJPFExecutionAnnotation(InfoObject infoObj) {
+    AnnotationInfo ai = infoObj.getAnnotation("gov.nasa.jpf.annotation.NoJPFExecution");
+    if (ai != null) {
+      infoObj.addAttr(NoJPFExec.SINGLETON);
+    }
+  }
+
+  protected void processJPFAnnotations(InfoObject infoObj) {
+    processJPFAttrAnnotation(infoObj);
+    processNoJPFExecutionAnnotation(infoObj);
+  }
+
+    public AnnotationInfo getResolvedAnnotationInfo (String typeName){
+    return classLoader.getResolvedAnnotationInfo( typeName);
+  }
+  
+  @Override
+  public void setAnnotations(AnnotationInfo[] annotations) {
+    this.annotations = annotations;
+  }
+  
+  //--- end initialization interface
+  //--- the overridden annotation accessors (we need these because of inherited superclass annotations)
+  // note that we don't flatten annotations anymore, assuming the prevalent query will be getAnnotation(name)
+  
+  @Override
+  public boolean hasAnnotations(){
+    if (annotations.length > 0){
+      return true;
+    }
+    
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int j=0; j<a.length; j++){
+        if (a[j].isInherited()){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  /**
+   * return all annotations, which includes the ones inherited from our superclasses
+   * NOTE - this is not very efficient
+   */
+  @Override
+  public AnnotationInfo[] getAnnotations() {
+    int nAnnotations = annotations.length;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        if (a[i].isInherited()){
+          nAnnotations++;
+        }
+      }
+    }
+    
+    AnnotationInfo[] allAnnotations = new AnnotationInfo[nAnnotations];
+    System.arraycopy(annotations, 0, allAnnotations, 0, annotations.length);
+    int idx=annotations.length;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      AnnotationInfo[] a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        if (a[i].isInherited()){
+          allAnnotations[idx++] = a[i];
+        }
+      }
+    }
+    
+    return allAnnotations;
+  }
+    
+  @Override
+  public AnnotationInfo getAnnotation (String annotationName){
+    AnnotationInfo[] a = annotations;
+    for (int i=0; i<a.length; i++){
+      if (a[i].getName().equals(annotationName)){
+        return a[i];
+      }
+    }
+    
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      a = ci.annotations;
+      for (int i=0; i<a.length; i++){
+        AnnotationInfo ai = a[i];
+        if (ai.getName().equals(annotationName) && ai.isInherited()){
+          return ai;
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  protected ClassInfo (String name, ClassLoaderInfo cli, String classFileUrl){
+    nClassInfos++;
+    
+    this.name = name;
+    this.classLoader = cli;
+    this.classFileUrl = classFileUrl;
+    
+    this.methods = NO_METHODS;  // yet
+
+    // rest has to be initialized by concrete ctor, which should call resolveAndLink(parser)
+  }
+  
+  /**
+   * the initialization part that has to happen once we have super, fields, methods and annotations
+   * NOTE - this has to be called by concrete ctors after parsing class files
+   */
+  protected void resolveAndLink () throws ClassParseException {
+    
+    //--- these might get streamlined
+    isStringClassInfo = isStringClassInfo0();
+    isThreadClassInfo = isThreadClassInfo0();
+    isObjectClassInfo = isObjectClassInfo0();
+    isRefClassInfo = isRefClassInfo0();
+   // isWeakReference = isWeakReference0();
+    isAbstract = (modifiers & Modifier.ABSTRACT) != 0;
+   // isEnum = isEnum0();
+    
+    finalizer = getFinalizer0();
+
+    resolveClass(); // takes care of super classes and interfaces
+
+    // Used to enter native methods (in the host VM).
+    // This needs to be initialized AFTER we get our  MethodInfos, since it does a reverse lookup to determine which
+    // ones are handled by the peer (by means of setting MethodInfo attributes)
+    nativePeer = loadNativePeer();
+    checkUnresolvedNativeMethods();
+
+    linkFields(); // computes field offsets
+    
+    setAssertionStatus();
+    processJPFConfigAnnotation();
+    processJPFAnnotations(this);
+    loadAnnotationListeners();    
+  }
+  
+  protected ClassInfo(){
+    nClassInfos++;
+    
+    // for explicit subclass initialization
+  }
+  
+  /**
+   * ClassInfo ctor used for builtin types (arrays and primitive types)
+   * idx.e. classes we don't have class files for
+   */
+  protected ClassInfo (String builtinClassName, ClassLoaderInfo classLoader) {
+    nClassInfos++;
+
+    this.classLoader = classLoader;
+
+    isArray = (builtinClassName.charAt(0) == '[');
+    isReferenceArray = isArray && (builtinClassName.endsWith(";") || builtinClassName.charAt(1) == '[');
+    isBuiltin = true;
+
+    name = builtinClassName;
+
+    logger.log(Level.FINE, "generating builtin class: %1$s", name);
+
+    packageName = ""; // builtin classes don't reside in java.lang !
+    sourceFileName = null;
+    source = null;
+    genericSignature = "";
+
+    // no fields
+    iFields = EMPTY_FIELDINFO_ARRAY;
+    sFields = EMPTY_FIELDINFO_ARRAY;
+
+    if (isArray) {
+      if(classLoader.isSystemClassLoader()) {
+        superClass = ((SystemClassLoaderInfo)classLoader).getObjectClassInfo();
+      } else {
+        superClass = ClassLoaderInfo.getCurrentSystemClassLoader().getObjectClassInfo();
+      }
+      interfaceNames = loadArrayInterfaces();
+      methods = loadArrayMethods();
+    } else {
+      superClass = null; // strange, but true, a 'no object' class
+      interfaceNames = loadBuiltinInterfaces(name);
+      methods = loadBuiltinMethods(name);
+    }
+
+    enableAssertions = true; // doesn't really matter - no code associated
+
+    classFileUrl = name;
+    
+    // no fields or declaredMethods, so we don't have to link/resolve anything
+  }
+  
+  public static int getNumberOfLoadedClasses(){
+    return nClassInfos;
+  }
+  
+  //--- the VM type specific methods
+  // <2do> those should be abstract
+  
+  protected void setAnnotationValueGetterCode (MethodInfo pmi, FieldInfo fi){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setDirectCallCode (MethodInfo miCallee, MethodInfo miStub){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setLambdaDirectCallCode (MethodInfo miDirectCall, BootstrapMethodInfo bootstrapMethod){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setNativeCallCode (NativeMethodInfo miNative){
+    // to be overridden by VM specific class
+  }
+  
+  protected void setRunStartCode (MethodInfo miStub, MethodInfo miRun){
+    // to be overridden by VM specific class
+  }
+  
+  /**
+   * createAndInitialize a fully synthetic implementation of an Annotation proxy
+   */
+  protected ClassInfo (ClassInfo annotationCls, String name, ClassLoaderInfo classLoader, String url) {
+    this.classLoader = classLoader;
+    
+    this.name = name;
+    isClass = true;
+
+    //superClass = objectClassInfo;
+    superClass = ClassLoaderInfo.getSystemResolvedClassInfo("gov.nasa.jpf.AnnotationProxyBase");
+
+    interfaceNames = new String[]{ annotationCls.name };    
+    packageName = annotationCls.packageName;
+    sourceFileName = annotationCls.sourceFileName;
+    genericSignature = annotationCls.genericSignature;
+
+    sFields = new FieldInfo[0]; // none
+    staticDataSize = 0;
+
+    methods = new HashMap<String, MethodInfo>();
+    iFields = new FieldInfo[annotationCls.methods.size()];
+    nInstanceFields = iFields.length;
+
+    // all accessor declaredMethods of ours make it into iField/method combinations
+    int idx = 0;
+    int off = 0;  // no super class
+    for (MethodInfo mi : annotationCls.getDeclaredMethodInfos()) {
+      String mname = mi.getName();
+      String mtype = mi.getReturnType();
+      String genericSignature = mi.getGenericSignature();
+
+      // create and initialize an instance field for it
+      FieldInfo fi = FieldInfo.create(mname, mtype, 0);
+      fi.linkToClass(this, idx, off);
+      fi.setGenericSignature(genericSignature);
+      iFields[idx++] = fi;
+      off += fi.getStorageSize();
+
+      MethodInfo pmi = new MethodInfo(this, mname, mi.getSignature(), Modifier.PUBLIC, 1, 2);
+      pmi.setGenericSignature(genericSignature);
+      
+      setAnnotationValueGetterCode( pmi, fi);
+      methods.put(pmi.getUniqueName(), pmi);
+    }
+
+    instanceDataSize = computeInstanceDataSize();
+    instanceDataOffset = 0;
+
+    classFileUrl = url;
+    linkFields();
+  }
+  
+  
+  //used to create synthetic classes that implement functional interfaces
+  protected ClassInfo createFuncObjClassInfo (BootstrapMethodInfo bootstrapMethod, String name, String samUniqueName, String[] fieldTypesName) {
+   return null;
+ }
+ protected ClassInfo (ClassInfo funcInterface, BootstrapMethodInfo bootstrapMethod, String name, String[] fieldTypesName) {
+   ClassInfo enclosingClass = bootstrapMethod.enclosingClass;
+   this.classLoader = enclosingClass.classLoader;
+
+   this.name = name;
+   isClass = true;
+
+   superClassName = "java.lang.Object";
+
+   interfaceNames = new String[]{ funcInterface.name };    
+   packageName = enclosingClass.getPackageName();
+
+   // creating fields used to capture free variables
+   int n = fieldTypesName.length;
+   
+   iFields = new FieldInfo[n];
+   nInstanceFields = n;
+   
+   sFields = new FieldInfo[0];
+   staticDataSize = 0;
+   
+   int idx = 0;
+   int off = 0;  // no super class
+   
+   int i = 0;
+   for(String type: fieldTypesName) {
+     FieldInfo fi = FieldInfo.create("arg" + i++, type, 0);
+     fi.linkToClass(this, idx, off);
+     iFields[idx++] = fi;
+     off += fi.getStorageSize();
+   }
+   
+   linkFields();
+ }
+  
+  // since id and hence uniqueId are not set before this class is registered, we can't use them
+  
+  @Override
+  public int hashCode() {
+    return OATHash.hash(name.hashCode(), classLoader.hashCode());
+  }
+  
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof ClassInfo) {
+      ClassInfo other = (ClassInfo)o;
+      if (classLoader == other.classLoader) {
+        // beware of ClassInfos that are not registered yet - in this case we have to equals names
+        if (name.equals(other.name)) {
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+
+  protected String computeSourceFileName(){
+    return name.replace('.', '/') + ".java";
+  }
+
+  protected void checkUnresolvedNativeMethods(){
+    for (MethodInfo mi : methods.values()){
+      if (mi.isUnresolvedNativeMethod()){
+        NativeMethodInfo nmi = new NativeMethodInfo(mi, null, nativePeer);
+        nmi.replace(mi);
+      }
+    }
+  }
+
+  protected void processJPFConfigAnnotation() {
+    AnnotationInfo ai = getAnnotation("gov.nasa.jpf.annotation.JPFConfig");
+    if (ai != null) {
+      for (String s : ai.getValueAsStringArray()) {
+        config.parse(s);
+      }
+    }
+  }
+
+  protected void loadAnnotationListeners () {
+    if (autoloadAnnotations != null) {
+      autoloadListeners(annotations); // class annotations
+
+      for (int i=0; i<sFields.length; i++) {
+        autoloadListeners(sFields[i].getAnnotations());
+      }
+
+      for (int i=0; i<iFields.length; i++) {
+        autoloadListeners(iFields[i].getAnnotations());
+      }
+
+      // method annotations are checked during method loading
+      // (to avoid extra iteration)
+    }
+  }
+
+  void autoloadListeners(AnnotationInfo[] annos) {
+    if ((annos != null) && (autoloadAnnotations != null)) {
+      for (AnnotationInfo ai : annos) {
+        String aName = ai.getName();
+        if (autoloadAnnotations.contains(aName)) {
+          if (!autoloaded.contains(aName)) {
+            autoloaded.add(aName);
+            String key = "listener." + aName;
+            String defClsName = aName + "Checker";
+            try {
+              JPFListener listener = config.getInstance(key, JPFListener.class, defClsName);
+              
+              JPF jpf = VM.getVM().getJPF(); // <2do> that's a BAD access path
+              jpf.addUniqueTypeListener(listener);
+
+              if (logger.isLoggable(Level.INFO)){
+                logger.info("autoload annotation listener: @", aName, " => ", listener.getClass().getName());
+              }
+
+            } catch (JPFConfigException cx) {
+              logger.warning("no autoload listener class for annotation " + aName +
+                             " : " + cx.getMessage());
+              autoloadAnnotations.remove(aName);
+            }
+          }
+        }
+      }
+
+      if (autoloadAnnotations.isEmpty()) {
+        autoloadAnnotations = null;
+      }
+    }
+  }
+
+  protected NativePeer loadNativePeer(){
+    return NativePeer.getNativePeer(this);
+  }
+  
+  /**
+   * Returns the class loader that 
+   */
+  public ClassLoaderInfo getClassLoaderInfo() {
+    return classLoader;
+  }
+
+  /**
+   * the container this is stored in
+   */
+  public Statics getStatics() {
+    return classLoader.getStatics();
+  }
+  
+  /**
+   * required by InfoObject interface
+   */
+  public ClassInfo getClassInfo() {
+    return this;
+  }
+
+  protected void setAssertionStatus() {
+    if(isInitialized()) {
+      return;
+    } else {
+      enableAssertions = classLoader.desiredAssertionStatus(name);
+    }
+  }
+
+  boolean getAssertionStatus () {
+    return enableAssertions;
+  }
+
+  public boolean desiredAssertionStatus() {
+    return classLoader.desiredAssertionStatus(name);
+  }
+
+  @Override
+  public String getGenericSignature() {
+    return genericSignature;
+  }
+
+  @Override
+  public void setGenericSignature(String sig){
+    genericSignature = sig;
+  }
+  
+  public boolean isArray () {
+    return isArray;
+  }
+
+  public boolean isEnum () {
+    return isEnum;
+  }
+
+  public boolean isAbstract() {
+    return isAbstract;
+  }
+
+  public boolean isBuiltin(){
+    return isBuiltin;
+  }
+  
+  public boolean isInterface() {
+    return ((modifiers & Modifier.INTERFACE) != 0);
+  }
+
+  public boolean isReferenceArray () {
+    return isReferenceArray;
+  }
+
+  public boolean isObjectClassInfo() {
+    return isObjectClassInfo;
+  }
+
+  public boolean isStringClassInfo() {
+    return isStringClassInfo;
+  }
+
+  public boolean isThreadClassInfo() {
+    return isThreadClassInfo;
+  }
+
+  protected void checkNoClinitInitialization(){
+    if (!isInitialized()){
+      ThreadInfo ti = ThreadInfo.getCurrentThread();
+      registerClass(ti);
+      setInitialized(); // we might want to check if there is a clinit
+    }
+  }
+  
+  protected ClassInfo createAnnotationProxy (String proxyName){
+    // to be overridden by VM specific ClassInfos
+    return null;
+  }
+  
+  public ClassInfo getAnnotationProxy (){
+    // <2do> test if this is a annotation ClassInfo
+    
+    checkNoClinitInitialization(); // annotation classes don't have clinits
+    
+    ClassInfo ciProxy = classLoader.getResolvedAnnotationProxy(this);
+    ciProxy.checkNoClinitInitialization();
+    
+    return ciProxy;
+  }
+  
+/**
+  public static ClassInfo getAnnotationProxy (ClassInfo ciAnnotation){
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+
+    // make sure the annotationCls is initialized (no code there)
+    if (!ciAnnotation.isInitialized()) {
+      ciAnnotation.registerClass(ti);
+      ciAnnotation.setInitialized(); // no clinit
+    }
+
+    String url = computeProxyUrl(ciAnnotation);
+    ClassInfo ci = null; // getOriginalClassInfo(url);
+
+    if (ci == null){
+      String cname = ciAnnotation.getName() + "$Proxy";
+      ci = new ClassInfo(ciAnnotation, cname, ciAnnotation.classLoader, url);
+      ciAnnotation.classLoader.addResolvedClass(ci);
+      if (!ci.isInitialized()){
+        ci.registerClass(ti);
+        ci.setInitialized();
+      }
+    }
+
+    return ci;
+  }
+**/
+
+  public boolean areAssertionsEnabled() {
+    return enableAssertions;
+  }
+
+  public boolean hasInstanceFields () {
+    return (instanceDataSize > 0);
+  }
+
+  public ElementInfo getClassObject(){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei != null){
+      int objref = sei.getClassObjectRef();
+      return VM.getVM().getElementInfo(objref);
+    }
+
+    return null;
+  }
+  
+  public ElementInfo getModifiableClassObject(){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei != null){
+      int objref = sei.getClassObjectRef();
+      return VM.getVM().getModifiableElementInfo(objref);
+    }
+
+    return null;
+  }
+  
+
+  public int getClassObjectRef () {
+    StaticElementInfo sei = getStaticElementInfo();    
+    return (sei != null) ? sei.getClassObjectRef() : MJIEnv.NULL;
+  }
+
+  public gov.nasa.jpf.vm.ClassFileContainer getContainer(){
+    return container;
+  }
+  
+  public String getClassFileUrl (){
+    return classFileUrl;
+  }
+
+  //--- type based object release actions
+  
+  public boolean hasReleaseAction (ReleaseAction action){
+    return (releaseActions != null) && releaseActions.contains(action);
+  }
+  
+  /**
+   * NOTE - this can only be set *before* subclasses are loaded (e.g. from classLoaded() notification) 
+   */
+  public void addReleaseAction (ReleaseAction action){
+    // flattened in ctor to super releaseActions
+    releaseActions = new ImmutableList<ReleaseAction>( action, releaseActions);
+  }
+  
+  /**
+   * recursively process release actions registered for this type or any of
+   * its super types (only classes). The releaseAction list is flattened during
+   * ClassInfo initialization, to reduce runtime overhead during GC sweep
+   */
+  public void processReleaseActions (ElementInfo ei){
+    if (superClass != null){
+      superClass.processReleaseActions(ei);
+    }
+    
+    if (releaseActions != null) {
+      for (ReleaseAction action : releaseActions) {
+        action.release(ei);
+      }
+    }
+  }
+  
+  public int getModifiers() {
+    return modifiers;
+  }
+
+  /**
+   * Note that 'uniqueName' is the name plus the argument type part of the
+   * signature, idx.e. everything that's relevant for overloading
+   * (besides saving some const space, we also ease reverse lookup
+   * of natives that way).
+   * Note also that we don't have to make any difference between
+   * class and instance declaredMethods, because that just matters in the
+   * INVOKExx instruction, when looking up the relevant ClassInfo to start
+   * searching in (either by means of the object type, or by means of the
+   * constpool classname entry).
+   */
+  public MethodInfo getMethod (String uniqueName, boolean isRecursiveLookup) {
+    MethodInfo mi = methods.get(uniqueName);
+
+    if ((mi == null) && isRecursiveLookup && (superClass != null)) {
+      mi = superClass.getMethod(uniqueName, true);
+    }
+
+    return mi;
+  }
+
+  /**
+   * if we don't know the return type
+   * signature is in paren/dot notation
+   */
+  public MethodInfo getMethod (String name, String signature, boolean isRecursiveLookup) {
+    MethodInfo mi = null;
+    String matchName = name + signature;
+
+    for (Map.Entry<String, MethodInfo>e : methods.entrySet()) {
+      if (e.getKey().startsWith(matchName)){
+        mi = e.getValue();
+        break;
+      }
+    }
+
+    if ((mi == null) && isRecursiveLookup && (superClass != null)) {
+      mi = superClass.getMethod(name, signature, true);
+    }
+
+    return mi;
+  }
+
+  
+  public MethodInfo getDefaultMethod (String uniqueName) {
+    MethodInfo mi = null;
+    
+    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
+      for (ClassInfo ciIfc : ci.interfaces){
+        MethodInfo miIfc = ciIfc.getMethod(uniqueName, true);
+        if (miIfc != null && !miIfc.isAbstract()){
+          if (mi != null){
+            // this has to throw a IncompatibleClassChangeError in the client since Java prohibits ambiguous default methods
+            String msg = "Conflicting default methods: " + mi.getFullName() + ", " + miIfc.getFullName();
+            throw new ClassChangeException(msg);
+          } else {
+            mi = miIfc;
+          }
+        }
+      }
+    }
+    
+    return mi;
+  }
+  
+  /**
+   * This retrieves the SAM from this functional interface. Note that this is only
+   * called on functional interface expecting to have a SAM. This shouldn't expect 
+   * this interface to have only one method which is abstract, since:
+   *    1. functional interface can declare the abstract methods from the java.lang.Object 
+   *       class.
+   *    2. functional interface can extend another interface which is functional, but it 
+   *       should not declare any new abstract methods.
+   *    3. functional interface can have one abstract method and any number of default
+   *       methods.
+   * 
+   * To retrieve the SAM, this method iterates over the methods of this interface and its 
+   * superinterfaces, and it returns the first method which is abstract and it does not 
+   * declare a method in java.lang.Object.
+   */
+  public MethodInfo getInterfaceAbstractMethod () {
+    ClassInfo objCi = ClassLoaderInfo.getCurrentResolvedClassInfo("java.lang.Object");
+    
+    for(MethodInfo mi: this.methods.values()) {
+      if(mi.isAbstract() && objCi.getMethod(mi.getUniqueName(), false)==null) {
+        return mi;
+      }
+    }
+    
+    for (ClassInfo ifc : this.interfaces){
+      MethodInfo mi = ifc.getInterfaceAbstractMethod();
+      if(mi!=null) {
+        return mi;
+      }
+    }
+    
+    return null;
+  }
+
+  /**
+   * method lookup for use by reflection methods (java.lang.Class.getXMethod)
+   * 
+   * note this doesn't specify the return type, which means covariant return 
+   * types are not allowed in reflection lookup.
+   * 
+   * note also this includes interface methods, but only after the inheritance
+   * hierarchy has been searched
+   */
+  public MethodInfo getReflectionMethod (String fullName, boolean isRecursiveLookup) {
+        
+    // first look for methods within the class hierarchy
+    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
+      for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
+        String name = e.getKey();
+        if (name.startsWith(fullName)) {
+          return e.getValue();
+        }
+      }
+      if (!isRecursiveLookup){
+        return null;
+      }
+    }
+
+    // this is the recursive case - if none found, look for interface methods
+    for (ClassInfo ci : getAllInterfaces() ){
+      for (Map.Entry<String, MethodInfo>e : ci.methods.entrySet()) {
+        String name = e.getKey();
+        if (name.startsWith(fullName)) {
+          return e.getValue();
+        }
+      }      
+    }    
+
+    return null;
+  }
+  
+  /**
+   * iterate over all declaredMethods of this class (and it's superclasses), until
+   * the provided MethodLocator tells us it's done
+   */
+  public void matchMethods (MethodLocator loc) {
+    for (MethodInfo mi : methods.values()) {
+      if (loc.match(mi)) {
+        return;
+      }
+    }
+    if (superClass != null) {
+      superClass.matchMethods(loc);
+    }
+  }
+
+  /**
+   * iterate over all declaredMethods declared in this class, until the provided
+   * MethodLocator tells us it's done
+   */
+  public void matchDeclaredMethods (MethodLocator loc) {
+    for (MethodInfo mi : methods.values()) {
+      if (loc.match(mi)) {
+        return;
+      }
+    }
+  }
+
+  @Override
+  public Iterator<MethodInfo> iterator() {
+    return new Iterator<MethodInfo>() {
+      ClassInfo ci = ClassInfo.this;
+      Iterator<MethodInfo> it = ci.methods.values().iterator();
+
+      @Override
+       public boolean hasNext() {
+        if (it.hasNext()) {
+          return true;
+        } else {
+          if (ci.superClass != null) {
+            ci = ci.superClass;
+            it = ci.methods.values().iterator();
+            return it.hasNext();
+          } else {
+            return false;
+          }
+        }
+      }
+
+      @Override
+       public MethodInfo next() {
+        if (hasNext()) {
+          return it.next();
+        } else {
+          throw new NoSuchElementException();
+        }
+      }
+
+      @Override
+       public void remove() {
+        // not supported
+        throw new UnsupportedOperationException("can't remove methods");
+      }
+    };
+  }
+  
+  public Iterator<MethodInfo> declaredMethodIterator() {
+    return methods.values().iterator();
+  }
+
+  /**
+   * Search up the class hierarchy to find a static field
+   * @param fName name of field
+   * @return null if field name not found (not declared)
+   */
+  public FieldInfo getStaticField (String fName) {
+    FieldInfo fi;
+    ClassInfo c = this;
+
+    while (c != null) {
+      fi = c.getDeclaredStaticField(fName);
+      if (fi != null) {
+        return fi;
+      }
+      c = c.superClass;
+    }
+
+    //interfaceNames can have static fields too
+    // <2do> why would that not be already resolved here ?
+    for (ClassInfo ci : getAllInterfaces()) {
+      fi = ci.getDeclaredStaticField(fName);
+      if (fi != null) {
+        return fi;
+      }
+    }
+
+    return null;
+  }
+
+  public Object getStaticFieldValueObject (String id){
+    ClassInfo c = this;
+    Object v;
+
+    while (c != null){
+      ElementInfo sei = c.getStaticElementInfo();
+      v = sei.getFieldValueObject(id);
+      if (v != null){
+        return v;
+      }
+      c = c.getSuperClass();
+    }
+
+    return null;
+  }
+
+  public FieldInfo[] getDeclaredStaticFields() {
+    return sFields;
+  }
+
+  public FieldInfo[] getDeclaredInstanceFields() {
+    return iFields;
+  }
+
+  /**
+   * FieldInfo lookup in the static fields that are declared in this class
+   * <2do> pcm - should employ a map at some point, but it's usually not that
+   * important since we can cash the returned FieldInfo in the PUT/GET_STATIC insns
+   */
+  public FieldInfo getDeclaredStaticField (String fName) {
+    for (int i=0; i<sFields.length; i++) {
+      if (sFields[i].getName().equals(fName)) return sFields[i];
+    }
+
+    return null;
+  }
+
+  /**
+   * base relative FieldInfo lookup - the workhorse
+   * <2do> again, should eventually use Maps
+   * @param fName the field name
+   */
+  public FieldInfo getInstanceField (String fName) {
+    FieldInfo fi;
+    ClassInfo c = this;
+
+    while (c != null) {
+      fi = c.getDeclaredInstanceField(fName);
+      if (fi != null) return fi;
+      c = c.superClass;
+    }
+
+    return null;
+  }
+
+  /**
+   * FieldInfo lookup in the fields that are declared in this class
+   */
+  public FieldInfo getDeclaredInstanceField (String fName) {
+    for (int i=0; i<iFields.length; i++) {
+      if (iFields[i].getName().equals(fName)) return iFields[i];
+    }
+
+    return null;
+  }
+  
+  public String getSignature() {
+    if (signature == null) {
+      signature = Types.getTypeSignature(name, false);
+    }
+    
+    return signature;     
+  }
+
+  /**
+   * Returns the name of the class.  e.g. "java.lang.String".  similar to
+   * java.lang.Class.getName().
+   */
+  public String getName () {
+    return name;
+  }
+
+  public String getSimpleName () {
+    int i;
+    String enclosingClassName = getEnclosingClassName();
+    
+    if(enclosingClassName!=null){
+      i = enclosingClassName.length();      
+    } else{
+      i = name.lastIndexOf('.');
+    }
+    
+    return name.substring(i+1);
+  }
+
+  public String getPackageName () {
+    return packageName;
+  }
+
+  public int getId() {
+    return id;
+  }
+
+  public long getUniqueId() {
+    return uniqueId;
+  }
+
+  public int getFieldAttrs (int fieldIndex) {
+    fieldIndex = 0; // Get rid of IDE warning
+     
+    return 0;
+  }
+
+  public void setElementInfoAttrs (int attrs){
+    elementInfoAttrs = attrs;
+  }
+
+  public void addElementInfoAttr (int attr){
+    elementInfoAttrs |= attr;
+  }
+
+  public int getElementInfoAttrs () {
+    return elementInfoAttrs;
+  }
+
+  public Source getSource () {
+    if (source == null) {
+      source = loadSource();
+    }
+
+    return source;
+  }
+
+  public String getSourceFileName () {
+    return sourceFileName;
+  }
+
+  /**
+   * Returns the information about a static field.
+   */
+  public FieldInfo getStaticField (int index) {
+    return sFields[index];
+  }
+
+  /**
+   * Returns the name of a static field.
+   */
+  public String getStaticFieldName (int index) {
+    return getStaticField(index).getName();
+  }
+
+  /**
+   * Checks if a static method call is deterministic, but only for
+   * abtraction based determinism, due to Bandera.choose() calls
+   */
+  public boolean isStaticMethodAbstractionDeterministic (ThreadInfo th,
+                                                         MethodInfo mi) {
+    //    Reflection r = reflection.instantiate();
+    //    return r.isStaticMethodAbstractionDeterministic(th, mi);
+    // <2do> - still has to be implemented
+     
+    th = null;  // Get rid of IDE warning
+    mi = null;
+     
+    return true;
+  }
+
+  public String getSuperClassName() {
+    return superClassName;
+  }
+
+  /**
+   * Return the super class.
+   */
+  public ClassInfo getSuperClass () {
+    return superClass;
+  }
+
+  /**
+   * return the ClassInfo for the provided superclass name. If this is equals
+   * to ourself, return this (a little bit strange if we hit it in the first place)
+   */
+  public ClassInfo getSuperClass (String clsName) {
+    if (clsName.equals(name)) return this;
+
+    if (superClass != null) {
+      return superClass.getSuperClass(clsName);
+    } else {
+      return null;
+    }
+  }
+
+  public int getNumberOfSuperClasses(){
+    int n = 0;
+    for (ClassInfo ci = superClass; ci != null; ci = ci.superClass){
+      n++;
+    }
+    return n;
+  }
+  
+  /**
+   * beware - this loads (but not yet registers) the enclosing class
+   */
+  public String getEnclosingClassName(){
+    return enclosingClassName;
+  }
+  
+  /**
+   * beware - this loads (but not yet registers) the enclosing class
+   */
+  public ClassInfo getEnclosingClassInfo() {
+    String enclName = getEnclosingClassName();
+    return (enclName == null ? null : classLoader.getResolvedClassInfo(enclName)); // ? is this supposed to use the same classloader
+  }
+
+  public String getEnclosingMethodName(){
+    return enclosingMethodName;
+  }
+
+  /**
+   * same restriction as getEnclosingClassInfo() - might not be registered/initialized
+   */
+  public MethodInfo getEnclosingMethodInfo(){
+    MethodInfo miEncl = null;
+    
+    if (enclosingMethodName != null){
+      ClassInfo ciIncl = getEnclosingClassInfo();
+      miEncl = ciIncl.getMethod( enclosingMethodName, false);
+    }
+    
+    return miEncl;
+  }
+  
+  /**
+   * Returns true if the class is a system class.
+   */
+  public boolean isSystemClass () {
+    return name.startsWith("java.") || name.startsWith("javax.");
+  }
+
+  /**
+   * <2do> that's stupid - we should use subclasses for builtin and box types
+   */
+  public boolean isBoxClass () {
+    if (name.startsWith("java.lang.")) {
+      String rawType = name.substring(10);
+      if (rawType.startsWith("Boolean") ||
+          rawType.startsWith("Byte") ||
+          rawType.startsWith("Character") ||
+          rawType.startsWith("Integer") ||
+          rawType.startsWith("Float") ||
+          rawType.startsWith("Long") ||
+          rawType.startsWith("Double")) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * Returns the type of a class.
+   */
+  public String getType () {
+    if (!isArray) {
+      return "L" + name.replace('.', '/') + ";";
+    } else {
+      return name;
+    }
+  }
+
+  /**
+   * is this a (subclass of) WeakReference? this must be efficient, since it's
+   * called in the mark phase on all live objects
+   */
+  public boolean isWeakReference () {
+    return isWeakReference;
+  }
+
+  /**
+   * note this only returns true is this is really the java.lang.ref.Reference classInfo
+   */
+  public boolean isReferenceClassInfo () {
+    return isRefClassInfo;
+  }
+
+  /**
+   * whether this refers to a primitive type.
+   */
+  public boolean isPrimitive() {
+    return superClass == null && !isObjectClassInfo();
+  }
+
+
+  boolean hasRefField (int ref, Fields fv) {
+    ClassInfo c = this;
+
+    do {
+      FieldInfo[] fia = c.iFields;
+      for (int i=0; i<fia.length; i++) {
+        FieldInfo fi = c.iFields[i];
+        if (fi.isReference() && (fv.getIntValue( fi.getStorageOffset()) == ref)) return true;
+      }
+      c = c.superClass;
+    } while (c != null);
+
+    return false;
+  }
+
+  boolean hasImmutableInstances () {
+    return ((elementInfoAttrs & ElementInfo.ATTR_IMMUTABLE) != 0);
+  }
+
+  public boolean hasInstanceFieldInfoAttr (Class<?> type){
+    for (int i=0; i<nInstanceFields; i++){
+      if (getInstanceField(i).hasAttr(type)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public NativePeer getNativePeer () {
+    return nativePeer;
+  }
+  
+  /**
+   * Returns true if the given class is an instance of the class
+   * or interface specified.
+   */
+  public boolean isInstanceOf (String cname) {
+    if (isPrimitive()) {
+      return Types.getJNITypeCode(name).equals(cname);
+
+    } else {
+      cname = Types.getClassNameFromTypeName(cname);
+      ClassInfo ci = this.classLoader.getResolvedClassInfo(cname);
+      return isInstanceOf(ci);
+    }
+  }
+
+  /**
+   * Returns true if the given class is an instance of the class
+   * or interface specified.
+   */
+  public boolean isInstanceOf (ClassInfo ci) {
+    if (isPrimitive()) { // no inheritance for builtin types
+      return (this==ci);
+    } else {
+      for (ClassInfo c = this; c != null; c = c.superClass) {
+        if (c==ci) {
+          return true;
+        }
+      }
+
+      return getAllInterfaces().contains(ci);
+    }
+  }
+
+  public boolean isInnerClassOf (String enclosingName){
+    // don't register or initialize yet
+    ClassInfo ciEncl = classLoader.tryGetResolvedClassInfo( enclosingName);
+    if (ciEncl != null){
+      return ciEncl.hasInnerClass(name);
+    } else {
+      return false;
+    }
+  }
+  
+  public boolean hasInnerClass (String innerName){
+    for (int i=0; i<innerClassNames.length; i++){
+      if (innerClassNames[i].equals(innerName)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+
+
+  public static String makeModelClassPath (Config config) {
+    StringBuilder buf = new StringBuilder(256);
+    String ps = File.pathSeparator;
+    String v;
+
+    for (File f : config.getPathArray("boot_classpath")){
+      buf.append(f.getAbsolutePath());
+      buf.append(ps);
+    }
+
+    for (File f : config.getPathArray("classpath")){
+      buf.append(f.getAbsolutePath());
+      buf.append(ps);
+    }
+
+    // finally, we load from the standard Java libraries
+    v = System.getProperty("sun.boot.class.path");
+    if (v != null) {
+      buf.append(v);
+    }
+    
+    return buf.toString();
+  }
+  
+  protected static String[] loadArrayInterfaces () {
+    return new String[] {"java.lang.Cloneable", "java.io.Serializable"};
+  }
+
+  protected static String[] loadBuiltinInterfaces (String type) {
+    return EMPTY_STRING_ARRAY;
+  }
+
+
+  /**
+   * Loads the ClassInfo for named class.
+   */
+  void loadInterfaceRec (Set<ClassInfo> set, String[] interfaces) throws ClassInfoException {
+    if (interfaces != null) {
+      for (String iname : interfaces) {
+
+        ClassInfo ci = classLoader.getResolvedClassInfo(iname);
+
+        if (set != null){
+          set.add(ci);
+        }
+
+        loadInterfaceRec(set, ci.interfaceNames);
+      }
+    }
+  }
+
+  int computeInstanceDataOffset () {
+    if (superClass == null) {
+      return 0;
+    } else {
+      return superClass.getInstanceDataSize();
+    }
+  }
+
+  int getInstanceDataOffset () {
+    return instanceDataOffset;
+  }
+
+  ClassInfo getClassBase (String clsBase) {
+    if ((clsBase == null) || (name.equals(clsBase))) return this;
+
+    if (superClass != null) {
+      return superClass.getClassBase(clsBase);
+    }
+
+    return null; // Eeek - somebody asked for a class that isn't in the base list
+  }
+
+  int computeInstanceDataSize () {
+    int n = getDataSize( iFields);
+
+    for (ClassInfo c=superClass; c!= null; c=c.superClass) {
+      n += c.getDataSize(c.iFields);
+    }
+
+    return n;
+  }
+
+  public int getInstanceDataSize () {
+    return instanceDataSize;
+  }
+
+  int getDataSize (FieldInfo[] fields) {
+    int n=0;
+    for (int i=0; i<fields.length; i++) {
+      n += fields[i].getStorageSize();
+    }
+
+    return n;
+  }
+
+  public int getNumberOfDeclaredInstanceFields () {
+    return iFields.length;
+  }
+
+  public FieldInfo getDeclaredInstanceField (int i) {
+    return iFields[i];
+  }
+
+  public int getNumberOfInstanceFields () {
+    return nInstanceFields;
+  }
+
+  public FieldInfo getInstanceField (int i) {
+    int idx = i - (nInstanceFields - iFields.length);
+    if (idx >= 0) {
+      return ((idx < iFields.length) ? iFields[idx] : null);
+    } else {
+      return ((superClass != null) ? superClass.getInstanceField(i) : null);
+    }
+  }
+
+  public FieldInfo[] getInstanceFields(){
+    FieldInfo[] fields = new FieldInfo[nInstanceFields];
+    
+    for (int i=0; i<fields.length; i++){
+      fields[i] = getInstanceField(i);
+    }
+    
+    return fields;
+  }
+
+  public int getStaticDataSize () {
+    return staticDataSize;
+  }
+
+  int computeStaticDataSize () {
+    return getDataSize(sFields);
+  }
+
+  public int getNumberOfStaticFields () {
+    return sFields.length;
+  }
+
+  protected Source loadSource () {
+    return Source.getSource(sourceFileName);
+  }
+
+  public static boolean isBuiltinClass (String cname) {
+    char c = cname.charAt(0);
+
+    // array class
+    if ((c == '[') || cname.endsWith("[]")) {
+      return true;
+    }
+
+    // primitive type class
+    if (Character.isLowerCase(c)) {
+      if ("int".equals(cname) || "byte".equals(cname) ||
+          "boolean".equals(cname) || "double".equals(cname) ||
+          "long".equals(cname) || "char".equals(cname) ||
+          "short".equals(cname) || "float".equals(cname) || "void".equals(cname)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * set the locations where we look up sources
+   */
+  static void setSourceRoots (Config config) {
+    Source.init(config);
+  }
+
+  /**
+   * get names of all interfaceNames (transitive, idx.e. incl. bases and super-interfaceNames)
+   * @return a Set of String interface names
+   */
+  public Set<ClassInfo> getAllInterfaces () {
+    if (allInterfaces == null) {
+      HashSet<ClassInfo> set = new HashSet<ClassInfo>();
+
+      for (ClassInfo ci=this; ci != null; ci=ci.superClass) {
+        loadInterfaceRec(set, ci.interfaceNames);
+      }
+
+      allInterfaces = Collections.unmodifiableSet(set);
+    }
+
+    return allInterfaces;
+  }
+
+  /**
+   * get names of directly implemented interfaceNames
+   */
+  public String[] getDirectInterfaceNames () {
+    return interfaceNames;
+  }
+
+  public Set<ClassInfo> getInterfaceClassInfos() {
+    return interfaces;
+  }
+
+  public Set<ClassInfo> getAllInterfaceClassInfos() {
+    return getAllInterfaces();
+  }
+
+  
+  /**
+   * get names of direct inner classes
+   */
+  public String[] getInnerClasses(){
+    return innerClassNames;
+  }
+  
+  public ClassInfo[] getInnerClassInfos(){
+    ClassInfo[] innerClassInfos = new ClassInfo[innerClassNames.length];
+    
+    for (int i=0; i< innerClassNames.length; i++){
+      innerClassInfos[i] = classLoader.getResolvedClassInfo(innerClassNames[i]); // ? is this supposed to use the same classloader
+    }
+    
+    return innerClassInfos;
+  }
+  
+  public BootstrapMethodInfo getBootstrapMethodInfo(int index) {
+    return bootstrapMethods[index];
+  }
+
+  public ClassInfo getComponentClassInfo () {
+    if (isArray()) {
+      String cn = name.substring(1);
+
+      if (cn.charAt(0) != '[') {
+        cn = Types.getTypeName(cn);
+      }
+
+      ClassInfo cci = classLoader.getResolvedClassInfo(cn);
+
+      return cci;
+    }
+
+    return null;
+  }
+
+  /**
+   * most definitely not a public method, but handy for the NativePeer
+   */
+  protected Map<String, MethodInfo> getDeclaredMethods () {
+    return methods;
+  }
+
+  /**
+   * be careful, this replaces or adds MethodInfos dynamically
+   */
+  public MethodInfo putDeclaredMethod (MethodInfo mi){
+    return methods.put(mi.getUniqueName(), mi);
+  }
+
+  public MethodInfo[] getDeclaredMethodInfos() {
+    MethodInfo[] a = new MethodInfo[methods.size()];
+    methods.values().toArray(a);
+    return a;
+  }
+
+  public Instruction[] getMatchingInstructions (LocationSpec lspec){
+    Instruction[] insns = null;
+
+    if (lspec.matchesFile(sourceFileName)){
+      for (MethodInfo mi : methods.values()) {
+        Instruction[] a = mi.getMatchingInstructions(lspec);
+        if (a != null){
+          if (insns != null) {
+            // not very efficient but probably rare
+            insns = Misc.appendArray(insns, a);
+          } else {
+            insns = a;
+          }
+
+          // little optimization
+          if (!lspec.isLineInterval()) {
+            break;
+          }
+        }
+      }
+    }
+
+    return insns;
+  }
+
+  public List<MethodInfo> getMatchingMethodInfos (MethodSpec mspec){
+    ArrayList<MethodInfo> list = null;
+    if (mspec.matchesClass(name)) {
+      for (MethodInfo mi : methods.values()) {
+        if (mspec.matches(mi)) {
+          if (list == null) {
+            list = new ArrayList<MethodInfo>();
+          }
+          list.add(mi);
+        }
+      }
+    }
+    return list;
+  }
+
+  public MethodInfo getFinalizer () {
+    return finalizer;
+  }
+
+  public MethodInfo getClinit() {
+    // <2do> braindead - cache
+    for (MethodInfo mi : methods.values()) {
+      if ("<clinit>".equals(mi.getName())) {
+        return mi;
+      }
+    }
+    return null;
+  }
+
+  public boolean hasCtors() {
+    // <2do> braindead - cache
+    for (MethodInfo mi : methods.values()) {
+      if ("<init>".equals(mi.getName())) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  /**
+   * see getInitializedClassInfo() for restrictions.
+   */
+  public static ClassInfo getInitializedSystemClassInfo (String clsName, ThreadInfo ti){
+    ClassLoaderInfo systemLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
+    ClassInfo ci = systemLoader.getResolvedClassInfo(clsName);
+    ci.initializeClassAtomic(ti);
+
+    return ci;
+  }
+
+  /**
+   * this one is for clients that need to synchronously get an initialized classinfo.
+   * NOTE: we don't handle clinits here. If there is one, this will throw
+   * an exception. NO STATIC BLOCKS / FIELDS ALLOWED
+   */
+  public static ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
+    ClassLoaderInfo cl = ClassLoaderInfo.getCurrentClassLoader();
+    ClassInfo ci = cl.getResolvedClassInfo(clsName);
+    ci.initializeClassAtomic(ti);
+
+    return ci;
+  }
+
+  public boolean isRegistered () {
+    //return (id != -1);
+    return getStaticElementInfo() != null;
+  }
+  
+  /**
+   * this registers a ClassInfo in the corresponding ClassLoader statics so that we can cross-link from
+   * SUT code and access static fields.
+   */
+  public StaticElementInfo registerClass (ThreadInfo ti){
+    StaticElementInfo sei = getStaticElementInfo();
+    
+    if (sei == null) {
+      // do this recursively for superclasses and interfaceNames
+      // respective classes might be defined by another classloader, so we have to call their ClassInfo.registerClass()
+      
+      if (superClass != null) {
+        superClass.registerClass(ti);
+      }
+
+      for (ClassInfo ifc : interfaces) {
+        ifc.registerClass(ti);
+      }
+      
+      ClassInfo.logger.finer("registering class: ", name);
+      
+      ElementInfo ei = createClassObject( ti);
+      sei = createAndLinkStaticElementInfo( ti, ei);
+      
+      // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
+      ti.getVM().notifyClassLoaded(this);
+    }
+    
+    return sei;
+  }
+
+  ElementInfo createClassObject (ThreadInfo ti){
+    Heap heap = VM.getVM().getHeap(); // ti can be null (during main thread initialization)
+
+    int anchor = name.hashCode(); // 2do - this should also take the ClassLoader ref into account
+
+    SystemClassLoaderInfo systemClassLoader = ti.getSystemClassLoaderInfo();
+
+    ClassInfo classClassInfo = systemClassLoader.getClassClassInfo();    
+    ElementInfo ei = heap.newSystemObject(classClassInfo, ti, anchor);
+    int clsObjRef = ei.getObjectRef();
+    
+    ElementInfo eiClsName = heap.newSystemString(name, ti, clsObjRef);
+    ei.setReferenceField("name", eiClsName.getObjectRef());
+
+    ei.setBooleanField("isPrimitive", isPrimitive());
+    
+    // setting the ID_FIELD is done in registerClass once we have a StaticElementInfo
+
+    // link the SUT class object to the classloader 
+    ei.setReferenceField("classLoader", classLoader.getClassLoaderObjectRef());
+    
+    return ei;
+  }
+  
+  StaticElementInfo createAndLinkStaticElementInfo (ThreadInfo ti, ElementInfo eiClsObj) {
+    Statics statics = classLoader.getStatics();
+    StaticElementInfo sei = statics.newClass(this, ti, eiClsObj);
+    
+    id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
+    uniqueId = ((long)classLoader.getId() << 32) | id;
+    
+    eiClsObj.setIntField( ID_FIELD, id);      
+    
+    return sei;
+  }
+
+  
+  // for startup classes, the order of initialization is reversed since we can't create
+  // heap objects before we have a minimal set of registered classes
+  
+  void registerStartupClass(ThreadInfo ti, List<ClassInfo> list) {
+    if (!isRegistered()) {
+      // do this recursively for superclasses and interfaceNames
+      // respective classes might be defined by another classloader, so we have
+      // to call their ClassInfo.registerClass()
+
+      if (superClass != null) {
+        superClass.registerStartupClass(ti, list);
+      }
+
+      for (ClassInfo ifc : interfaces) {
+        ifc.registerStartupClass(ti, list);
+      }
+    }
+
+    if (!list.contains(this)) {
+      list.add(this);
+      ClassInfo.logger.finer("registering startup class: ", name);
+      createStartupStaticElementInfo(ti);
+    }
+    
+      // SUT class is fully resolved and registered (but not necessarily initialized), notify listeners
+      ti.getVM().notifyClassLoaded(this);
+  }
+  
+  StaticElementInfo createStartupStaticElementInfo (ThreadInfo ti) {
+    Statics statics = classLoader.getStatics();
+    StaticElementInfo sei = statics.newStartupClass(this, ti);
+    
+    id = sei.getObjectRef();  // kind of a misnomer, it's really an id    
+    uniqueId = ((long)classLoader.getId() << 32) | id;
+    
+    return sei;
+  }
+  
+  ElementInfo createAndLinkStartupClassObject (ThreadInfo ti) {
+    StaticElementInfo sei = getStaticElementInfo();
+    ElementInfo ei = createClassObject(ti);
+    
+    sei.setClassObjectRef(ei.getObjectRef());
+    ei.setIntField( ID_FIELD, id);      
+    
+    return ei;
+  }
+  
+  boolean checkIfValidClassClassInfo() {
+    return getDeclaredInstanceField( ID_FIELD) != null;
+  }
+  
+  public boolean isInitializing () {
+    StaticElementInfo sei = getStaticElementInfo();
+    return ((sei != null) && (sei.getStatus() >= 0));
+  }
+
+  /**
+   * note - this works recursively upwards since there might
+   * be a superclass with a clinit that is still executing
+   */
+  public boolean isInitialized () {
+    for (ClassInfo ci = this; ci != null; ci = ci.superClass){
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      if (sei == null || sei.getStatus() != INITIALIZED){
+        return false;
+      }
+    }
+    
+    return true;
+  }
+
+  public boolean isResolved () {
+    return (!isObjectClassInfo() && superClass != null);
+  }
+
+  public boolean needsInitialization (ThreadInfo ti){
+    StaticElementInfo sei = getStaticElementInfo();
+    if (sei != null){
+      int status = sei.getStatus();
+      if (status == INITIALIZED || status == ti.getId()){
+        return false;
+      }
+    }
+
+    return true;
+  }
+
+  public void setInitializing(ThreadInfo ti) {
+    StaticElementInfo sei = getModifiableStaticElementInfo();
+    sei.setStatus(ti.getId());
+  }
+  
+  /**
+   * initialize this class and its superclasses (but not interfaces)
+   * this will cause execution of clinits of not-yet-initialized classes in this hierarchy
+   *
+   * note - we don't treat registration/initialization of a class as
+   * a sharedness-changing operation since it is done automatically by
+   * the VM and the triggering action in the SUT (e.g. static field access or method call)
+   * is the one that should update sharedness and/or break the transition accordingly
+   *
+   * @return true - if initialization pushed DirectCallStackFrames and caller has to re-execute
+   */
+  public boolean initializeClass(ThreadInfo ti){
+    int pushedFrames = 0;
+
+    // push clinits of class hierarchy (upwards, since call stack is LIFO)
+    for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      if (sei == null){
+        sei = ci.registerClass(ti);
+      }
+
+      int status = sei.getStatus();
+      if (status != INITIALIZED){
+        // we can't do setInitializing() yet because there is no global lock that
+        // covers the whole clinit chain, and we might have a context switch before executing
+        // a already pushed subclass clinit - there can be races as to which thread
+        // does the static init first. Note this case is checked in INVOKECLINIT
+        // (which is one of the reasons why we have it).
+
+        if (status != ti.getId()) {
+          // even if it is already initializing - if it does not happen in the current thread
+          // we have to sync, which we do by calling clinit
+          MethodInfo mi = ci.getMethod("<clinit>()V", false);
+          if (mi != null) {
+            DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
+            ti.pushFrame( frame);
+            pushedFrames++;
+
+          } else {
+            // it has no clinit, we can set it initialized
+            ci.setInitialized();
+          }
+        } else {
+          // ignore if it's already being initialized  by our own thread (recursive request)
+        }
+      } else {
+        break; // if this class is initialized, so are its superclasses
+      }
+    }
+
+    return (pushedFrames > 0);
+  }
+
+  /**
+   * use this with care since it will throw a JPFException if we encounter a choice point
+   * during execution of clinits
+   * Use this mostly for wrapper exceptions and other system classes that are guaranteed to load
+   */
+  public void initializeClassAtomic (ThreadInfo ti){
+    for (ClassInfo ci = this; ci != null; ci = ci.getSuperClass()) {
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      if (sei == null){
+        sei = ci.registerClass(ti);
+      }
+
+      int status = sei.getStatus();
+      if (status != INITIALIZED && status != ti.getId()){
+          MethodInfo mi = ci.getMethod("<clinit>()V", false);
+          if (mi != null) {
+            DirectCallStackFrame frame = ci.createDirectCallStackFrame(ti, mi, 0);
+            ti.executeMethodAtomic(frame);
+          } else {
+            ci.setInitialized();
+          }
+      } else {
+        break; // if this class is initialized, so are its superclasses
+      }
+    }
+  }
+
+  public void setInitialized() {
+    StaticElementInfo sei = getStaticElementInfo();
+    if (sei != null && sei.getStatus() != INITIALIZED){
+      sei = getModifiableStaticElementInfo();
+      sei.setStatus(INITIALIZED);
+
+      // we don't emit classLoaded() notifications for non-builtin classes
+      // here anymore because it would be confusing to get instructionExecuted()
+      // notifications from the <clinit> execution before the classLoaded()
+    }
+  }
+
+  public StaticElementInfo getStaticElementInfo() {
+    if (id != -1) {
+      return classLoader.getStatics().get( id);
+    } else {
+      return null;
+    }
+  }
+
+  public StaticElementInfo getModifiableStaticElementInfo() {
+    if (id != -1) {
+      return classLoader.getStatics().getModifiable( id);
+    } else {
+      return null;      
+    }
+  }
+
+  Fields createArrayFields (String type, int nElements, int typeSize, boolean isReferenceArray) {
+    return fieldsFactory.createArrayFields( type, this,
+                                            nElements, typeSize, isReferenceArray);
+  }
+
+  /**
+   * Creates the fields for a class.  This gets called during registration of a ClassInfo
+   */
+  Fields createStaticFields () {
+    return fieldsFactory.createStaticFields(this);
+  }
+
+  void initializeStaticData (ElementInfo ei, ThreadInfo ti) {
+    for (int i=0; i<sFields.length; i++) {
+      FieldInfo fi = sFields[i];
+      fi.initialize(ei, ti);
+    }
+  }
+
+  /**
+   * Creates the fields for an object.
+   */
+  public Fields createInstanceFields () {
+    return fieldsFactory.createInstanceFields(this);
+  }
+
+  void initializeInstanceData (ElementInfo ei, ThreadInfo ti) {
+    // Note this is only used for field inits, and array elements are not fields!
+    // Since Java has only limited element init requirements (either 0 or null),
+    // we do this ad hoc in the ArrayFields ctor
+
+    // the order of inits should not matter, since this is only
+    // for constant inits. In case of a "class X { int a=42; int b=a; ..}"
+    // we have a explicit "GETFIELD a, PUTFIELD b" in the ctor, but to play it
+    // safely we init top down
+
+    if (superClass != null) { // do superclasses first
+      superClass.initializeInstanceData(ei, ti);
+    }
+
+    for (int i=0; i<iFields.length; i++) {
+      FieldInfo fi = iFields[i];
+      fi.initialize(ei, ti);
+    }
+  }
+
+  Map<String, MethodInfo> loadArrayMethods () {
+    return new HashMap<String, MethodInfo>(0);
+  }
+
+  Map<String, MethodInfo> loadBuiltinMethods (String type) {
+    type = null;  // Get rid of IDE warning 
+     
+    return new HashMap<String, MethodInfo>(0);
+  }
+
+  protected ClassInfo loadSuperClass (String superName) throws ClassInfoException {
+    if (isObjectClassInfo()) {
+      return null;
+    }
+
+    logger.finer("resolving superclass: ", superName, " of ", name);
+
+    // resolve the superclass
+    ClassInfo sci = resolveReferencedClass(superName);
+
+    return sci;
+  }
+
+  protected Set<ClassInfo> loadInterfaces (String[] ifcNames) throws ClassInfoException {
+    if (ifcNames == null || ifcNames.length == 0){
+      return NO_INTERFACES;
+      
+    } else {
+      Set<ClassInfo> set = new HashSet<ClassInfo>();
+
+      for (String ifcName : ifcNames) {
+        ClassInfo.logger.finer("resolving interface: ", ifcName, " of ", name);
+        ClassInfo ifc = resolveReferencedClass(ifcName);
+        set.add(ifc);
+      }
+
+      return set;
+    }
+  }
+  
+  /**
+   * loads superclass and direct interfaces, and computes information
+   * that depends on them
+   */
+  protected void resolveClass() {
+    if (!isObjectClassInfo){
+      superClass = loadSuperClass(superClassName);
+      releaseActions = superClass.releaseActions;
+    }
+    interfaces = loadInterfaces(interfaceNames);
+
+    //computeInheritedAnnotations(superClass);
+
+    isWeakReference = isWeakReference0();
+    isEnum = isEnum0();
+  }
+
+  /**
+   * get a ClassInfo for a referenced type that is resolved with the same classLoader, but make
+   * sure we only do this once per path
+   * 
+   * This method is called by the following bytecode instructions:
+   * anewarray, checkcast, getstatic, instanceof, invokespecial, 
+   * invokestatic, ldc, ldc_w, multianewarray, new, and putstatic
+   * 
+   * It loads the class referenced by these instructions and adds it to the 
+   * resolvedClasses map of the classLoader
+   */
+  public ClassInfo resolveReferencedClass(String cname) {
+    if(name.equals(cname)) {
+      return this;
+    }
+
+    // if the class has been already resolved just return it
+    ClassInfo ci = classLoader.getAlreadyResolvedClassInfo(cname);
+    if(ci != null) {
+      return ci;
+    }
+    // The defining class loader of the class initiate the load of referenced classes
+    ci = classLoader.loadClass(cname);
+    classLoader.addResolvedClass(ci);
+
+    return ci;
+  }
+
+  protected int linkFields (FieldInfo[] fields, int idx, int off){
+    for (FieldInfo fi: fields) {      
+      fi.linkToClass(this, idx, off);
+      
+      int storageSize = fi.getStorageSize();      
+      off += storageSize;
+      idx++;
+    }
+    
+    return off;
+  }
+  
+  protected void linkFields() {
+    //--- instance fields
+    if(superClass != null) {
+      int superDataSize = superClass.instanceDataSize;
+      instanceDataSize = linkFields( iFields,  superClass.nInstanceFields, superDataSize);
+      nInstanceFields = superClass.nInstanceFields + iFields.length;
+      instanceDataOffset = superClass.instanceDataSize;
+      
+    } else {
+      instanceDataSize = linkFields( iFields, 0, 0);
+      nInstanceFields = iFields.length;
+      instanceDataOffset = 0;
+    }
+    
+    //--- static fields
+    staticDataSize = linkFields( sFields, 0, 0);
+  }
+
+  // this resolves all annotations in this class hierarchy, which sets inherited attributes
+  protected void checkInheritedAnnotations (){
+    
+  }
+  
+  @Override
+  public String toString() {
+    return "ClassInfo[name=" + name + "]";
+  }
+
+  protected MethodInfo getFinalizer0 () {
+    MethodInfo mi = getMethod("finalize()V", true);
+
+    // we are only interested in non-empty method bodies, Object.finalize()
+    // is a dummy
+    if ((mi != null) && (!mi.getClassInfo().isObjectClassInfo())) {
+      return mi;
+    }
+
+    return null;
+  }
+
+  protected boolean isObjectClassInfo0 () {
+       if (name.equals("java.lang.Object")) {
+         return true;
+       }
+       return false;
+  }
+
+  protected boolean isStringClassInfo0 () {
+    if(name.equals("java.lang.String")) {
+      return true;
+    }
+    return false;
+  }
+
+  protected boolean isRefClassInfo0 () {
+    if(name.equals("java.lang.ref.Reference")) {
+      return true;
+    }
+    return false;
+  }
+
+  protected boolean isWeakReference0 () {
+       if(name.equals("java.lang.ref.WeakReference")) {
+      return true;
+       }
+
+    for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
+      if (ci.isWeakReference()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean isEnum0 () {
+       if(name.equals("java.lang.Enum")) {
+      return true;
+       }
+
+    for (ClassInfo ci = this; !ci.isObjectClassInfo(); ci = ci.superClass) {
+      if (ci.isEnum()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean isThreadClassInfo0 () {
+    if(name.equals("java.lang.Thread")) {
+      return true;
+    }
+    return false;
+  }
+
+
+  /**
+   * It creates an instance from a original ClassInfo instance. It doesn't copy sei & 
+   * uniqueId.
+   * 
+   * It is used for the cases where cl tries to load a class that the original version 
+   * of which has been loaded by some other classloader.
+   */
+  public ClassInfo cloneFor (ClassLoaderInfo cl) {
+    ClassInfo ci;
+
+    try {
+      ci = (ClassInfo)clone();
+
+      ci.classLoader = cl;
+      ci.interfaces = new HashSet<ClassInfo>();
+      ci.resolveClass();
+
+      ci.id = -1;
+      ci.uniqueId = -1;
+
+      if (methods != Collections.EMPTY_MAP){
+        ci.methods = (Map<String, MethodInfo>)((HashMap<String, MethodInfo>) methods).clone();
+      }
+
+      for(Map.Entry<String, MethodInfo> e: ci.methods.entrySet()) {
+        MethodInfo mi = e.getValue();
+        e.setValue(mi.getInstanceFor(ci));
+      }
+
+      ci.iFields = new FieldInfo[iFields.length];
+      for(int i=0; i<iFields.length; i++) {
+        ci.iFields[i] = iFields[i].getInstanceFor(ci);
+      }
+
+      ci.sFields = new FieldInfo[sFields.length];
+      for(int i=0; i<sFields.length; i++) {
+        ci.sFields[i] = sFields[i].getInstanceFor(ci);
+      }
+
+      if(nativePeer != null) {
+        ci.nativePeer = NativePeer.getNativePeer(ci);
+      }
+
+      ci.setAssertionStatus();
+
+    } catch (CloneNotSupportedException cnsx){
+      cnsx.printStackTrace();
+      return null;
+    }
+
+    VM.getVM().notifyClassLoaded(ci);
+    return ci;
+  }
+  
+  // <2do> should be abstract
+  public StackFrame createStackFrame (ThreadInfo ti, MethodInfo callee){
+    return null;
+  }
+  
+  public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, MethodInfo callee, int nLocalSlots){
+    return null;
+  }
+  
+  public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti, MethodInfo miRun){
+    return null;
+  }
+}
+
+
diff --git a/src/main/gov/nasa/jpf/vm/ClassInfoException.java b/src/main/gov/nasa/jpf/vm/ClassInfoException.java
new file mode 100644 (file)
index 0000000..6a86f96
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *
+ * This unchecked exception is thrown by the host VM. It captures all errors
+ * and exceptions that can occur at the load of a JPF class, which includes 
+ * defining, and resolving it. Here are some of the scenarios that this 
+ * exception is thrown and captures the corresponding exception:
+ *
+ *    * if the representation does not represent a class with the requested 
+ *      name, loading throws an instance of NoClassDefFoundError
+ *
+ *    * if any of the superclasses of a class, is the class itself, or if 
+ *      any of the superinterfaces of an interface, is the interface itself, 
+ *      loading throws an instance of ClassCircularityError
+ *
+ *    * if the representation is not a ClassFile structure, loading throws an
+ *      instance of ClassFormatError
+ * 
+ * If this exception is thrown during the initialization of VM, and the failed
+ * class is a system class, or creating of the main thread in not successful, we 
+ * immediately bail out by throwing JPFException.
+ * 
+ * While JPF is running, this error is handled by throwing an exception at the 
+ * SUT level. This exception is handled if it is thrown by 
+ * 
+ *    1. a native peer method,  
+ *    2. Intruction.execute(),
+ *    3. ThreadInfo.creatAndThrowException()
+ *    4. VM.initialize() // here it is handled only if it a non-system class
+ * 
+ * If this exception is thrown by a Listener, the host VM throws JPFListenerException.
+ * 
+ */
+public class ClassInfoException extends RuntimeException{
+
+  ClassLoaderInfo classLoader;
+  String exceptionClass; // how we map this into the SUT (i.e. the JPF side)
+  String failedClass;
+
+  public ClassInfoException(String details, ClassLoaderInfo cl, String exceptionClass, String faildClass) {
+    super(details);
+    this.classLoader = cl;
+    this.exceptionClass = exceptionClass;
+    this.failedClass = faildClass;
+  }
+
+  public ClassInfoException (String details, ClassLoaderInfo cl, String exceptionClass, String faildClass, Throwable cause) {
+    super(details, cause);
+    this.classLoader = cl;
+    this.exceptionClass = exceptionClass;
+    this.failedClass = faildClass;
+  }
+
+  
+  public boolean checkSystemClassFailure() {
+    return (failedClass.startsWith("java."));
+  }
+
+  public ClassLoaderInfo getClassLoaderInfo() {
+    return classLoader;
+  }
+
+  public String getFailedClass() {
+    return failedClass;
+  }
+
+  public String getExceptionClass() {
+    return exceptionClass;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java b/src/main/gov/nasa/jpf/vm/ClassLoaderInfo.java
new file mode 100644 (file)
index 0000000..07fbf5f
--- /dev/null
@@ -0,0 +1,862 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.jar.JarEntry;
+import java.util.jar.JarFile;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.SystemAttribute;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.SparseIntVector;
+import gov.nasa.jpf.util.StringSetMatcher;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *  
+ * Represents the classloader construct in VM which is responsible for loading
+ * classes.
+ */
+public class ClassLoaderInfo 
+     implements Iterable<ClassInfo>, Comparable<ClassLoaderInfo>, Cloneable, Restorable<ClassLoaderInfo> {
+
+  static JPFLogger log = JPF.getLogger("class");
+
+  
+  // the model class field name where we store our id 
+  protected static final String ID_FIELD = "nativeId";
+
+  protected static Config config;
+
+  // this is where we keep the global list of classloader ids
+  protected static SparseIntVector globalCLids;
+  
+  /**
+   * Map from class file URLs to first ClassInfo that was read from it. This search
+   * global map is used to make sure we only read class files once
+   */
+  protected static Map<String,ClassInfo> loadedClasses;
+  
+  /**
+   * map from annotation class file URLs to AnnotationInfos, which have a separate JPF internal
+   * representation. Again, using a global map ensures we only read the related class files once
+   */
+  protected static Map<String,AnnotationInfo> loadedAnnotations;
+  
+  // Map that keeps the classes defined (directly loaded) by this loader and the
+  // ones that are resolved from these defined classes
+  protected Map<String,ClassInfo> resolvedClasses;
+
+  // annotations directly loaded by this classloader
+  protected Map<String,AnnotationInfo> resolvedAnnotations;
+  
+  // Represents the locations where this classloader can load classes form - has to be populated subclasses 
+  protected ClassPath cp;
+
+  // The type of the corresponding class loader object
+  protected ClassInfo classInfo;
+
+  // The area containing static fields and  classes
+  protected Statics statics;
+
+  protected boolean roundTripRequired = false;
+
+  // Search global id, which is the basis for canonical order of classloaders
+  protected int id;
+
+  // The java.lang.ClassLoader object reference
+  protected int objRef;
+
+  protected ClassLoaderInfo parent;
+
+  
+  static class ClMemento implements Memento<ClassLoaderInfo> {
+    // note that we don't have to store the invariants (gid, parent, isSystemClassLoader)
+    ClassLoaderInfo cl;
+    Memento<Statics> staticsMemento;
+    Memento<ClassPath> cpMemento;
+    Map<String, Boolean> classAssertionStatus;
+    Map<String, Boolean> packageAssertionStatus;
+    boolean defaultAssertionStatus;
+    boolean isDefaultSet;
+
+    ClMemento (ClassLoaderInfo cl){
+      this.cl = cl;
+      staticsMemento = cl.statics.getMemento();
+      cpMemento = cl.cp.getMemento();
+      classAssertionStatus = new HashMap<String, Boolean>(cl.classAssertionStatus);
+      packageAssertionStatus = new HashMap<String, Boolean>(cl.packageAssertionStatus);
+      defaultAssertionStatus = cl.defaultAssertionStatus;
+      isDefaultSet = cl.isDefaultSet;
+    }
+
+    @Override
+       public ClassLoaderInfo restore(ClassLoaderInfo ignored) {
+      staticsMemento.restore(cl.statics);
+      cpMemento.restore(null);
+      cl.classAssertionStatus = this.classAssertionStatus;
+      cl.packageAssertionStatus = this.packageAssertionStatus;
+      cl.defaultAssertionStatus = this.defaultAssertionStatus;
+      cl.isDefaultSet = this.isDefaultSet;
+      return cl;
+    }
+  }
+
+  /**
+   * This is invoked by VM.initSubsystems()
+   */
+  static void init (Config config) {
+    ClassLoaderInfo.config = config;
+
+    globalCLids = new SparseIntVector();
+    loadedClasses = new HashMap<String,ClassInfo>(); // not sure we actually want this for multiple runs (unless we check file stamps)
+    loadedAnnotations = new HashMap<String,AnnotationInfo>();
+    
+    enabledAssertionPatterns = StringSetMatcher.getNonEmpty(config.getStringArray("vm.enable_assertions"));
+    disabledAssertionPatterns = StringSetMatcher.getNonEmpty(config.getStringArray("vm.disable_assertions"));
+  }
+    
+  public static int getNumberOfLoadedClasses (){
+    return loadedClasses.size();
+  }
+  
+  public static ClassInfo getCurrentResolvedClassInfo (String clsName){
+    ClassLoaderInfo cl = getCurrentClassLoader();
+    return cl.getResolvedClassInfo(clsName);
+  }
+
+  public static ClassInfo getSystemResolvedClassInfo (String clsName){
+    ClassLoaderInfo cl = getCurrentSystemClassLoader();
+    return cl.getResolvedClassInfo(clsName);
+  }
+   
+  /**
+   * for use from SystemClassLoaderInfo ctor, which doesn't have a ClassLoader object
+   * yet and has to set cp and id itself
+   */
+  protected ClassLoaderInfo (VM vm){
+    resolvedClasses = new HashMap<String,ClassInfo>();
+    resolvedAnnotations = new HashMap<String,AnnotationInfo>();
+    
+    this.statics = createStatics(vm);
+
+    cp = new ClassPath();
+    
+    // registration has to happen from SystemClassLoaderInfo ctor since we are
+    // only partially initialized at this point
+  }
+  
+  /**
+   * for all other classloaders, which require an already instantiated ClassLoader object 
+   */
+  protected ClassLoaderInfo (VM vm, int objRef, ClassPath cp, ClassLoaderInfo parent) {
+    resolvedClasses = new HashMap<String,ClassInfo>();
+    resolvedAnnotations = new HashMap<String,AnnotationInfo>();
+
+    this.parent = parent;
+    this.objRef = objRef;
+    this.cp = cp;
+    this.statics = createStatics(vm);
+
+    this.id = computeId(objRef);
+    ElementInfo ei = vm.getModifiableElementInfo(objRef);
+
+    ei.setIntField(ID_FIELD, id);
+    if (parent != null) {
+      ei.setReferenceField("parent", parent.objRef);
+    }
+    classInfo = ei.getClassInfo();
+    roundTripRequired = isRoundTripRequired();
+
+    vm.registerClassLoader(this);
+  }
+  
+  @Override
+  public Memento<ClassLoaderInfo> getMemento (MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  public Memento<ClassLoaderInfo> getMemento(){
+    return new ClMemento(this);
+  }
+
+  protected Statics createStatics (VM vm){
+    Class<?>[] argTypes = { Config.class, KernelState.class };
+    Object[] args = { config, vm.getKernelState() };
+    
+    return config.getEssentialInstance("vm.statics.class", Statics.class, argTypes, args);
+  }
+  
+  /**
+   * this is our internal, search global id that is used for the
+   * canonical root set
+   */
+  public int getId() {
+    return id;
+  }
+
+  /**
+   * Returns the type of the corresponding class loader object
+   */
+  public ClassInfo getClassInfo () {
+    return classInfo;
+  }
+
+  /**
+   * Returns the object reference.
+   */
+  public int getClassLoaderObjectRef () {
+    return objRef;
+  }
+
+  protected int computeId (int objRef) {
+    int id = globalCLids.get(objRef);
+    if (id == 0) {
+      id = globalCLids.size() + 1; // the first systemClassLoader is not in globalCLids and always has id '0'
+      globalCLids.set(objRef, id);
+    }
+    return id;
+  }
+
+  /**
+   * For optimizing the class loading mechanism, if the class loader class and the 
+   * classes of the whole parents hierarchy are descendant of URLClassLoader and 
+   * do not override the ClassLoader.loadClass() & URLClassLoader.findClass, resolving 
+   * the class is done natively within JPF
+   */
+  protected boolean isRoundTripRequired() {
+    return (parent!=null? parent.roundTripRequired: true)  || !hasOriginalLoadingImp();
+  }
+
+  private boolean hasOriginalLoadingImp() {
+    String signature = "(Ljava/lang/String;)Ljava/lang/Class;";
+    MethodInfo loadClass = classInfo.getMethod("loadClass" + signature, true);
+    MethodInfo findClass = classInfo.getMethod("findClass" + signature, true);
+  
+    return (loadClass.getClassName().equals("java.lang.ClassLoader") &&
+        findClass.getClassName().equals("java.net.URLClassLoader"));
+  }
+
+  public boolean isSystemClassLoader() {
+    return false;
+  }
+  
+  public static ClassLoaderInfo getCurrentClassLoader() {
+    return getCurrentClassLoader( ThreadInfo.getCurrentThread());
+  }
+
+  public static ClassLoaderInfo getCurrentClassLoader (ThreadInfo ti) {
+    for (StackFrame frame = ti.getTopFrame(); frame != null; frame = frame.getPrevious()){
+      MethodInfo miFrame = frame.getMethodInfo();
+      ClassInfo ciFrame =  miFrame.getClassInfo();
+      if (ciFrame != null){
+        return ciFrame.getClassLoaderInfo();
+      }
+    }
+
+    return ti.getSystemClassLoaderInfo();
+  }
+  
+  public static SystemClassLoaderInfo getCurrentSystemClassLoader() {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    if (ti != null){
+      return ti.getSystemClassLoaderInfo();
+    } else {
+      // this is kind of a hack - we just use the latest SystemClassLoaderInfo instance
+      // this might happen if the SystemClassLoader preloads classes before we have a main thread
+      return SystemClassLoaderInfo.lastInstance;
+    }
+  }
+
+  public SystemClassLoaderInfo getSystemClassLoader() {
+    return getCurrentSystemClassLoader();
+  }
+  
+  protected ClassInfo loadSystemClass (String clsName){
+    return getCurrentSystemClassLoader().loadSystemClass(clsName);
+  }
+
+  protected ClassInfo createClassInfo (String typeName, ClassFileMatch match, ClassLoaderInfo definingLoader) throws ClassParseException {
+    return getCurrentSystemClassLoader().createClassInfo( typeName, match, definingLoader);
+  }
+  
+  protected ClassInfo createClassInfo (String typeName, String url, byte[] data, ClassLoaderInfo definingLoader) throws ClassParseException {
+    return getCurrentSystemClassLoader().createClassInfo( typeName, url, data, definingLoader);
+  }
+
+  protected void setAttributes (ClassInfo ci){
+    getCurrentSystemClassLoader().setAttributes(ci);
+  }
+  
+  
+  /**
+   * obtain ClassInfo object for given class name
+   *
+   * if the requested class or any of its superclasses and interfaces
+   * is not found this method will throw a ClassInfoException. Loading
+   * of respective superclasses and interfaces happens recursively from here.
+   *
+   * Returned ClassInfo objects are not registered yet, i.e. still have to
+   * be added to the ClassLoaderInfo's statics, and don't have associated java.lang.Class
+   * objects until registerClass(ti) is called.
+   *
+   * Before any field or method access, the class also has to be initialized,
+   * which can include overlayed execution of &lt;clinit&gt; declaredMethods, which is done
+   * by calling initializeClass(ti,insn)
+   *
+   * this is for loading classes from the file system 
+   */
+  public ClassInfo getResolvedClassInfo (String className) throws ClassInfoException {
+    String typeName = Types.getClassNameFromTypeName( className);
+    
+    ClassInfo ci = resolvedClasses.get( typeName);
+    if (ci == null) {
+      if (ClassInfo.isBuiltinClass( typeName)){
+        ci = loadSystemClass( typeName);
+
+      } else {
+        ClassFileMatch match = getMatch( typeName);
+        if (match != null){
+          String url = match.getClassURL();
+          ci = loadedClasses.get( url); // have we loaded the class from this source before
+          if (ci != null){
+            if (ci.getClassLoaderInfo() != this){ // might have been loaded by another classloader
+              ci = ci.cloneFor(this);
+            }
+          } else {
+            try {
+              log.info("loading class ", typeName, " from ",  url);
+              ci = match.createClassInfo(this);
+              
+            } catch (ClassParseException cpx){
+              throw new ClassInfoException( "error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
+            }
+            
+            loadedClasses.put( url, ci);
+          }
+          
+        } else { // no match found
+          throw new ClassInfoException("class not found: " + typeName, this, "java.lang.ClassNotFoundException", typeName);
+        }
+      }
+      
+      setAttributes(ci);
+      resolvedClasses.put(typeName, ci);
+    }
+    
+    return ci;
+  }
+  
+  /**
+   * this is for user defined ClassLoaders that explicitly provide the class file data
+   */
+  public ClassInfo getResolvedClassInfo (String className, byte[] data, int offset, int length) throws ClassInfoException {
+    String typeName = Types.getClassNameFromTypeName( className);
+    ClassInfo ci = resolvedClasses.get( typeName);    
+    
+    if (ci == null) {        
+      try {
+        // it can't be a builtin class since we have classfile contents
+        String url = typeName; // three isn't really a URL for it, just choose somehting
+        SystemClassLoaderInfo sysCl = getCurrentSystemClassLoader();
+        ci = sysCl.createClassInfo(typeName, url, data, this);
+
+        // no use to store it in loadedClasses since the data might be dynamically generated
+
+      } catch (ClassParseException cpx) {
+        throw new ClassInfoException("error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
+      }
+
+      setAttributes(ci);
+      resolvedClasses.put( typeName, ci);
+    }
+    
+    return ci;
+  }
+    
+  public AnnotationInfo getResolvedAnnotationInfo (String typeName) throws ClassInfoException {
+    AnnotationInfo ai = resolvedAnnotations.get(typeName);
+    
+    if (ai == null){
+      ClassFileMatch match = getMatch( typeName);
+      if (match != null){
+        String url = match.getClassURL();
+        ai = loadedAnnotations.get(url); // have we loaded the class from this source before
+        if (ai != null) {
+          if (ai.getClassLoaderInfo() != this) { // might have been loaded by another classloader
+            ai = ai.cloneFor(this);
+          }
+          
+        } else {
+          try {
+            ai = match.createAnnotationInfo(this);
+            
+          } catch (ClassParseException cpx) {
+            throw new ClassInfoException("error parsing class", this, "java.lang.NoClassDefFoundError", typeName, cpx);
+          }
+            
+          loadedAnnotations.put( url, ai);
+        } 
+        
+      } else { // no match found
+        throw new ClassInfoException("class not found: " + typeName, this, "java.lang.ClassNotFoundException", typeName);
+      }
+      
+      resolvedAnnotations.put( typeName, ai);
+    }
+    
+    return ai;
+  }
+  
+  public ClassInfo getResolvedAnnotationProxy (ClassInfo ciAnnotation){
+    String typeName = ciAnnotation.getName() + "$Proxy";
+    
+    ClassInfo ci = resolvedClasses.get( typeName);
+    if (ci == null) {
+      ci = ciAnnotation.createAnnotationProxy(typeName);      
+      resolvedClasses.put( typeName, ci);
+    }
+
+    return ci;
+  }
+
+  /**
+   * This method returns a type which implements the given functional interface 
+   * and contains a method that captures the behavior of the lambda expression.
+   */
+  public ClassInfo getResolvedFuncObjType (int bsIdx, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, String[] freeVariableTypeNames) {
+    String typeName = bmi.enclosingClass.getName() + "$$Lambda$" + bsIdx;
+    
+    ClassInfo funcObjType = resolvedClasses.get( typeName);
+    
+    if (funcObjType == null) {
+      funcObjType = fiClassInfo.createFuncObjClassInfo(bmi, typeName, samUniqueName, freeVariableTypeNames);
+      resolvedClasses.put( typeName, funcObjType);
+    }
+    
+    return funcObjType;
+  }
+  
+  protected ClassInfo getAlreadyResolvedClassInfo(String cname) {
+    return resolvedClasses.get(cname);
+  }
+
+  protected void addResolvedClass(ClassInfo ci) {
+    resolvedClasses.put(ci.getName(), ci);
+  }
+
+  protected boolean hasResolved(String cname) {
+    return (resolvedClasses.get(cname)!=null);
+  }
+
+  /**
+   * this one is for clients that need to synchronously get an initialized classinfo.
+   * NOTE: we don't handle clinits here. If there is one, this will throw
+   * an exception. NO STATIC BLOCKS / FIELDS ALLOWED
+   */
+  public ClassInfo getInitializedClassInfo (String clsName, ThreadInfo ti){
+    ClassInfo ci = getResolvedClassInfo(clsName);
+    ci.initializeClassAtomic(ti);
+    return ci;
+  }
+
+  /**
+   * obtain ClassInfo from context that does not care about resolution, i.e.
+   * does not check for NoClassInfoExceptions
+   *
+   * @param className fully qualified classname to get a ClassInfo for
+   * @return null if class was not found
+   */
+  public ClassInfo tryGetResolvedClassInfo (String className){
+    try {
+      return getResolvedClassInfo(className);
+    } catch (ClassInfoException cx){
+      return null;
+    }
+  }
+
+  public ClassInfo getClassInfo (int id) {
+    ElementInfo ei = statics.get(id);
+    if (ei != null) {
+      return ei.getClassInfo();
+    } else {
+      return null;
+    }
+  }
+
+  // it acquires the resolvedClassInfo by executing the class loader loadClass() method
+  public ClassInfo loadClass(String cname) {
+    ClassInfo ci = null;
+    if(roundTripRequired) {
+      // loadClass bytecode needs to be executed by the JPF vm
+      ci = loadClassOnJPF(cname);
+    } else {
+      // This class loader and the whole parent hierarchy use the standard class loading
+      // mechanism, therefore the class is loaded natively
+      ci = loadClassOnJVM(cname);
+    }
+
+    return ci;
+  }
+
+  protected ClassInfo loadClassOnJVM(String cname) {
+    String className = Types.getClassNameFromTypeName(cname);
+    // Check if the given class is already resolved by this loader
+    ClassInfo ci = getAlreadyResolvedClassInfo(className);
+
+    if (ci == null) {
+      try {
+        if(parent != null) {
+          ci = parent.loadClassOnJVM(cname);
+        } else {
+          ClassLoaderInfo systemClassLoader = getCurrentSystemClassLoader();
+          ci = systemClassLoader.getResolvedClassInfo(cname);
+        }
+      } catch(ClassInfoException cie) {
+        if(cie.getExceptionClass().equals("java.lang.ClassNotFoundException")) {
+          ci = getResolvedClassInfo(cname);
+        } else {
+          throw cie;
+        }
+      }
+    }
+
+    return ci;
+  }
+
+  // we need a system attribute to 
+  class LoadClassRequest implements SystemAttribute {
+    String typeName;
+    
+    LoadClassRequest (String typeName){
+      this.typeName = typeName;
+    }
+    
+    boolean isRequestFor( String typeName){
+      return this.typeName.equals( typeName);
+    }
+  }
+  
+  protected ClassInfo loadClassOnJPF (String typeName) {
+    String className = Types.getClassNameFromTypeName(typeName);
+    // Check if the given class is already resolved by this loader
+    ClassInfo ci = getAlreadyResolvedClassInfo(className);
+
+    if(ci != null) { // class already resolved
+      return ci;
+      
+    } else {   // class is not yet resolved, do a roundtrip for the respective loadClass() method
+      ThreadInfo ti = VM.getVM().getCurrentThread();  
+      StackFrame frame = ti.getReturnedDirectCall();
+      
+      if (frame != null){ // there was a roundtrip, but make sure it wasn't a recursive one
+        LoadClassRequest a = frame.getFrameAttr(LoadClassRequest.class);
+        if (a != null && a.isRequestFor(typeName)){ // the roundtrip is completed
+          int clsObjRef = frame.pop();
+
+          if (clsObjRef == MJIEnv.NULL) {
+            throw new ClassInfoException("class not found: " + typeName, this, "java.lang.NoClassDefFoundError", typeName);
+          } else {
+            return ti.getEnv().getReferredClassInfo(clsObjRef);
+          }          
+        }
+      }
+      
+      // initiate the roundtrip & bail out
+      pushloadClassFrame(typeName);
+      throw new LoadOnJPFRequired(typeName);
+    }
+  }
+
+  protected void pushloadClassFrame (String typeName) {
+    ThreadInfo ti = VM.getVM().getCurrentThread();
+
+    // obtain the class of this ClassLoader
+    ClassInfo clClass = VM.getVM().getClassInfo(objRef);
+
+    // retrieve the loadClass() method of this ClassLoader class
+    MethodInfo miLoadClass = clClass.getMethod("loadClass(Ljava/lang/String;)Ljava/lang/Class;", true);
+
+    // create a frame representing loadClass() & push it to the stack of the  current thread 
+    DirectCallStackFrame frame = miLoadClass.createDirectCallStackFrame( ti, 0);
+
+    String clsName = typeName.replace('/', '.');
+    int sRef = ti.getEnv().newString( clsName);
+    int argOffset = frame.setReferenceArgument( 0, objRef, null);
+    frame.setReferenceArgument( argOffset, sRef, null);
+
+    frame.setFrameAttr( new LoadClassRequest(typeName));
+    
+    ti.pushFrame(frame);
+  }
+
+  protected ClassInfo getDefinedClassInfo(String typeName){
+    ClassInfo ci = resolvedClasses.get(typeName);
+    if(ci != null && ci.classLoader == this) {
+      return ci;
+    } else {
+      return null;
+    }
+  }
+  
+  public ElementInfo getElementInfo (String typeName) {
+    ClassInfo ci = resolvedClasses.get(typeName);
+    if (ci != null) {
+      ClassLoaderInfo cli = ci.classLoader;
+      Statics st = cli.statics;
+      return st.get(ci.getId());
+      
+    } else {
+      return null; // not resolved
+    }
+  }
+
+  public ElementInfo getModifiableElementInfo (String typeName) {
+    ClassInfo ci = resolvedClasses.get(typeName);
+    if (ci != null) {
+      ClassLoaderInfo cli = ci.classLoader;
+      Statics st = cli.statics;
+      return st.getModifiable(ci.getId());
+      
+    } else {
+      return null; // not resolved
+    }
+  }
+
+  protected ClassFileMatch getMatch(String typeName) {
+    if(ClassInfo.isBuiltinClass(typeName)) {
+      return null;
+    }
+
+    ClassFileMatch match;
+    try {
+      match = cp.findMatch(typeName); 
+    } catch (ClassParseException cfx){
+      throw new JPFException("error reading class " + typeName, cfx);
+    }
+
+    return match;
+  }
+
+  /**
+   * Finds the first Resource in the classpath which has the specified name. 
+   * Returns null if no Resource is found.
+   */
+  public String findResource (String resourceName){
+    for (String cpe : getClassPathElements()) {
+      String URL = getResourceURL(cpe, resourceName);
+      if(URL != null) {
+        return URL;
+      }
+    }
+    return null;
+  }
+
+  /**
+   * Finds all resources in the classpath with the given name. Returns an 
+   * enumeration of the URL objects.
+   */
+  public String[] findResources (String resourceName){
+    ArrayList<String> resources = new ArrayList(0);
+    for (String cpe : getClassPathElements()) {
+      String URL = getResourceURL(cpe, resourceName);
+      if(URL != null) {
+        if(!resources.contains(URL)) {
+          resources.add(URL);
+        }
+      }
+    }
+    return resources.toArray(new String[resources.size()]);
+  }
+  
+  protected String getResourceURL(String path, String resource) {
+    if(resource != null) {
+      try {
+        if (path.endsWith(".jar")){
+          JarFile jar = new JarFile(path);
+          JarEntry e = jar.getJarEntry(resource);
+          if (e != null){
+            File f = new File(path);
+            return "jar:" + f.toURI().toURL().toString() + "!/" + resource;
+          }
+        } else {
+          File f = new File(path, resource);
+          if (f.exists()){
+            return f.toURI().toURL().toString();
+          }
+        }
+      } catch (MalformedURLException mfx){
+        return null;
+      } catch (IOException iox){
+        return null;
+      }
+    }
+
+    return null;
+  }
+
+  public Statics getStatics() {
+    return statics;
+  }
+
+  public ClassPath getClassPath() {
+    return cp;
+  }
+
+  public String[] getClassPathElements() {
+    return cp.getPathNames();
+  }
+
+  protected ClassFileContainer createClassFileContainer (String path){
+    return getCurrentSystemClassLoader().createClassFileContainer(path);
+  }
+  
+  public void addClassPathElement (String path){
+    ClassFileContainer cfc = createClassFileContainer(path);
+    
+    if (cfc != null){
+      cp.addClassFileContainer(cfc);
+    } else {
+      log.warning("unknown classpath element: ", path);
+    }
+  }
+  
+  /**
+   * Comparison for sorting based on index.
+   */
+  @Override
+  public int compareTo (ClassLoaderInfo that) {
+    return this.id - that.id;
+  }
+
+  /**
+   * Returns an iterator over the classes that are defined (directly loaded) by this classloader. 
+   */
+  @Override
+  public Iterator<ClassInfo> iterator () {
+    return resolvedClasses.values().iterator();
+  }
+
+  /**
+   * For now, this always returns true, and it used while the classloader is being
+   * serialized. That is going to be changed if we ever consider unloading the
+   * classes. For now, it is just added in analogy to ThreadInfo
+   */
+  public boolean isAlive () {
+    return true;
+  }
+
+  public Map<String, ClassLoaderInfo> getPackages() {
+    Map<String, ClassLoaderInfo> pkgs = new HashMap<String, ClassLoaderInfo>();
+    for(String cname: resolvedClasses.keySet()) {
+      if(!ClassInfo.isBuiltinClass(cname) && cname.indexOf('.')!=-1) {
+        pkgs.put(cname.substring(0, cname.lastIndexOf('.')), this);
+      }
+    }
+
+    Map<String, ClassLoaderInfo> parentPkgs = null;
+    if(parent!=null) {
+      parentPkgs = parent.getPackages();
+    }
+
+    if (parentPkgs != null) {
+      for (String pName: parentPkgs.keySet()) {
+        if (pkgs.get(pName) == null) {
+          pkgs.put(pName, parentPkgs.get(pName));
+        }
+      }
+    }
+    return pkgs;
+  }
+
+  //-------- assertion management --------
+  
+  // set in the jpf.properties file
+  static StringSetMatcher enabledAssertionPatterns;
+  static StringSetMatcher disabledAssertionPatterns;
+
+  protected Map<String, Boolean> classAssertionStatus = new HashMap<String, Boolean>();
+  protected Map<String, Boolean> packageAssertionStatus = new HashMap<String, Boolean>();
+  protected boolean defaultAssertionStatus = false;
+  protected boolean isDefaultSet = false;
+
+  protected boolean desiredAssertionStatus(String cname) {
+    // class level assertion can override all their assertion settings
+    Boolean result = classAssertionStatus.get(cname);
+    if (result != null) {
+      return result.booleanValue();
+    }
+
+    // package level assertion can override the default assertion settings
+    int dotIndex = cname.lastIndexOf(".");
+    if (dotIndex < 0) { // check for default package
+      result = packageAssertionStatus.get(null);
+      if (result != null) {
+        return result.booleanValue();
+      }
+    }
+
+    if(dotIndex > 0) {
+      String pkgName = cname;
+      while(dotIndex > 0) { // check for the class package and its upper level packages 
+        pkgName = pkgName.substring(0, dotIndex);
+        result = packageAssertionStatus.get(pkgName);
+        if (result != null) {
+          return result.booleanValue();
+        }
+        dotIndex = pkgName.lastIndexOf(".", dotIndex-1);
+      }
+    }
+
+    // class loader default, if it has been set, can override the settings
+    // specified by VM arguments
+    if(isDefaultSet) {
+      return defaultAssertionStatus;
+    } else {
+      return StringSetMatcher.isMatch(cname, enabledAssertionPatterns, disabledAssertionPatterns);
+    }
+  }
+
+  public void setDefaultAssertionStatus(boolean enabled) {
+    isDefaultSet = true;
+    defaultAssertionStatus = enabled;
+  }
+
+  public void setClassAssertionStatus(String cname, boolean enabled) {
+    classAssertionStatus.put(cname, enabled);
+  }
+
+  public void setPackageAssertionStatus(String pname, boolean enabled) {
+    packageAssertionStatus.put(pname, enabled);
+  }
+
+  public void clearAssertionStatus() {
+    classAssertionStatus = new HashMap<String, Boolean>();
+    packageAssertionStatus = new HashMap<String, Boolean>();
+    defaultAssertionStatus = false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassLoaderList.java b/src/main/gov/nasa/jpf/vm/ClassLoaderList.java
new file mode 100644 (file)
index 0000000..b99e5fb
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * container for all ClassLoaderInfos that are in the current state.
+ * It is important to keep this as canonically (search globally) sorted list so that
+ * we can use the iterator for state matching
+ */
+public class ClassLoaderList implements Cloneable, Iterable<ClassLoaderInfo>, Restorable<ClassLoaderList> {
+
+  /** the ordered list of the class loaders */
+  ClassLoaderInfo[] classLoaders;
+
+  static class CllMemento implements Memento<ClassLoaderList> {
+    Memento<ClassLoaderInfo>[] clMementos;
+
+    CllMemento (ClassLoaderList cll) {
+      ClassLoaderInfo[] classLoaders = cll.classLoaders;
+      
+      int len = classLoaders.length;
+      clMementos =  new Memento[len];
+    
+      for (int i=0; i<len; i++){
+        ClassLoaderInfo cl = classLoaders[i];
+        Memento<ClassLoaderInfo> m = cl.getMemento();
+        clMementos[i] = m;
+      }
+    }
+
+    @Override
+       public ClassLoaderList restore (ClassLoaderList cll){
+      int len = clMementos.length;
+      ClassLoaderInfo[] classLoaders = new ClassLoaderInfo[len];
+      for (int i=0; i<len; i++){
+        Memento<ClassLoaderInfo> m = clMementos[i];
+        ClassLoaderInfo cl = m.restore(null);
+        classLoaders[i] = cl;
+      }
+      cll.classLoaders = classLoaders;
+
+      return cll;
+    }
+  }
+  
+  class CllIterator implements Iterator<ClassLoaderInfo>{
+    int next = 0;
+    
+    @Override
+    public boolean hasNext() {
+      if (classLoaders != null) {
+        return next < classLoaders.length;
+      } else {
+        return false;
+      }
+    }
+
+    @Override
+    public ClassLoaderInfo next() {
+      if (classLoaders != null) {
+        if (next < classLoaders.length) {
+          return classLoaders[next++];
+        }
+      }
+      
+      throw new NoSuchElementException();
+    }
+
+    @Override
+    public void remove() {
+      throw new UnsupportedOperationException();
+    }
+  }
+
+  public ClassLoaderList() {
+  }
+
+  @Override
+  public Memento<ClassLoaderList> getMemento (MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  public Memento<ClassLoaderList> getMemento(){
+    return new CllMemento(this);
+  }
+
+  @Override
+  public Iterator<ClassLoaderInfo> iterator () {
+    return new CllIterator();
+  }
+
+  public void add (ClassLoaderInfo cli) {
+    int id = cli.getId();
+    
+    if (classLoaders == null) {
+      classLoaders = new ClassLoaderInfo[1];
+      classLoaders[0] = cli;
+      
+    } else { // sort it in
+      int len = classLoaders.length;
+      ClassLoaderInfo[] a = new ClassLoaderInfo[len+1];
+      
+      for (int i=0; i<len; i++) {
+        ClassLoaderInfo c = classLoaders[i];
+        if (c.getId() > id) {
+          System.arraycopy(classLoaders, i, a, i+1, (len-i));
+          a[i] = cli;
+          classLoaders = a;
+          return;
+        } else {
+          a[i] = c;
+        }
+      }
+      
+      a[len] = cli;
+      classLoaders = a;
+    }
+  }
+
+  public ClassLoaderInfo get(int i) {
+    return classLoaders[i];
+  }
+  
+  public ClassLoaderInfo getClassLoaderInfoWithId (int id) {
+    int len = classLoaders.length;
+    for (int i=0; i<len; i++) {
+      ClassLoaderInfo c = classLoaders[i];
+      if (c.getId() == id) {
+        return c;
+      }
+    }
+    
+    return null;
+  }
+
+  public int size() {
+    return classLoaders.length;
+  }
+  
+  public void markRoots (Heap heap) {
+    int len = classLoaders.length;
+    for (int i=0; i<len; i++) {
+      ClassLoaderInfo cli = classLoaders[i];
+      cli.getStatics().markRoots(heap);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassParseException.java b/src/main/gov/nasa/jpf/vm/ClassParseException.java
new file mode 100644 (file)
index 0000000..7d4289a
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * an exception while parsing a ClassFile
+ */
+public class ClassParseException extends Exception {
+
+  public ClassParseException (String details){
+    super(details);
+  }
+  
+  public ClassParseException (String details, Throwable cause){
+    super(details, cause);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClassPath.java b/src/main/gov/nasa/jpf/vm/ClassPath.java
new file mode 100644 (file)
index 0000000..7b79db3
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.JPFLogger;
+
+import java.io.File;
+import java.util.ArrayList;
+
+/**
+ * this is a lookup mechanism for class files that is based on an ordered
+ * list of directory or jar entries
+ */
+public class ClassPath implements Restorable<ClassPath>{
+
+  static class CPMemento implements Memento<ClassPath> {
+    ClassPath cp;
+    ArrayList<ClassFileContainer> pathElements;
+
+    CPMemento (ClassPath cp){
+      this.cp = cp;
+      this.pathElements = new ArrayList<ClassFileContainer>(cp.pathElements);
+    }
+
+    @Override
+    public ClassPath restore (ClassPath ignored) {
+      cp.pathElements = this.pathElements;
+      return cp;
+    }
+  }
+
+  
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.jvm.classfile");
+  
+  protected ArrayList<ClassFileContainer> pathElements;
+
+
+  public ClassPath(){
+    pathElements = new ArrayList<ClassFileContainer>();
+  }
+  
+  @Override
+  public Memento<ClassPath> getMemento (MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  public Memento<ClassPath> getMemento(){
+    return new CPMemento(this);
+  }
+
+  public void addClassFileContainer (ClassFileContainer pathElement){
+    assert pathElement != null;
+    pathElements.add(pathElement);
+  }
+
+
+  public String[] getPathNames(){
+    String[] pn = new String[pathElements.size()];
+
+    for (int i=0; i<pn.length; i++){
+      pn[i] = pathElements.get(i).getName();
+    }
+
+    return pn;
+  }
+
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+    int len = pathElements.size();
+    int i=0;
+
+    for (ClassFileContainer e : pathElements){
+      sb.append(e.getName());
+      if (++i < len){
+        sb.append(File.pathSeparator);
+      }
+    }
+    return sb.toString();
+  }
+
+  protected static void error(String msg) throws ClassParseException {
+    throw new ClassParseException(msg);
+  }
+
+  public ClassFileMatch findMatch (String clsName) throws ClassParseException {
+    for (ClassFileContainer container : pathElements){
+      ClassFileMatch match = container.getMatch(clsName);
+      if (match != null){
+        logger.fine("found ", clsName, " in ", container.getName());
+        return match;
+      }
+    }
+
+    return null;    
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/ClinitRequired.java b/src/main/gov/nasa/jpf/vm/ClinitRequired.java
new file mode 100644 (file)
index 0000000..e350ff9
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * this one is kind of a hack for situations where we detect deep from
+ * the stack that we need a clinit to be executed, but we can't flag this
+ * to the currently executed insn via a return value. referencing annotation
+ * elements that are enums is a good (bad) example
+ */
+public class ClinitRequired extends RuntimeException {
+  ClassInfo ci;
+  
+  public ClinitRequired (ClassInfo ci){
+    this.ci = ci;
+  }
+  
+  ClassInfo getRequiredClassInfo() {
+    return ci;
+  }
+  
+  @Override
+  public String getMessage(){
+    return ci.getName();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ClosedMemento.java b/src/main/gov/nasa/jpf/vm/ClosedMemento.java
new file mode 100644 (file)
index 0000000..59e0b02
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * this is a self-contained memento that remembers which object to restore,
+ * and where to restore it into (e.g. a container).
+ * 
+ * This differs from a normal Memento in that it requires nothing from the
+ * caller except of being called at the right times.
+ * 
+ * Normally, implementors keep a reference to the original object, and copy
+ * the field values which have to be restored.
+ */
+public interface ClosedMemento {
+
+  void restore();
+}
diff --git a/src/main/gov/nasa/jpf/vm/CollapsePools.java b/src/main/gov/nasa/jpf/vm/CollapsePools.java
new file mode 100644 (file)
index 0000000..8aa263b
--- /dev/null
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashPool;
+import gov.nasa.jpf.util.IntTable.Entry;
+import gov.nasa.jpf.util.WeakPool;
+
+abstract class CollapsePools {
+  static class AllWeak {
+    /** Pool used to store the stack frames.*/
+    private WeakPool<StackFrame> stackFramePool = new WeakPool<StackFrame>(11);
+
+    /** Pool used to store collections of field values.*/
+    private WeakPool<Fields>     fieldsPool     = new WeakPool<Fields>    (11);
+
+    /** Pool used to store the thread data.*/
+    private WeakPool<ThreadData> threadDataPool = new WeakPool<ThreadData>(8);
+    
+    /** Pool used to store monitor states.*/
+    private WeakPool<Monitor>    monitorPool    = new WeakPool<Monitor>   (8);
+
+    public StackFrame poolStackFrame(StackFrame o) {
+      StackFrame p = stackFramePool.pool(o);
+      if (VM.CHECK_CONSISTENCY) assert p.equals(o);
+      return p;
+    }
+
+    public Fields poolFields(Fields o) {
+      Fields p = fieldsPool.pool(o);
+      if (VM.CHECK_CONSISTENCY) assert p.equals(o);
+      return p;
+    }
+
+    public ThreadData poolThreadData(ThreadData o) {
+      ThreadData p = threadDataPool.pool(o);
+      if (VM.CHECK_CONSISTENCY) assert p.equals(o);
+      return p;
+    }
+
+    public Monitor poolMonitor(Monitor o) {
+      Monitor p = monitorPool.pool(o);
+      if (VM.CHECK_CONSISTENCY) assert p.equals(o);
+      return p;
+    }
+  }
+  
+  static class AllIndexed {
+    /** Pool used to store the thread data.*/
+    private HashPool<ThreadData> threadDataPool = new HashPool<ThreadData>(8).addNull();
+
+    /** Pool used to store monitor states.*/
+    private HashPool<Monitor>    monitorPool    = new HashPool<Monitor>   (8).addNull();
+
+    /** Pool used to store the stack frames.*/
+    private HashPool<StackFrame> stackFramePool = new HashPool<StackFrame>(11).addNull();
+
+    /** Pool used to store collections of field values.*/
+    private HashPool<Fields>     fieldsPool     = new HashPool<Fields>    (11).addNull();
+    
+    public StackFrame poolStackFrame(StackFrame o) {
+      StackFrame p = stackFramePool.get(o);
+      if (VM.CHECK_CONSISTENCY) assert p.equals(o);
+      return p;
+    }
+
+    public Fields poolFields(Fields o) {
+      return fieldsPool.get(o);
+    }
+
+    public ThreadData poolThreadData(ThreadData o) {
+      return threadDataPool.get(o);
+    }
+
+    public Monitor poolMonitor(Monitor o) {
+      return monitorPool.get(o);
+    }
+
+    public int getFieldsIndex(ElementInfo ei) {
+      Entry<Fields> entry = fieldsPool.getEntry(ei.getFields());
+      ei.restoreFields(entry.key);
+      return entry.val;
+    }
+
+    public int getStackFrameIndex(StackFrame sf) {
+      return stackFramePool.getIndex(sf);
+    }
+
+    public int getThreadDataIndex(ThreadInfo ti) {
+      Entry<ThreadData> e = threadDataPool.getEntry(ti.threadData);
+      ti.threadData = e.key;
+      return e.val;
+    }
+    
+    public int getMonitorIndex(ElementInfo ei) {
+      Entry<Monitor> entry = monitorPool.getEntry(ei.getMonitor());
+      ei.restoreMonitor(entry.key);
+      return entry.val;
+    }
+    
+    public Fields getFieldsAt(int idx) {
+      return fieldsPool.getObject(idx);
+    }
+
+    public StackFrame getStackFrameAt(int idx) {
+      return stackFramePool.getObject(idx);
+    }
+
+    public ThreadData getThreadDataAt(int idx) {
+      return threadDataPool.getObject(idx);
+    }
+
+    public Monitor getMonitorAt(int idx) {
+      return monitorPool.getObject(idx);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ConstInsnPathTime.java b/src/main/gov/nasa/jpf/vm/ConstInsnPathTime.java
new file mode 100644 (file)
index 0000000..f80b264
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+
+/**
+ * time model that uses instruction count along current path to deduce
+ * the execution time.
+ * 
+ * NOTE: the requirement that time has to be strictly increasing along a path
+ * means we cannot skip the initialization
+ */
+public class ConstInsnPathTime extends ListenerAdapter implements TimeModel {
+
+  // note - this class is not public since we want to make sure this listener
+  // is the only one using this type
+  static class TimeVal {
+    final long time;
+    TimeVal (long t){
+      time = t;
+    }
+  }
+  
+  VM vm;
+  int perInsnTime; // simple constant time assumed for each instruction
+  
+  long transitionStartTime;
+  
+  public ConstInsnPathTime (VM vm, Config conf){
+    perInsnTime = conf.getInt("vm.time.per_insn", 1);
+    
+    vm.addListener(this);
+    this.vm = vm;
+  }
+
+  protected long computeTime (){
+    ThreadInfo tiCurrent = vm.getCurrentThread();
+    long t = tiCurrent.getExecutedInstructions() * perInsnTime;
+    
+    return transitionStartTime + t;
+  }
+  
+  //-- the listener interface
+  @Override
+  public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo ti, Instruction executedInsn){
+    ChoiceGenerator<?> cgPrev = nextCG.getPreviousChoiceGenerator();
+    ThreadInfo tiCurrent = vm.getCurrentThread();
+    long t = tiCurrent.getExecutedInstructions() * perInsnTime;
+    
+    TimeVal tv = null;
+    
+    if (nextCG.isCascaded()){
+      // there's got to be a previous one, and its associated with the same insn
+      tv = cgPrev.getAttr(TimeVal.class);
+      
+    } else {
+      if (cgPrev != null){
+        TimeVal tvPrev = cgPrev.getAttr(TimeVal.class); // there has to be one
+        tv = new TimeVal(tvPrev.time + t);
+             
+      } else {
+        tv = new TimeVal( t);
+      }
+    }
+    
+    nextCG.addAttr( tv);
+  }
+  
+  @Override
+  public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG){
+    TimeVal tv = currentCG.getAttr(TimeVal.class);
+    if (tv != null){
+      transitionStartTime = tv.time;
+    } else {
+      transitionStartTime = 0;
+    }
+  }
+  
+  //--- the TimeModel interface
+  @Override
+  public long currentTimeMillis() {
+    return computeTime();
+  }
+
+  @Override
+  public long nanoTime() {
+    return computeTime();
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/DebugJenkinsStateSet.java b/src/main/gov/nasa/jpf/vm/DebugJenkinsStateSet.java
new file mode 100644 (file)
index 0000000..0b75ad8
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.JPFException;
+
+/**
+ * a JenkinsStateSet that stores program state information in a readable
+ * and diffable format.
+ * 
+ * Storing states as readable text is enabled by setting vm.storage.class to this class
+ * 
+ * Note: this automatically sets/overrides the serializer to Debug<serializer-class>
+ */
+public class DebugJenkinsStateSet extends JenkinsStateSet {
+
+  static final String LOGFILE = "state";
+  
+  File outputDir;
+  File outputFile;
+  
+  public DebugJenkinsStateSet (Config conf){
+    String serializerCls = conf.getString("vm.serializer.class");
+    if (serializerCls != null){
+      int idx = serializerCls.lastIndexOf('.') + 1;
+      String cname = serializerCls.substring(idx);
+      
+      if (!cname.startsWith("Debug")){
+        if (idx > 0){
+          serializerCls = serializerCls.substring(0,idx) + "Debug" + cname;
+        } else {
+          serializerCls = "Debug" + cname;
+        }
+      }
+      
+      serializer = conf.getInstance(null, serializerCls, DebugStateSerializer.class);
+      if (serializer == null){
+        throw new JPFConfigException("Debug StateSet cannot instantiate serializer: " + serializerCls);
+      }
+    }
+    
+    String path = conf.getString("vm.serializer.output", "tmp");
+    outputDir = new File(path);
+    if (!outputDir.isDirectory()){
+      if (!outputDir.mkdirs()){
+        throw new JPFConfigException("Debug StateSet cannot create output dir: " + outputDir.getAbsolutePath());        
+      }
+    }
+    
+    outputFile = new File( outputDir, LOGFILE);
+  }
+  
+  @Override
+  public void attach(VM vm){
+    // we use our own serializer
+    vm.setSerializer( serializer);
+    
+    // <2do> this is a bit hack'ish - why does the VM keep the serializer anyways,
+    // if it is only used here
+    super.attach(vm);
+  }
+  
+  @Override
+  public int addCurrent () {
+    int maxId = lastStateId;
+    FileOutputStream fos = null;
+    
+    try {
+      fos = new FileOutputStream( outputFile);
+    } catch (FileNotFoundException fnfx){
+      throw new JPFException("cannot create Debug state set output file: " + outputFile.getAbsolutePath());
+    }
+    
+    ((DebugStateSerializer)serializer).setOutputStream(fos);
+    
+    int stateId = super.addCurrent();
+    
+    try {
+      fos.flush();
+      fos.close();
+    } catch (IOException iox){
+      throw new JPFException("cannot write Debug state set output file: " + outputFile.getAbsolutePath());      
+    }
+    
+    // if this is a new state, store it under its id, otherwise throw it away
+    if (stateId > maxId){
+      String fname = "state." + stateId;
+      outputFile.renameTo( new File(outputDir, fname));
+    } else {
+      if (outputFile.isFile()){
+        outputFile.delete();
+      }
+    }
+    
+    return stateId;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DebugStateSerializer.java b/src/main/gov/nasa/jpf/vm/DebugStateSerializer.java
new file mode 100644 (file)
index 0000000..894c4fd
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.OutputStream;
+
+/**
+ * debug extensions of StateSerializer interface
+ */
+public interface DebugStateSerializer extends StateSerializer {
+
+  void setOutputStream (OutputStream os);
+}
diff --git a/src/main/gov/nasa/jpf/vm/DefaultBacktracker.java b/src/main/gov/nasa/jpf/vm/DefaultBacktracker.java
new file mode 100644 (file)
index 0000000..d06b54d
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.ImmutableList;
+
+public class DefaultBacktracker<KState> implements Backtracker {
+  /** where we keep the saved KernelState head */ 
+  protected ImmutableList<KState> kstack;
+   
+  /** and that adds the SystemState specifics */
+  protected ImmutableList<Object> sstack;
+  
+  protected SystemState ss;
+  protected StateRestorer<KState> restorer;
+  
+  @Override
+  public void attach(VM vm) {
+    ss = vm.getSystemState();
+    restorer = vm.getRestorer();
+  }
+
+  //--- the backtrack support (depth first only)
+  
+  protected void backtrackKernelState() {
+    KState data = kstack.head;
+    kstack = kstack.tail;
+    
+    restorer.restore(data);
+  }
+
+  protected void backtrackSystemState() {
+    Object o = sstack.head;
+    sstack = sstack.tail;
+    ss.backtrackTo(o);
+  }
+
+  
+  /**
+   * Moves one step backward. This method and forward() are the main methods
+   * used by the search object.
+   * Note this is called with the state that caused the backtrack still being on
+   * the stack, so we have to remove that one first (i.e. popping two states
+   * and restoring the second one)
+   */
+  @Override
+  public boolean backtrack () {
+    if (sstack != null) {
+  
+      backtrackKernelState();
+      backtrackSystemState();
+
+      return true;
+    } else {
+      // we are back to the top of where we can backtrack to
+      return false;
+    }
+  }
+  
+  /**
+   * Saves the state of the system.
+   */
+  @Override
+  public void pushKernelState () {
+    kstack = new ImmutableList<KState>(restorer.getRestorableData(),kstack);
+  }
+  
+  /**
+   * Saves the backtracking information.
+   */
+  @Override
+  public void pushSystemState () {
+    sstack = new ImmutableList<Object>(ss.getBacktrackData(),sstack);
+  }
+
+  
+  //--- the restore support
+  
+  // <2do> this saves both the backtrack and the restore data - too redundant
+  class RestorableStateImpl implements RestorableState {
+    final ImmutableList<KState> savedKstack;
+    final ImmutableList<Object> savedSstack;
+    
+    final KState kcur;
+    final Object scur;
+    
+    RestorableStateImpl() {
+      savedKstack = kstack;
+      savedSstack = sstack;
+      kcur = restorer.getRestorableData();
+      scur = ss.getRestoreData();
+    }
+    
+    void restore() {
+      kstack = savedKstack;
+      sstack = savedSstack;
+      restorer.restore(kcur);
+      ss.restoreTo(scur);
+    }
+  }
+  
+  @Override
+  public void restoreState (RestorableState state) {
+    ((RestorableStateImpl) state).restore();
+  }
+  
+  @Override
+  public RestorableState getRestorableState() {
+    return new RestorableStateImpl();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DefaultFieldsFactory.java b/src/main/gov/nasa/jpf/vm/DefaultFieldsFactory.java
new file mode 100644 (file)
index 0000000..9ca1927
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * our concrete Fields factory (representing the default JPF object model)
+ */
+public class DefaultFieldsFactory implements FieldsFactory {
+
+  @Override
+  public Fields createInstanceFields (ClassInfo ci) {
+    return new NamedFields(ci.getInstanceDataSize());
+  }
+
+  @Override
+  public Fields createStaticFields (ClassInfo ci) {
+    return new NamedFields(ci.getStaticDataSize());
+  }
+
+  @Override
+  public Fields createArrayFields (String type, ClassInfo ci, int nElements, int typeSize, boolean isReferenceArray) {
+    char t = type.charAt(1);
+    switch (t){
+      case 'Z': return new BooleanArrayFields(nElements);
+      case 'B': return new ByteArrayFields(nElements);
+      case 'C': return new CharArrayFields(nElements);
+      case 'S': return new ShortArrayFields(nElements);
+      case 'I': return new IntArrayFields(nElements);
+      case 'J': return new LongArrayFields(nElements);
+      case 'F': return new FloatArrayFields(nElements);
+      case 'D': return new DoubleArrayFields(nElements);
+      case 'L':
+      case '[':
+        return new ReferenceArrayFields(nElements);
+      default:
+        throw new JPFException("unknown array type: " + type);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DefaultMementoRestorer.java b/src/main/gov/nasa/jpf/vm/DefaultMementoRestorer.java
new file mode 100644 (file)
index 0000000..b277559
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a MementoRestorer that uses the default mementos
+ */
+public class DefaultMementoRestorer extends MementoRestorer {
+
+  @Override
+  public Memento<KernelState> getMemento(KernelState ks) {
+    return ks.getMemento();
+  }
+
+  @Override
+  public Memento<ThreadList> getMemento(ThreadList tlist) {
+    return tlist.getMemento();
+  }
+
+  @Override
+  public Memento<ThreadInfo> getMemento(ThreadInfo ti) {
+    return ti.getMemento();
+  }
+  
+  @Override
+  public Memento<Heap> getMemento(Heap heap){
+    return heap.getMemento();
+  }
+
+  @Override
+  public Memento<Statics> getMemento(Statics statics){
+    return statics.getMemento();
+  }
+  
+  @Override
+  public Memento<ClassLoaderList> getMemento (ClassLoaderList cllist) {
+    return cllist.getMemento();
+  }
+
+  @Override
+  public Memento<ClassLoaderInfo> getMemento (ClassLoaderInfo cl) {
+    return cl.getMemento();
+  }
+
+  @Override
+  public Memento<ClassPath> getMemento (ClassPath cp) {
+    return cp.getMemento();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DelegatingScheduler.java b/src/main/gov/nasa/jpf/vm/DelegatingScheduler.java
new file mode 100644 (file)
index 0000000..860b7c0
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * a generic scheduler with configured SyncPolicy and SharednesPolicy objects it delegates to
+ */
+public class DelegatingScheduler implements Scheduler {
+  
+  protected SyncPolicy syncPolicy;
+  protected SharednessPolicy sharednessPolicy;
+  
+  
+  public DelegatingScheduler (Config config){
+    syncPolicy = config.getEssentialInstance("vm.scheduler.sync.class", SyncPolicy.class);
+    sharednessPolicy = config.getEssentialInstance("vm.scheduler.sharedness.class", SharednessPolicy.class);
+  }
+  
+  //--- Scheduler interface
+
+  @Override
+  public void initialize (VM vm, ApplicationContext appCtx) {
+    syncPolicy.initializeSyncPolicy(vm, appCtx);
+    sharednessPolicy.initializeSharednessPolicy(vm, appCtx);
+  }
+
+  
+  //--- SyncPolicy interface
+  
+  @Override
+  public void initializeSyncPolicy (VM vm, ApplicationContext appCtx) {
+    syncPolicy.initializeSyncPolicy(vm, appCtx);
+  }
+
+  @Override
+  public void initializeThreadSync (ThreadInfo tiCurrent, ThreadInfo tiNew) {
+    syncPolicy.initializeThreadSync(tiCurrent, tiNew);
+  }
+
+  @Override
+  public void setRootCG (){
+    syncPolicy.setRootCG();
+  }
+  
+  @Override
+  public boolean setsBlockedThreadCG (ThreadInfo ti, ElementInfo ei) {
+    return syncPolicy.setsBlockedThreadCG(ti, ei);
+  }
+
+  @Override
+  public boolean setsLockAcquisitionCG (ThreadInfo ti, ElementInfo ei) {
+    return syncPolicy.setsLockAcquisitionCG(ti, ei);
+  }
+
+  @Override
+  public boolean setsLockReleaseCG (ThreadInfo ti, ElementInfo ei, boolean didUnblock) {
+    return syncPolicy.setsLockReleaseCG(ti, ei, didUnblock);
+  }
+
+  @Override
+  public boolean setsTerminationCG (ThreadInfo ti) {
+    return syncPolicy.setsTerminationCG(ti);
+  }
+
+  @Override
+  public boolean setsWaitCG (ThreadInfo ti, long timeout) {
+    return syncPolicy.setsWaitCG(ti, timeout);
+  }
+
+  @Override
+  public boolean setsNotifyCG (ThreadInfo ti, boolean didNotify) {
+    return syncPolicy.setsNotifyCG(ti, didNotify);
+  }
+
+  @Override
+  public boolean setsNotifyAllCG (ThreadInfo ti, boolean didNotify) {
+    return syncPolicy.setsNotifyAllCG(ti, didNotify);
+  }
+
+  @Override
+  public boolean setsStartCG (ThreadInfo tiCurrent, ThreadInfo tiStarted) {
+    return syncPolicy.setsStartCG(tiCurrent, tiStarted);
+  }
+
+  @Override
+  public boolean setsYieldCG (ThreadInfo ti) {
+    return syncPolicy.setsYieldCG(ti);
+  }
+
+  @Override
+  public boolean setsPriorityCG (ThreadInfo ti) {
+    return syncPolicy.setsPriorityCG(ti);
+  }
+
+  
+  @Override
+  public boolean setsSleepCG (ThreadInfo ti, long millis, int nanos) {
+    return syncPolicy.setsSleepCG(ti, millis, nanos);
+  }
+
+  @Override
+  public boolean setsSuspendCG (ThreadInfo tiCurrent, ThreadInfo tiSuspended) {
+    return syncPolicy.setsSuspendCG(tiCurrent, tiSuspended);
+  }
+
+  @Override
+  public boolean setsResumeCG (ThreadInfo tiCurrent, ThreadInfo tiResumed) {
+    return syncPolicy.setsResumeCG(tiCurrent, tiResumed);
+  }
+
+  @Override
+  public boolean setsJoinCG (ThreadInfo tiCurrent, ThreadInfo tiJoin, long timeout) {
+    return syncPolicy.setsJoinCG(tiCurrent, tiJoin, timeout);
+  }
+
+  @Override
+  public boolean setsStopCG (ThreadInfo tiCurrent, ThreadInfo tiStopped) {
+    return syncPolicy.setsStopCG(tiCurrent, tiStopped);
+  }
+
+  @Override
+  public boolean setsInterruptCG (ThreadInfo tiCurrent, ThreadInfo tiInterrupted) {
+    return syncPolicy.setsInterruptCG(tiCurrent, tiInterrupted);
+  }
+
+  @Override
+  public boolean setsParkCG (ThreadInfo ti, boolean isAbsTime, long timeout) {
+    return syncPolicy.setsParkCG(ti, isAbsTime, timeout);
+  }
+
+  @Override
+  public boolean setsUnparkCG (ThreadInfo tiCurrent, ThreadInfo tiUnparked) {
+    return syncPolicy.setsUnparkCG(tiCurrent, tiUnparked);
+  }
+
+  @Override
+  public boolean setsBeginAtomicCG (ThreadInfo ti) {
+    return syncPolicy.setsBeginAtomicCG(ti);
+  }
+
+  @Override
+  public boolean setsEndAtomicCG (ThreadInfo ti) {
+    return syncPolicy.setsEndAtomicCG(ti);
+  }
+
+  @Override
+  public boolean setsRescheduleCG (ThreadInfo ti, String reason) {
+    return syncPolicy.setsRescheduleCG(ti, reason);
+  }
+
+  @Override
+  public boolean setsPostFinalizeCG (ThreadInfo tiFinalizer) {
+    return syncPolicy.setsPostFinalizeCG(tiFinalizer);
+  }
+
+  
+  //--- SharednessPolicy interface
+  
+  @Override
+  public void initializeSharednessPolicy (VM vm, ApplicationContext appCtx) {
+    sharednessPolicy.initializeSharednessPolicy( vm, appCtx);
+  }
+
+  @Override
+  public void initializeObjectSharedness (ThreadInfo allocThread, DynamicElementInfo ei) {
+    sharednessPolicy.initializeObjectSharedness(allocThread, ei);
+  }
+
+  @Override
+  public void initializeClassSharedness (ThreadInfo allocThread, StaticElementInfo ei) {
+    sharednessPolicy.initializeClassSharedness(allocThread, ei);
+  }
+
+  @Override
+  public boolean canHaveSharedObjectCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi) {
+    return sharednessPolicy.canHaveSharedObjectCG(ti, insn, eiFieldOwner, fi);
+  }
+
+  @Override
+  public ElementInfo updateObjectSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    return sharednessPolicy.updateObjectSharedness(ti, ei, fi);
+  }
+
+  @Override
+  public boolean setsSharedObjectCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi) {
+    return sharednessPolicy.setsSharedObjectCG(ti, insn, eiFieldOwner, fi);
+  }
+
+  @Override
+  public boolean canHaveSharedClassCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi) {
+    return sharednessPolicy.canHaveSharedClassCG(ti,insn,eiFieldOwner, fi);
+  }
+
+  @Override
+  public ElementInfo updateClassSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    return sharednessPolicy.updateClassSharedness(ti, ei, fi);
+  }
+
+  @Override
+  public boolean setsSharedClassCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi) {
+    return sharednessPolicy.setsSharedClassCG(ti, insn, eiFieldOwner, fi);
+  }
+
+  @Override
+  public boolean canHaveSharedArrayCG (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int idx) {
+    return sharednessPolicy.canHaveSharedArrayCG(ti, insn, eiArray, idx);
+  }
+
+  @Override
+  public ElementInfo updateArraySharedness (ThreadInfo ti, ElementInfo eiArray, int idx) {
+    return sharednessPolicy.updateArraySharedness(ti,eiArray,idx);
+  }
+
+  @Override
+  public boolean setsSharedArrayCG (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int idx) {
+    return sharednessPolicy.setsSharedArrayCG(ti, insn, eiArray, idx);
+  }
+
+  @Override
+  public boolean setsSharedObjectExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed) {
+    return sharednessPolicy.setsSharedObjectExposureCG(ti,insn,eiFieldOwner,fi,eiExposed);
+  }
+
+  @Override
+  public boolean setsSharedClassExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed) {
+    return sharednessPolicy.setsSharedClassExposureCG(ti, insn, eiFieldOwner, fi, eiExposed);
+  }
+
+  @Override
+  public void cleanupThreadTermination (ThreadInfo ti) {
+    sharednessPolicy.cleanupThreadTermination(ti);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DirectCallStackFrame.java b/src/main/gov/nasa/jpf/vm/DirectCallStackFrame.java
new file mode 100644 (file)
index 0000000..8cb928e
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.SystemAttribute;
+
+/**
+ * DirectCallStackFrames are only used for overlay calls (from native code), i.e.
+ * there is no corresponding INVOKE instruction. The associated MethodInfos are
+ * synthetic, their only code is (usually) a INVOKEx and a DIRECTCALLRETURN.
+ * NOTE: such MethodInfos do not belong to any class
+ * 
+ * Arguments for the invoke insn have to be pushed explicitly by the caller
+ * 
+ * direct calls do not return any values themselves, but they do get the return values of the
+ * called method pushed onto their own operand stack. If the DirectCallStackFrame user
+ * needs such return values, it has to do so via ThreadInfo.getReturnedDirectCall()
+ *
+ */
+public abstract class DirectCallStackFrame extends StackFrame implements SystemAttribute {
+  
+  MethodInfo callee;
+
+  protected DirectCallStackFrame (MethodInfo miDirectCall, MethodInfo callee, int maxLocals, int maxStack){
+    super( miDirectCall, maxLocals, maxStack);    
+    this.callee = callee;
+  }
+  
+  protected DirectCallStackFrame (MethodInfo miDirectCall, MethodInfo callee){
+    super( miDirectCall, miDirectCall.getMaxLocals(), miDirectCall.getMaxStack());
+    this.callee = callee;
+  }
+  
+  public MethodInfo getCallee (){
+    return callee;
+  }
+  
+  @Override
+  public String getStackTraceInfo () {
+    StringBuilder sb = new StringBuilder(128);
+    sb.append('[');
+    sb.append( callee.getUniqueName());
+    sb.append(']');
+    return sb.toString();
+  }
+  
+  public DirectCallStackFrame getPreviousDirectCallStackFrame(){
+    StackFrame f = prev;
+    while (f != null && !(f instanceof DirectCallStackFrame)){
+      f = f.prev;
+    }
+    
+    return (DirectCallStackFrame) f;
+  }
+  
+  public void setFireWall(){
+    mi.setFirewall(true);
+  }
+
+  @Override
+  public boolean isDirectCallFrame () {
+    return true;
+  }
+  
+  @Override
+  public boolean isSynthetic() {
+    return true;
+  }
+  
+  /*
+   * those set the callee arguments for the invoke insn - the returned value is the slot index OFFSET (not the slot index
+   * itself) for the next argument. This has to be used in a pattern like 
+   *   int argOffset = frame.setArgument( 0, firstArg, a0);
+   *   argOffset = frame.setLongArgument( argOffset, secondArg, a1);
+   *   ...
+   */
+  
+  public abstract int setArgument (int argOffset, int value, Object attr);
+  public abstract int setLongArgument (int argOffset, long value, Object attr);
+  public abstract int setReferenceArgument (int argOffset, int ref, Object attr);
+
+  public int setFloatArgument (int argOffset, float value, Object attr){
+    return setArgument( argOffset, Float.floatToIntBits(value), attr);
+  }
+  public int setDoubleArgument (int argOffset, double value, Object attr){
+    return setLongArgument( argOffset, Double.doubleToLongBits(value), attr);
+  }
+  
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/DoubleArrayFields.java b/src/main/gov/nasa/jpf/vm/DoubleArrayFields.java
new file mode 100644 (file)
index 0000000..d6543e2
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for double[] objects
+ */
+public class DoubleArrayFields extends ArrayFields {
+
+  double[] values;
+
+  public DoubleArrayFields (int length) {
+    values = new double[length];
+  }
+
+  @Override
+  public double[] asDoubleArray() {
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    DoubleArrayFields a = (DoubleArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 8;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendRawBits(values);
+  }
+
+  @Override
+  public DoubleArrayFields clone(){
+    DoubleArrayFields f = (DoubleArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof DoubleArrayFields) {
+      DoubleArrayFields other = (DoubleArrayFields)o;
+
+      double[] v = values;
+      double[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void setDoubleValue (int pos, double newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public double getDoubleValue (int pos) {
+    return values[pos];
+  }
+
+
+  @Override
+  public void hash(HashData hd) {
+    double[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/DoubleChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/DoubleChoiceGenerator.java
new file mode 100644 (file)
index 0000000..0541170
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+* Choice Generator that creates double values - this is only an
+* interface so that we can type check implementors that use 
+* their own generic hierarchy
+*/
+public interface DoubleChoiceGenerator extends ChoiceGenerator<Double> {
+}
diff --git a/src/main/gov/nasa/jpf/vm/DoubleFieldInfo.java b/src/main/gov/nasa/jpf/vm/DoubleFieldInfo.java
new file mode 100644 (file)
index 0000000..8cf6809
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+
+/**
+ * type, name and attribute information for 'double' fields
+ */
+public class DoubleFieldInfo extends DoubleSlotFieldInfo {
+  double init;
+
+  public DoubleFieldInfo (String name, int modifiers) {
+    super(name, "D", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Double){
+      cv = constValue;
+      init = ((Double)constValue).doubleValue();
+
+    } else {
+      throw new JPFException("illegal boolean ConstValue=" + constValue);
+    }
+  }
+
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setDoubleValue(storageOffset, init);
+  }
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType() {
+    return DoubleChoiceGenerator.class;
+  }
+
+  @Override
+  public int getStorageSize () {
+    return 2;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    double d = f.getDoubleValue(storageOffset);
+    return Double.toString(d);
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    double d = f.getDoubleValue(storageOffset);
+    return new Double(d);
+  }
+
+  @Override
+  public boolean isDoubleField(){
+    return true;
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+
+  @Override
+  public boolean isFloatingPointField(){
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/DoubleSlotFieldInfo.java b/src/main/gov/nasa/jpf/vm/DoubleSlotFieldInfo.java
new file mode 100644 (file)
index 0000000..798ca02
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a double or long field
+ */
+public abstract class DoubleSlotFieldInfo extends FieldInfo {
+
+    protected DoubleSlotFieldInfo (String name, String signature, int modifiers) {
+      super(name, signature, modifiers);
+    }
+    
+    @Override
+       public boolean is2SlotField(){
+      return true;
+    }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/DynamicElementInfo.java b/src/main/gov/nasa/jpf/vm/DynamicElementInfo.java
new file mode 100644 (file)
index 0000000..faeb280
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * A specialized version of ElementInfo that represents heap objects
+ */
+public class DynamicElementInfo extends ElementInfo {
+  
+  public DynamicElementInfo () {
+    // for restoration
+  }
+
+  public DynamicElementInfo (int objref, ClassInfo ci, Fields f, Monitor m, ThreadInfo ti) {
+    super(objref, ci, f, m, ti);
+
+    attributes = ci.getElementInfoAttrs();
+
+    ti.getScheduler().initializeObjectSharedness(ti, this);
+  }
+
+  @Override
+  public ElementInfo getModifiableInstance() {
+    if (!isFrozen()) {
+      return this;
+    } else {
+      return VM.getVM().getHeap().getModifiable( objRef);
+    }
+  }
+    
+  @Override
+  public boolean isObject(){
+    return true;
+  }
+  
+  @Override
+  public boolean hasFinalizer() {
+    return (ci.getFinalizer()!=null);
+  }
+  
+  @Override
+  protected int getNumberOfFieldsOrElements(){
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).arrayLength();
+    } else {
+      return ci.getNumberOfInstanceFields();
+    }
+  }
+
+  @Override
+  public int getNumberOfFields () {
+    return getClassInfo().getNumberOfInstanceFields();
+  }
+
+  @Override
+  public FieldInfo getFieldInfo (int fieldIndex) {
+    return getClassInfo().getInstanceField(fieldIndex);
+  }
+
+  @Override
+  public FieldInfo getFieldInfo (String fname) {
+    return getClassInfo().getInstanceField(fname);
+  }
+  @Override
+  protected FieldInfo getDeclaredFieldInfo (String clsBase, String fname) {
+    return getClassInfo().getClassLoaderInfo().getResolvedClassInfo(clsBase).getDeclaredInstanceField(fname);
+  }
+
+  @Override
+  public ElementInfo getEnclosingElementInfo(){
+    for (FieldInfo fi : getClassInfo().getDeclaredInstanceFields()){
+      if (fi.getName().startsWith("this$")){
+        int objref = getReferenceField(fi);
+        return VM.getVM().getElementInfo(objref);
+      }
+    }
+    return null;
+  }
+
+  @Override
+  public String asString() {
+    char[] data = getStringChars();
+    if (data != null){
+      return new String(data);
+      
+    } else {
+      return "";
+    }
+  }
+
+  @Override
+  public char[] getStringChars(){
+    if (!ClassInfo.isStringClassInfo(ci)) {
+      throw new JPFException("object is not of type java.lang.String");
+    }
+
+    int vref = getDeclaredReferenceField("value", "java.lang.String");    
+    if (vref != MJIEnv.NULL){
+      ElementInfo eVal = VM.getVM().getHeap().get(vref);
+      char[] value = eVal.asCharArray();
+      return value;
+      
+    } else {
+      return null;
+    }    
+  }
+  
+  /**
+   * just a helper to avoid creating objects just for the sake of comparing
+   */
+  @Override
+  public boolean equalsString (String s) {
+    if (!ClassInfo.isStringClassInfo(ci)) {
+      return false;
+    }
+
+    int vref = getDeclaredReferenceField("value", "java.lang.String");
+    ElementInfo e = VM.getVM().getHeap().get(vref);
+    CharArrayFields cf = (CharArrayFields)e.getFields();
+    char[] v = cf.asCharArray();
+    
+    return new String(v).equals(s);
+  }
+
+  @Override
+  public boolean isBoxObject(){
+    String cname = ci.getName();
+    if (cname.startsWith("java.lang.")){
+      cname = cname.substring(10);
+      return ("Boolean".equals(cname) ||
+          "Character".equals(cname) ||
+          "Byte".equals(cname) ||
+          "Short".equals(cname) ||
+          "Integer".equals(cname) ||
+          "Float".equals(cname) ||
+          "Long".equals(cname) ||
+          "Double".equals(cname) );
+        
+    } else {
+      return false;
+    }
+  }
+  
+  @Override
+  public Object asBoxObject(){
+    String cname = ci.getName();
+    if (cname.startsWith("java.lang.")){
+      cname = cname.substring(10);
+      if ("Boolean".equals(cname)){
+        return Boolean.valueOf( getBooleanField("value"));
+      } else if ("Character".equals(cname)){
+        return Character.valueOf(getCharField("value"));
+      } else if ("Byte".equals(cname)){
+        return Byte.valueOf( getByteField("value"));
+      } else if ("Short".equals(cname)){
+        return Short.valueOf( getShortField("value"));
+      } else if ("Integer".equals(cname)){
+        return Integer.valueOf( getIntField("value"));
+      } else if ("Float".equals(cname)){
+        return Float.valueOf( getFloatField("value"));
+      } else if ("Long".equals(cname)){
+        return Long.valueOf( getLongField("value"));
+      } else if ("Double".equals(cname)){
+        return Double.valueOf( getDoubleField("value"));
+      }
+    }
+    
+    throw new JPFException("object is not a box object: " + this);    
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ElementInfo.java b/src/main/gov/nasa/jpf/vm/ElementInfo.java
new file mode 100644 (file)
index 0000000..d853afb
--- /dev/null
@@ -0,0 +1,2257 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.ObjectList;
+import gov.nasa.jpf.util.Processor;
+
+import java.io.PrintWriter;
+
+/**
+ * Describes an element of memory containing the field values of a class or an
+ * object. In the case of a class, contains the values of the static fields. For
+ * an object contains the values of the object fields.
+ *
+ * @see gov.nasa.jpf.vm.FieldInfo
+ */
+public abstract class ElementInfo implements Cloneable {
+
+
+  //--- the lower 2 bytes of the attribute field are sticky (state stored/restored)
+
+  // object attribute flag values
+
+  // the first 8 bits constitute an unsigned pinDown count
+  public static final int   ATTR_PINDOWN_MASK = 0xff;
+
+  // this ElementInfo is not allowed to be modified anymore since it has been state stored
+  // ElementInfos are constructed in a non-frozen state, and will be frozen upon
+  // heap store (usuall in the heap or ElementInfo memento ctor)
+  // This is the basis for lifting the change management from the fields level (fields,monitor,attributes)
+  // to the ElementInfo object level
+  public static final int   ATTR_IS_FROZEN     = 0x100;
+
+  // object has an immutable type (no field value change)
+  public static final int   ATTR_IMMUTABLE     = 0x200;
+
+  // The constructor for the object has returned.  Final fields can no longer break POR
+  // This attribute is set in gov.nasa.jpf.vm.bytecode.RETURN.enter().
+  // If ThreadInfo.usePorSyncDetection() is false, then this attribute is never set.
+  public static final int   ATTR_CONSTRUCTED   = 0x400;
+
+  // object has been added to finalizer queue
+  public static final int ATTR_FINALIZED = 0x800;
+  
+  public static final int   ATTR_EXPOSED       = 0x1000;
+  
+  // object is shared between threads
+  public static final int   ATTR_SHARED        = 0x4000;
+  
+  // ATTR_SHARED is frozen (has to be changed explicitly, will not be updated by checkUpdatedSharedness)
+  public static final int   ATTR_FREEZE_SHARED = 0x8000; 
+  
+  
+  //--- the upper two bytes are for transient (heap internal) use only, and are not stored
+
+  // BEWARE if you add or change values, make sure these are not used in derived classes !
+  // <2do> this is efficient but fragile
+
+  public static final int   ATTR_TREF_CHANGED       = 0x10000; // referencingThreads have changed
+  public static final int   ATTR_FLI_CHANGED        = 0x20000; // fieldLockInfos changed
+  public static final int   ATTR_ATTRIBUTE_CHANGED  = 0x80000; // refers only to sticky bits
+
+  
+  //--- useful flag sets & masks
+
+  static final int   ATTR_STORE_MASK = 0x0000ffff;
+
+  static final int   ATTR_ANY_CHANGED = (ATTR_TREF_CHANGED | ATTR_FLI_CHANGED | ATTR_ATTRIBUTE_CHANGED);
+
+  // transient flag set if object is reachable from root object, i.e. can't be recycled
+  public static final int   ATTR_IS_MARKED   = 0x80000000;
+  
+  // this bit is set/unset by the heap in order to identify live objects that have
+  // already been unmarked. This is to avoid additional passes over the whole heap in
+  // order to clean up dangling references etc.
+  // NOTE - this bit should never be state stored - restored ElementInfo should never have it set
+  public static final int   ATTR_LIVE_BIT    = 0x40000000;
+  
+  public static final int   ATTR_MARKED_OR_LIVE_BIT = (ATTR_IS_MARKED | ATTR_LIVE_BIT);
+
+
+  //--- instance fields
+
+  protected ClassInfo       ci;
+  protected Fields          fields;
+  protected Monitor         monitor;
+  
+  // the set of threads using this object. Note this is not used for state matching
+  // so that order or thread id do not have a direct impact on heap symmetry
+  protected ThreadInfoSet referencingThreads;
+
+  // this is where we keep track of lock sets that potentially protect field access
+  // of shared objects. Since usually only a subset of objects are shared, we
+  // initialize this on demand
+  protected FieldLockInfo[] fLockInfo;
+
+  
+  // this is the reference value for the object represented by this ElementInfo
+  // (note this is a slight misnomer for StaticElementInfos, which don't really
+  // represent objects but collections of static fields belonging to the same class)
+  protected int objRef;
+
+  // these are our state-stored object attributes
+  // WATCH OUT! only include info that otherwise reflects a state change, so
+  // that we don't introduce new changes. Its value is used to hash the state!
+  // <2do> what a pity - 32 stored bits for (currently) only 2 bits of
+  // information,but we might use this as a hash for more complex reference
+  // info in the future.
+  // We distinguish between propagates and private object attributes, the first
+  // one stored in the lower 2 bytes
+  protected int attributes;
+
+  //--- the following fields are transient or search global, i.e. their values
+  // are not state-stored, but might be set during state restoration
+
+  // cache for unchanged ElementInfos, so that we don't have to re-create cachedMemento
+  // objects all the time
+  protected Memento<ElementInfo> cachedMemento;
+
+  // cache for a serialized representation of the object, which can be used
+  // by state-matching. Value interpretation depends on the configured Serializer
+  protected int sid;
+
+
+  // helpers for state storage/restore processing, to avoid explicit iterators on
+  // respective ElementInfo containers (heap,statics)
+  
+  static class Restorer implements Processor<ElementInfo> {
+    @Override
+    public void process (ElementInfo ei) {
+      ei.attributes &= ElementInfo.ATTR_STORE_MASK;
+      ei.sid = 0;
+      ei.updateLockingInfo();
+      ei.markUnchanged();
+    }        
+  }
+  static Restorer restorer = new Restorer();
+  
+  static class Storer implements Processor<ElementInfo> {
+    @Override
+    public void process (ElementInfo ei) {
+      ei.freeze();
+    }
+  }
+  static Storer storer = new Storer();
+  
+  
+  static boolean init (Config config) {
+    return true;
+  }
+
+  protected ElementInfo (int id, ClassInfo c, Fields f, Monitor m, ThreadInfo ti) {
+    objRef = id;
+    ci = c;
+    fields = f;
+    monitor = m;
+
+    assert ti != null; // we need that for our POR
+  }
+
+  public abstract ElementInfo getModifiableInstance();
+    
+  // not ideal, a sub-type checker.
+  public abstract boolean isObject();
+
+  public abstract boolean hasFinalizer();
+  
+  protected ElementInfo() {
+  }
+
+  public boolean hasChanged() {
+    return !isFrozen();
+    //return (attributes & ATTR_ANY_CHANGED) != 0;
+  }
+
+  @Override
+  public String toString() {
+    return ((ci != null ? ci.getName() : "ElementInfo") + '@' + Integer.toHexString(objRef));
+  }
+
+  public FieldLockInfo getFieldLockInfo (FieldInfo fi) {
+    if (fLockInfo != null){
+      return fLockInfo[fi.getFieldIndex()];
+    } else {
+      return null;
+    }
+  }
+
+  public void setFieldLockInfo (FieldInfo fi, FieldLockInfo flInfo) {
+    checkIsModifiable();
+
+    if (fLockInfo == null){
+      fLockInfo = new FieldLockInfo[getNumberOfFields()];
+    }
+    
+    fLockInfo[fi.getFieldIndex()] = flInfo;
+    attributes |= ATTR_FLI_CHANGED;
+  }
+  
+  public boolean isLockProtected (FieldInfo fi){
+    if (fLockInfo != null){
+      FieldLockInfo fli = fLockInfo[fi.getFieldIndex()];
+      if (fli != null){
+        return fli.isProtected();
+      }
+    }
+    
+    return false;
+  }
+
+  /**
+   * object is recycled (after potential finalization)
+   */
+  public void processReleaseActions(){
+    // type based release actions
+    ci.processReleaseActions(this);
+    
+    // instance based release actions
+    if (fields.hasObjectAttr()){
+      for (ReleaseAction action : fields.objectAttrIterator(ReleaseAction.class)){
+        action.release(this);
+      }
+    }
+  }
+
+  /**
+   * post transition live object cleanup
+   * update all non-fields references used by this object. This is only called
+   * at the end of the gc, and recycled objects should be either null or not marked
+   */
+  void cleanUp (Heap heap, boolean isThreadTermination, int tid) {
+    if (fLockInfo != null) {
+      for (int i=0; i<fLockInfo.length; i++) {
+        FieldLockInfo fli = fLockInfo[i];
+        if (fli != null) {
+          fLockInfo[i] = fli.cleanUp(heap);
+        }
+      }
+    }
+  }
+  
+  
+  //--- sids are only supposed to be used by the Serializer
+  public void setSid(int id){
+    sid = id;
+  }
+
+  public int getSid() {
+    return sid;
+  }
+
+  //--- cached mementos are only supposed to be used/set by the Restorer
+
+  public Memento<ElementInfo> getCachedMemento(){
+    return cachedMemento;
+  }
+
+  public void setCachedMemento (Memento<ElementInfo> memento){
+    cachedMemento = memento;
+  }
+
+  /**
+   * do we have a reference field with value objRef?
+   */
+  public boolean hasRefField (int objRef) {
+    return ci.hasRefField( objRef, fields);
+  }
+
+
+  public int numberOfUserThreads() {
+    return referencingThreads.size();
+  }
+
+
+  /**
+   * the recursive phase2 marker entry, which propagates the attributes set by a
+   * previous phase1. This one is called on all 'root'-marked objects after
+   * phase1 is completed. ElementInfo is not an ideal place for this method, as
+   * it has to access some innards of both ClassInfo (FieldInfo container) and
+   * Fields. But on the other hand, we want to keep the whole heap traversal
+   * business as much centralized in ElementInfo and Heap implementors
+   */
+  void markRecursive(Heap heap) {
+    int i, n;
+
+    if (isArray()) {
+      if (fields.isReferenceArray()) {
+        n = ((ArrayFields)fields).arrayLength();
+        for (i = 0; i < n; i++) {
+          int objref = fields.getReferenceValue(i);
+          if (objref != MJIEnv.NULL){
+            heap.queueMark( objref);
+          }
+        }
+      }
+
+    } else { // not an array
+      ClassInfo ci = getClassInfo();
+      boolean isWeakRef = ci.isWeakReference();
+
+      do {
+        n = ci.getNumberOfDeclaredInstanceFields();
+        boolean isRef = isWeakRef && ci.isReferenceClassInfo(); // is this the java.lang.ref.Reference part?
+
+        for (i = 0; i < n; i++) {
+          FieldInfo fi = ci.getDeclaredInstanceField(i);
+          if (fi.isReference()) {
+            if ((i == 0) && isRef) {
+              // we need to reset the ref field once the referenced object goes away
+              // NOTE: only the *first* WeakReference field is a weak ref
+              // (this is why we have our own implementation)
+              heap.registerWeakReference(this);
+            } else {
+              int objref = fields.getReferenceValue(fi.getStorageOffset());
+              if (objref != MJIEnv.NULL){
+                heap.queueMark( objref);
+              }
+            }
+          }
+        }
+        ci = ci.getSuperClass();
+      } while (ci != null);
+    }
+  }
+
+
+  int getAttributes () {
+    return attributes;
+  }
+
+  int getStoredAttributes() {
+    return attributes & ATTR_STORE_MASK;
+  }
+
+  public boolean isImmutable() {
+    return ((attributes & ATTR_IMMUTABLE) != 0);
+  }
+
+  //--- freeze handling
+  
+  public void freeze() {
+    attributes |= ATTR_IS_FROZEN;
+  }
+
+  public void defreeze() {
+    attributes &= ~ATTR_IS_FROZEN;
+  }
+  
+  public boolean isFrozen() {
+    return ((attributes & ATTR_IS_FROZEN) != 0);    
+  }
+  
+  //--- shared handling
+
+  /**
+   * set the referencing threads. Unless you know this is from a non-shared
+   * context, make sure to update sharedness by calling setShared()
+   */
+  public void setReferencingThreads (ThreadInfoSet refThreads){
+    checkIsModifiable();    
+    referencingThreads = refThreads;
+  }
+  
+  public ThreadInfoSet getReferencingThreads (){
+    return referencingThreads;
+  }
+  
+  public void freezeSharedness (boolean freeze) {
+    if (freeze) {
+      if ((attributes & ATTR_FREEZE_SHARED) == 0) {
+        checkIsModifiable();
+        attributes |= (ATTR_FREEZE_SHARED | ATTR_ATTRIBUTE_CHANGED);
+      }
+    } else {
+      if ((attributes & ATTR_FREEZE_SHARED) != 0) {
+        checkIsModifiable();
+        attributes &= ~ATTR_FREEZE_SHARED;
+        attributes |= ATTR_ATTRIBUTE_CHANGED;
+      }
+    }
+  }
+  
+  public boolean isSharednessFrozen () {
+    return (attributes & ATTR_FREEZE_SHARED) != 0;
+  }
+  
+  public boolean isShared() {
+    //return usingTi.getNumberOfLiveThreads() > 1;
+    return ((attributes & ATTR_SHARED) != 0);
+  }
+  
+  public void setShared (ThreadInfo ti, boolean isShared) {
+    if (isShared) {
+      if ((attributes & ATTR_SHARED) == 0) {
+        checkIsModifiable();
+        attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED);
+        
+        // note we don't report the thread here since this is explicitly set via Verify.setShared
+        VM.getVM().notifyObjectShared(ti, this);
+      }
+    } else {
+      if ((attributes & ATTR_SHARED) != 0) {
+        checkIsModifiable();
+        attributes &= ~ATTR_SHARED;
+        attributes |= ATTR_ATTRIBUTE_CHANGED;
+      }
+    }
+  }
+  
+  /**
+   * NOTE - this should only be called internally if we know the object is
+   * modifiable (e.g. from the ctor)
+   */
+  void setSharednessFromReferencingThreads () {
+    if (referencingThreads.isShared( null, this)) {
+      if ((attributes & ATTR_SHARED) == 0) {
+        checkIsModifiable();
+        attributes |= (ATTR_SHARED | ATTR_ATTRIBUTE_CHANGED);
+      }
+    }
+  }
+  
+  public boolean isReferencedBySameThreads (ElementInfo eiOther) {
+    return referencingThreads.equals(eiOther.referencingThreads);
+  }
+  
+  public boolean isReferencedByThread (ThreadInfo ti) {
+    return referencingThreads.contains(ti);
+  }
+  
+  public boolean isExposed(){
+    return (attributes & ATTR_EXPOSED) != 0;
+  }
+  
+  public boolean isExposedOrShared(){
+    return (attributes & (ATTR_SHARED | ATTR_EXPOSED)) != 0;
+  }
+  
+  public ElementInfo getExposedInstance (ThreadInfo ti, ElementInfo eiFieldOwner){
+    ElementInfo ei = getModifiableInstance();
+    ei.setExposed( ti, eiFieldOwner);
+    
+    // <2do> do we have to traverse every object reachable from here?
+    // (does every reference of an indirectly exposed object have to go through this one?)
+    
+    return ei;
+  }
+  
+  protected void setExposed (){
+    attributes |= (ATTR_EXPOSED | ATTR_ATTRIBUTE_CHANGED);
+  }
+  
+  public void setExposed (ThreadInfo ti, ElementInfo eiFieldOwner){
+    // we actually have to add this to the attributes to avoid endless loops by
+    // re-exposing the same object along a given path
+    attributes |= (ATTR_EXPOSED | ATTR_ATTRIBUTE_CHANGED);
+    
+    ti.getVM().notifyObjectExposed(ti, eiFieldOwner, this);
+  }
+  
+  /**
+   * this is called before the system attempts to reclaim the object. If
+   * we return 'false', the object will *not* be removed
+   */
+  protected boolean recycle () {  
+    // this is required to avoid loosing field lock assumptions
+    // when the system sequentialized threads with conflicting assumptions,
+    // but the offending object goes out of scope before the system backtracks
+    if (hasVolatileFieldLockInfos()) {
+      return false;
+    }
+
+    setObjectRef(MJIEnv.NULL);
+
+    return true;
+  }
+
+  boolean hasVolatileFieldLockInfos() {
+    if (fLockInfo != null) {
+      for (int i=0; i<fLockInfo.length; i++) {
+        FieldLockInfo fli = fLockInfo[i];
+        if (fli != null) {
+          if (fli.needsPindown(this)) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+  
+  public void hash(HashData hd) {
+    hd.add(ci.getClassLoaderInfo().getId());
+    hd.add(ci.getId());
+    fields.hash(hd);
+    monitor.hash(hd);
+    hd.add(attributes & ATTR_STORE_MASK);
+  }
+
+  @Override
+  public int hashCode() {
+    HashData hd = new HashData();
+
+    hash(hd);
+
+    return hd.getValue();
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (o != null && o instanceof ElementInfo) {
+      ElementInfo other = (ElementInfo) o;
+
+      if (ci != other.ci){
+        return false;
+      }
+
+      if ((attributes & ATTR_STORE_MASK) != (other.attributes & ATTR_STORE_MASK)){
+        return false;
+      }
+      if (!fields.equals(other.fields)) {
+        return false;
+      }
+      if (!monitor.equals(other.monitor)){
+        return false;
+      }
+      if (referencingThreads != other.referencingThreads){
+        return false;
+      }
+
+      return true;
+
+    } else {
+      return false;
+    }
+  }
+
+  public ClassInfo getClassInfo() {
+    return ci;
+  }
+
+  abstract protected FieldInfo getDeclaredFieldInfo(String clsBase, String fname);
+
+  abstract protected FieldInfo getFieldInfo(String fname);
+
+  protected abstract int getNumberOfFieldsOrElements();
+
+  
+  //--- Object attribute accessors
+
+  public boolean hasObjectAttr(){
+    return fields.hasObjectAttr();
+  }
+  
+  public boolean hasObjectAttr(Class<?> attrType) {
+    return fields.hasObjectAttr(attrType);
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getObjectAttr(){
+    return fields.getObjectAttr();
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setObjectAttr (Object a){
+    checkIsModifiable();
+    fields.setObjectAttr(a);
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setObjectAttrNoClone (Object a){
+    fields.setObjectAttr(a);
+  }
+
+  
+  public void addObjectAttr(Object a){
+    checkIsModifiable();
+    fields.addObjectAttr(a);
+  }
+  public void removeObjectAttr(Object a){
+    checkIsModifiable();
+    fields.removeObjectAttr(a);
+  }
+  public void replaceObjectAttr(Object oldAttr, Object newAttr){
+    checkIsModifiable();
+    fields.replaceObjectAttr(oldAttr, newAttr);
+  }
+
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getObjectAttr (Class<T> attrType) {
+    return fields.getObjectAttr(attrType);
+  }
+  public <T> T getNextObjectAttr (Class<T> attrType, Object prev) {
+    return fields.getNextObjectAttr(attrType, prev);
+  }
+  public ObjectList.Iterator objectAttrIterator(){
+    return fields.objectAttrIterator();
+  }
+  public <T> ObjectList.TypedIterator<T> objectAttrIterator(Class<T> type){
+    return fields.objectAttrIterator(type);
+  }
+  
+  //--- field attribute accessors
+  
+  public boolean hasFieldAttr() {
+    return fields.hasFieldAttr();
+  }
+
+  public boolean hasFieldAttr (Class<?> attrType){
+    return fields.hasFieldAttr(attrType);
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getFieldAttr (FieldInfo fi){
+    return fields.getFieldAttr(fi.getFieldIndex());
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setFieldAttr (FieldInfo fi, Object attr){
+    checkIsModifiable();
+    
+    int nFields = getNumberOfFieldsOrElements();
+    fields.setFieldAttr( nFields, fi.getFieldIndex(), attr);    
+  }
+
+  
+  public void addFieldAttr (FieldInfo fi, Object a){
+    checkIsModifiable();
+    
+    int nFields = getNumberOfFieldsOrElements();    
+    fields.addFieldAttr( nFields, fi.getFieldIndex(), a);
+  }
+  public void removeFieldAttr (FieldInfo fi, Object a){
+    checkIsModifiable();
+    fields.removeFieldAttr(fi.getFieldIndex(), a);
+  }
+  public void replaceFieldAttr (FieldInfo fi, Object oldAttr, Object newAttr){
+    checkIsModifiable();    
+    fields.replaceFieldAttr(fi.getFieldIndex(), oldAttr, newAttr);
+  }
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getFieldAttr (FieldInfo fi, Class<T> attrType) {
+    return fields.getFieldAttr(fi.getFieldIndex(), attrType);
+  }
+  public <T> T getNextFieldAttr (FieldInfo fi, Class<T> attrType, Object prev) {
+    return fields.getNextFieldAttr(fi.getFieldIndex(), attrType, prev);
+  }
+  public ObjectList.Iterator fieldAttrIterator (FieldInfo fi){
+    return fields.fieldAttrIterator(fi.getFieldIndex());
+  }
+  public <T> ObjectList.TypedIterator<T> fieldAttrIterator (FieldInfo fi, Class<T> type){
+    return fields.fieldAttrIterator(fi.getFieldIndex(), type);
+  }
+  
+
+  
+  //--- element attribute accessors
+  
+  public boolean hasElementAttr() {
+    return fields.hasFieldAttr();
+  }
+
+  public boolean hasElementAttr (Class<?> attrType){
+    return fields.hasFieldAttr(attrType);
+  }
+
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getElementAttr (int idx){
+    return fields.getFieldAttr(idx);
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setElementAttr (int idx, Object attr){
+    int nElements = getNumberOfFieldsOrElements();
+    checkIsModifiable();
+    fields.setFieldAttr( nElements, idx, attr);
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setElementAttrNoClone (int idx, Object attr){
+    int nElements = getNumberOfFieldsOrElements();
+    fields.setFieldAttr(nElements,idx, attr);
+  }
+
+  
+  public void addElementAttr (int idx, Object a){
+    checkIsModifiable();
+    
+    int nElements = getNumberOfFieldsOrElements();   
+    fields.addFieldAttr( nElements, idx, a);
+  }
+  public void removeElementAttr (int idx, Object a){
+    checkIsModifiable();
+    fields.removeFieldAttr(idx, a);
+  }
+  public void replaceElementAttr (int idx, Object oldAttr, Object newAttr){
+    checkIsModifiable();
+    fields.replaceFieldAttr(idx, oldAttr, newAttr);
+  }
+
+/** <2do> those will be obsolete */
+  public void addElementAttrNoClone (int idx, Object a){
+    int nElements = getNumberOfFieldsOrElements();   
+    fields.addFieldAttr( nElements, idx, a);
+  }
+  public void removeElementAttrNoClone (int idx, Object a){
+    fields.removeFieldAttr(idx, a);
+  }
+  public void replaceElementAttrNoClone (int idx, Object oldAttr, Object newAttr){
+    fields.replaceFieldAttr(idx, oldAttr, newAttr);
+  }
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getElementAttr (int idx, Class<T> attrType) {
+    return fields.getFieldAttr(idx, attrType);
+  }
+  public <T> T getNextElementAttr (int idx, Class<T> attrType, Object prev) {
+    return fields.getNextFieldAttr(idx, attrType, prev);
+  }
+  public ObjectList.Iterator elementAttrIterator (int idx){
+    return fields.fieldAttrIterator(idx);
+  }
+  public <T> ObjectList.TypedIterator<T> elementAttrIterator (int idx, Class<T> type){
+    return fields.fieldAttrIterator(idx, type);
+  }
+
+  // -- end attributes --
+  
+  
+  public void setDeclaredIntField(String fname, String clsBase, int value) {
+    setIntField(getDeclaredFieldInfo(clsBase, fname), value);
+  }
+
+  public void setBooleanField (String fname, boolean value) {
+    setBooleanField( getFieldInfo(fname), value);
+  }
+  public void setByteField (String fname, byte value) {
+    setByteField( getFieldInfo(fname), value);
+  }
+  public void setCharField (String fname, char value) {
+    setCharField( getFieldInfo(fname), value);
+  }
+  public void setShortField (String fname, short value) {
+    setShortField( getFieldInfo(fname), value);
+  }
+  public void setIntField(String fname, int value) {
+    setIntField(getFieldInfo(fname), value);
+  }
+  public void setLongField (String fname, long value) {
+    setLongField( getFieldInfo(fname), value);
+  }
+  public void setFloatField (String fname, float value) {
+    setFloatField( getFieldInfo(fname), value);
+  }
+  public void setDoubleField (String fname, double value) {
+    setDoubleField( getFieldInfo(fname), value);
+  }
+  public void setReferenceField (String fname, int value) {
+    setReferenceField( getFieldInfo(fname), value);
+  }
+
+
+  // <2do> we need to tell 'null' values apart from 'no such field'
+  public Object getFieldValueObject (String fname) {
+    Object ret = null;
+    FieldInfo fi = getFieldInfo(fname);
+
+    if (fi != null){
+      ret = fi.getValueObject(fields);
+
+    } else {
+      // check if there is an enclosing class object
+      ElementInfo eiEnclosing = getEnclosingElementInfo();
+      if (eiEnclosing != null){
+        ret = eiEnclosing.getFieldValueObject(fname);
+
+      } else {
+        // we should check static fields in enclosing scopes, but there is no
+        // other way than to guess this from the name, and the outer
+        // classes might not even be initialized yet
+      }
+    }
+
+    return ret;
+  }
+
+  public ElementInfo getEnclosingElementInfo() {
+    return null; // only for DynamicElementInfos
+  }
+
+  public void setBooleanField(FieldInfo fi, boolean newValue) {
+    checkIsModifiable();
+    
+    if (fi.isBooleanField()) {
+      int offset = fi.getStorageOffset();
+      fields.setBooleanValue( offset, newValue);
+    } else {
+      throw new JPFException("not a boolean field: " + fi.getFullName());
+    }
+  }
+
+  public void setByteField(FieldInfo fi, byte newValue) {
+    checkIsModifiable();
+    
+    if (fi.isByteField()) {
+      int offset = fi.getStorageOffset();
+      fields.setByteValue( offset, newValue);
+    } else {
+      throw new JPFException("not a byte field: " + fi.getFullName());
+    }
+  }
+
+  public void setCharField(FieldInfo fi, char newValue) {
+    checkIsModifiable();
+    
+    if (fi.isCharField()) {
+      int offset = fi.getStorageOffset();
+      fields.setCharValue( offset, newValue);
+    } else {
+      throw new JPFException("not a char field: " + fi.getFullName());
+    }
+  }
+
+  public void setShortField(FieldInfo fi, short newValue) {
+    checkIsModifiable();
+
+    if (fi.isShortField()) {
+      int offset = fi.getStorageOffset();
+      fields.setShortValue( offset, newValue);
+    } else {
+      throw new JPFException("not a short field: " + fi.getFullName());
+    }
+  }
+
+  public void setIntField(FieldInfo fi, int newValue) {
+    checkIsModifiable();
+
+    if (fi.isIntField()) {
+      int offset = fi.getStorageOffset();
+      fields.setIntValue( offset, newValue);
+    } else {
+      throw new JPFException("not an int field: " + fi.getFullName());
+    }
+  }
+
+  public void setLongField(FieldInfo fi, long newValue) {
+    checkIsModifiable();
+
+    if (fi.isLongField()) {
+      int offset = fi.getStorageOffset();
+      fields.setLongValue( offset, newValue);
+    } else {
+      throw new JPFException("not a long field: " + fi.getFullName());
+    }
+  }
+
+  public void setFloatField(FieldInfo fi, float newValue) {
+    checkIsModifiable();
+
+    if (fi.isFloatField()) {
+      int offset = fi.getStorageOffset();
+      fields.setFloatValue( offset, newValue);
+    } else {
+      throw new JPFException("not a float field: " + fi.getFullName());
+    }
+  }
+
+  public void setDoubleField(FieldInfo fi, double newValue) {
+    checkIsModifiable();
+
+    if (fi.isDoubleField()) {
+      int offset = fi.getStorageOffset();
+      fields.setDoubleValue( offset, newValue);
+    } else {
+      throw new JPFException("not a double field: " + fi.getFullName());
+    }
+  }
+
+  public void setReferenceField(FieldInfo fi, int newValue) {
+    checkIsModifiable();
+
+    if (fi.isReference()) {
+      int offset = fi.getStorageOffset();
+      fields.setReferenceValue( offset, newValue);
+    } else {
+      throw new JPFException("not a reference field: " + fi.getFullName());
+    }
+  }
+
+  public void set1SlotField(FieldInfo fi, int newValue) {
+    checkIsModifiable();
+
+    if (fi.is1SlotField()) {
+      int offset = fi.getStorageOffset();
+      fields.setIntValue( offset, newValue);
+    } else {
+      throw new JPFException("not a 1 slot field: " + fi.getFullName());
+    }
+  }
+
+  public void set2SlotField(FieldInfo fi, long newValue) {
+    checkIsModifiable();
+
+    if (fi.is2SlotField()) {
+      int offset = fi.getStorageOffset();
+      fields.setLongValue( offset, newValue);
+    } else {
+      throw new JPFException("not a 2 slot field: " + fi.getFullName());
+    }
+  }
+
+
+  public void setDeclaredReferenceField(String fname, String clsBase, int value) {
+    setReferenceField(getDeclaredFieldInfo(clsBase, fname), value);
+  }
+
+  public int getDeclaredReferenceField(String fname, String clsBase) {
+    FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
+    return getReferenceField( fi);
+  }
+
+  public int getReferenceField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getReferenceField( fi);
+  }
+
+
+  public int getDeclaredIntField(String fname, String clsBase) {
+    // be aware of that static fields are not flattened (they are unique), i.e.
+    // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo
+    FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
+    return getIntField( fi);
+  }
+
+  public int getIntField(String fname) {
+    // be aware of that static fields are not flattened (they are unique), i.e.
+    // the FieldInfo might actually refer to another ClassInfo/StaticElementInfo
+    FieldInfo fi = getFieldInfo(fname);
+    return getIntField( fi);
+  }
+
+  public void setDeclaredLongField(String fname, String clsBase, long value) {
+    checkIsModifiable();
+    
+    FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
+    fields.setLongValue( fi.getStorageOffset(), value);
+  }
+
+  public long getDeclaredLongField(String fname, String clsBase) {
+    FieldInfo fi = getDeclaredFieldInfo(clsBase, fname);
+    return getLongField( fi);
+  }
+
+  public long getLongField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getLongField( fi);
+  }
+
+  public boolean getDeclaredBooleanField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getBooleanField( fi);
+  }
+
+  public boolean getBooleanField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getBooleanField( fi);
+  }
+
+  public byte getDeclaredByteField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getByteField( fi);
+  }
+
+  public byte getByteField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getByteField( fi);
+  }
+
+  public char getDeclaredCharField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getCharField( fi);
+  }
+
+  public char getCharField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getCharField( fi);
+  }
+
+  public double getDeclaredDoubleField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getDoubleField( fi);
+  }
+
+  public double getDoubleField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getDoubleField( fi);
+  }
+
+  public float getDeclaredFloatField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getFloatField( fi);
+  }
+
+  public float getFloatField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getFloatField( fi);
+  }
+
+  public short getDeclaredShortField(String fname, String refType) {
+    FieldInfo fi = getDeclaredFieldInfo(refType, fname);
+    return getShortField( fi);
+  }
+
+  public short getShortField(String fname) {
+    FieldInfo fi = getFieldInfo(fname);
+    return getShortField( fi);
+  }
+
+  /**
+   * note this only holds for instance fields, and hence the method has to
+   * be overridden in StaticElementInfo
+   */
+  private void checkFieldInfo(FieldInfo fi) {
+    if (!getClassInfo().isInstanceOf(fi.getClassInfo())) {
+      throw new JPFException("wrong FieldInfo : " + fi.getName()
+          + " , no such field in " + getClassInfo().getName());
+    }
+  }
+
+  // those are the cached field value accessors. The caller is responsible
+  // for assuring type compatibility
+
+  public boolean getBooleanField(FieldInfo fi) {
+    if (fi.isBooleanField()){
+      return fields.getBooleanValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a boolean field: " + fi.getName());
+    }
+  }
+  public byte getByteField(FieldInfo fi) {
+    if (fi.isByteField()){
+      return fields.getByteValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a byte field: " + fi.getName());
+    }
+  }
+  public char getCharField(FieldInfo fi) {
+    if (fi.isCharField()){
+      return fields.getCharValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a char field: " + fi.getName());
+    }
+  }
+  public short getShortField(FieldInfo fi) {
+    if (fi.isShortField()){
+      return fields.getShortValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a short field: " + fi.getName());
+    }
+  }
+  public int getIntField(FieldInfo fi) {
+    if (fi.isIntField()){
+      return fields.getIntValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a int field: " + fi.getName());
+    }
+  }
+  public long getLongField(FieldInfo fi) {
+    if (fi.isLongField()){
+      return fields.getLongValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a long field: " + fi.getName());
+    }
+  }
+  public float getFloatField (FieldInfo fi){
+    if (fi.isFloatField()){
+      return fields.getFloatValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a float field: " + fi.getName());
+    }
+  }
+  public double getDoubleField (FieldInfo fi){
+    if (fi.isDoubleField()){
+      return fields.getDoubleValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a double field: " + fi.getName());
+    }
+  }
+  public int getReferenceField (FieldInfo fi) {
+    if (fi.isReference()){
+      return fields.getReferenceValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a reference field: " + fi.getName());
+    }
+  }
+
+  public int get1SlotField(FieldInfo fi) {
+    if (fi.is1SlotField()){
+      return fields.getIntValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a 1 slot field: " + fi.getName());
+    }
+  }
+  public long get2SlotField(FieldInfo fi) {
+    if (fi.is2SlotField()){
+      return fields.getLongValue(fi.getStorageOffset());
+    } else {
+      throw new JPFException("not a 2 slot field: " + fi.getName());
+    }
+  }
+
+  protected void checkArray(int index) {
+    if (fields instanceof ArrayFields) { // <2do> should check for !long array
+      if ((index < 0) || (index >= ((ArrayFields)fields).arrayLength())) {
+        throw new JPFException("illegal array offset: " + index);
+      }
+    } else {
+      throw new JPFException("cannot access non array objects by index");
+    }
+  }
+
+  public boolean isReferenceArray() {
+    return getClassInfo().isReferenceArray();
+  }
+
+  /**
+   * this is the backend for System.arraycopy implementations, but since it only
+   * throws general exceptions it can also be used in other contexts that require
+   * type and objRef checking
+   *
+   * note that we have to do some additional type checking here because we store
+   * reference arrays as int[], i.e. for reference arrays we can't rely on
+   * System.arraycopy to do the element type checking for us
+   *
+   * @throws java.lang.ArrayIndexOutOfBoundsException
+   * @throws java.lang.ArrayStoreException
+   */
+  public void copyElements( ThreadInfo ti, ElementInfo eiSrc, int srcIdx, int dstIdx, int length){
+
+    if (!isArray()){
+      throw new ArrayStoreException("destination object not an array: " + ci.getName());
+    }
+    if (!eiSrc.isArray()){
+      throw new ArrayStoreException("source object not an array: " + eiSrc.getClassInfo().getName());
+    }
+
+    boolean isRefArray = isReferenceArray();
+    if (eiSrc.isReferenceArray() != isRefArray){
+      throw new ArrayStoreException("array types not compatible: " + eiSrc.getClassInfo().getName() + " and " + ci.getName());
+    }
+
+    // since the caller has to handle normal ArrayStoreExceptions and
+    // ArrayIndexOutOfBoundsExceptions, we don't have to explicitly check array length here
+
+    // if we copy reference arrays, we first have to check element type compatibility
+    // (the underlying Fields type is always int[], hence we have to do this explicitly)
+    if (isRefArray){
+      ClassInfo dstElementCi = ci.getComponentClassInfo();
+      int[] srcRefs = ((ArrayFields)eiSrc.fields).asReferenceArray();
+      int max = srcIdx + length;
+
+      for (int i=srcIdx; i<max; i++){
+        int eref = srcRefs[i];
+        if (eref != MJIEnv.NULL){
+          ClassInfo srcElementCi = ti.getClassInfo(eref);
+          if (!srcElementCi.isInstanceOf(dstElementCi)) {
+            throw new ArrayStoreException("incompatible reference array element type (required " + dstElementCi.getName() +
+                    ", found " + srcElementCi.getName());
+          }
+        }
+      }
+    }
+
+    // NOTE - we have to clone the fields even in case System.arraycopy fails, since
+    // the caller might handle ArrayStore/IndexOutOfBounds, and partial changes
+    // have to be preserved
+    // note also this preserves values in case of a self copy
+    checkIsModifiable();
+
+    Object srcVals = ((ArrayFields)eiSrc.getFields()).getValues();
+    Object dstVals = ((ArrayFields)fields).getValues();
+
+    // this might throw ArrayIndexOutOfBoundsExceptions and ArrayStoreExceptions
+    System.arraycopy(srcVals, srcIdx, dstVals, dstIdx, length);
+
+    // now take care of the attributes
+    // <2do> what in case arraycopy did throw - we should only copy the changed element attrs
+    if (eiSrc.hasFieldAttr()){
+      if (eiSrc == this && srcIdx < dstIdx) { // self copy
+        for (int i = length - 1; i >= 0; i--) {
+          Object a = eiSrc.getElementAttr( srcIdx+i);
+          setElementAttr( dstIdx+i, a);
+        }
+      } else {
+        for (int i = 0; i < length; i++) {
+          Object a = eiSrc.getElementAttr(srcIdx+i);
+          setElementAttr( dstIdx+i, a);
+        }
+      }
+    }
+  }
+
+  public void setBooleanElement(int idx, boolean value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setBooleanValue(idx, value);
+  }
+  public void setByteElement(int idx, byte value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setByteValue(idx, value);
+  }
+  public void setCharElement(int idx, char value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setCharValue(idx, value);
+  }
+  public void setShortElement(int idx, short value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setShortValue(idx, value);
+  }
+  public void setIntElement(int idx, int value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setIntValue(idx, value);
+  }
+  public void setLongElement(int idx, long value) {
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setLongValue(idx, value);
+  }
+  public void setFloatElement(int idx, float value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setFloatValue(idx, value);
+  }
+  public void setDoubleElement(int idx, double value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setDoubleValue(idx, value);
+  }
+  public void setReferenceElement(int idx, int value){
+    checkArray(idx);
+    checkIsModifiable();
+    fields.setReferenceValue(idx, value);
+  }
+
+  /**
+   * NOTE - this doesn't support element type checks or overlapping in-array copy 
+   */
+  public void arrayCopy (ElementInfo src, int srcPos, int dstPos, int len){
+    checkArray(dstPos+len-1);
+    src.checkArray(srcPos+len-1);
+    checkIsModifiable();
+    
+    ArrayFields da = (ArrayFields)fields;
+    ArrayFields sa = (ArrayFields)src.fields;
+    
+    da.copyElements(sa, srcPos, dstPos, len);
+  }
+
+  public boolean getBooleanElement(int idx) {
+    checkArray(idx);
+    return fields.getBooleanValue(idx);
+  }
+  public byte getByteElement(int idx) {
+    checkArray(idx);
+    return fields.getByteValue(idx);
+  }
+  public char getCharElement(int idx) {
+    checkArray(idx);
+    return fields.getCharValue(idx);
+  }
+  public short getShortElement(int idx) {
+    checkArray(idx);
+    return fields.getShortValue(idx);
+  }
+  public int getIntElement(int idx) {
+    checkArray(idx);
+    return fields.getIntValue(idx);
+  }
+  public long getLongElement(int idx) {
+    checkArray(idx);
+    return fields.getLongValue(idx);
+  }
+  public float getFloatElement(int idx) {
+    checkArray(idx);
+    return fields.getFloatValue(idx);
+  }
+  public double getDoubleElement(int idx) {
+    checkArray(idx);
+    return fields.getDoubleValue(idx);
+  }
+  public int getReferenceElement(int idx) {
+    checkArray(idx);
+    return fields.getReferenceValue(idx);
+  }
+
+  public void setObjectRef(int newObjRef) {
+    objRef = newObjRef;
+  }
+
+  public int getObjectRef() {
+    return objRef;
+  }
+
+  /** use getObjectRef() - this is not an index */
+  @Deprecated
+  public int getIndex(){
+    return objRef;
+  }
+
+  public int getLockCount() {
+    return monitor.getLockCount();
+  }
+
+  public ThreadInfo getLockingThread() {
+    return monitor.getLockingThread();
+  }
+
+  public boolean isLocked() {
+    return (monitor.getLockCount() > 0);
+  }
+
+  public boolean isArray() {
+    return ci.isArray();
+  }
+
+  public boolean isCharArray(){
+    return (fields instanceof CharArrayFields);
+  }
+  
+  public boolean isFloatArray(){
+    return (fields instanceof FloatArrayFields);
+  }
+
+  public boolean isDoubleArray(){
+    return (fields instanceof DoubleArrayFields);
+  }
+
+  
+  public String getArrayType() {
+    if (!ci.isArray()) {
+      throw new JPFException("object is not an array");
+    }
+
+    return Types.getArrayElementType(ci.getType());
+  }
+
+  public Object getBacktrackData() {
+    return null;
+  }
+
+
+  // <2do> these will check for corresponding ArrayFields types
+  public boolean[] asBooleanArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asBooleanArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public byte[] asByteArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asByteArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public short[] asShortArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asShortArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public char[] asCharArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asCharArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public int[] asIntArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asIntArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public long[] asLongArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asLongArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public float[] asFloatArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asFloatArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public double[] asDoubleArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asDoubleArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public int[] asReferenceArray() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).asReferenceArray();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+    
+  public boolean isNull() {
+    return (objRef == MJIEnv.NULL);
+  }
+
+  public ElementInfo getDeclaredObjectField(String fname, String referenceType) {
+    return VM.getVM().getHeap().get(getDeclaredReferenceField(fname, referenceType));
+  }
+
+  public ElementInfo getObjectField(String fname) {
+    return VM.getVM().getHeap().get(getReferenceField(fname));
+  }
+
+
+  /**
+   * answer an estimate of the heap size in bytes (this is of course VM
+   * dependent, but we can give an upper bound for the fields/elements, and that
+   * should be good in terms of application specific properties)
+   */
+  public int getHeapSize() {
+    return fields.getHeapSize();
+  }
+
+  public String getStringField(String fname) {
+    int ref = getReferenceField(fname);
+
+    if (ref != MJIEnv.NULL) {
+      ElementInfo ei = VM.getVM().getHeap().get(ref);
+      return ei.asString();
+    } else {
+      return "null";
+    }
+  }
+
+  public String getType() {
+    return ci.getType();
+  }
+
+  /**
+   * return all threads that are trying to acquire this lock
+   * (blocked, waiting, interrupted)
+   * NOTE - this is not a copy, don't modify the array
+   */
+  public ThreadInfo[] getLockedThreads() {
+    return monitor.getLockedThreads();
+  }
+  
+  /**
+   * get a cloned list of the waiters for this object
+   */
+  public ThreadInfo[] getWaitingThreads() {
+    return monitor.getWaitingThreads();
+  }
+
+  public boolean hasWaitingThreads() {
+    return monitor.hasWaitingThreads();
+  }
+
+  public ThreadInfo[] getBlockedThreads() {
+    return monitor.getBlockedThreads();
+  }
+
+  public ThreadInfo[] getBlockedOrWaitingThreads() {
+    return monitor.getBlockedOrWaitingThreads();
+  }
+    
+  public int arrayLength() {
+    if (fields instanceof ArrayFields){
+      return ((ArrayFields)fields).arrayLength();
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+
+  public boolean isStringObject() {
+    return ClassInfo.isStringClassInfo(ci);
+  }
+
+  public String asString() {
+    throw new JPFException("not a String object: " + this);
+  }
+
+  public char[] getStringChars(){
+    throw new JPFException("not a String object: " + this);    
+  }
+  
+  /**
+   * just a helper to avoid creating objects just for the sake of comparing
+   */
+  public boolean equalsString (String s) {
+    throw new JPFException("not a String object: " + this);
+  }
+
+  /**
+   * is this a Number, a Boolean or a Character object
+   * Note these classes are all final, so we don't have to check for subtypes
+   * 
+   * <2do> we should probably use a regular expression here
+   */
+  public boolean isBoxObject(){
+    return false;
+  }
+  
+  public Object asBoxObject(){
+    throw new JPFException("not a box object: " + this);    
+  }
+  
+  void updateLockingInfo() {
+    int i;
+
+    ThreadInfo ti = monitor.getLockingThread();
+    if (ti != null) {
+      // here we can update ThreadInfo lock object info (so that we don't
+      // have to store it separately)
+
+      // NOTE - the threads need to be restored *before* the ElementInfo containers,
+      // or this is going to choke
+
+      // note that we add only once, i.e. rely on the monitor lockCount to
+      // determine when to remove an object from our lock set
+      //assert area.ks.tl.get(ti.objRef) == ti;  // covered by verifyLockInfo
+      ti.updateLockedObject(this);
+    }
+
+    if (monitor.hasLockedThreads()) {
+      ThreadInfo[] lockedThreads = monitor.getLockedThreads();
+      for (i=0; i<lockedThreads.length; i++) {
+        ti = lockedThreads[i];
+        //assert area.ks.tl.get(ti.objRef) == ti;  // covered by verifyLockInfo
+        
+        // note that the thread might still be runnable if we have several threads
+        // competing for the same lock
+        if (!ti.isRunnable()){
+          ti.setLockRef(objRef);
+        }
+      }
+    }
+  }
+
+  public boolean canLock(ThreadInfo th) {
+    return monitor.canLock(th);
+  }
+
+  public void checkArrayBounds(int index) throws ArrayIndexOutOfBoundsExecutiveException {
+    if (fields instanceof ArrayFields) {
+      if (index < 0 || index >= ((ArrayFields)fields).arrayLength()){
+        throw new ArrayIndexOutOfBoundsExecutiveException(
+              ThreadInfo.getCurrentThread().createAndThrowException(
+              "java.lang.ArrayIndexOutOfBoundsException", Integer.toString(index)));
+      }
+    } else {
+      throw new JPFException("object is not an array: " + this);
+    }
+  }
+
+  @Override
+  public ElementInfo clone() {
+    try {
+      ElementInfo ei = (ElementInfo) super.clone();
+      ei.fields = fields.clone();
+      ei.monitor = monitor.clone();
+
+      return ei;
+      
+    } catch (CloneNotSupportedException e) {
+      throw new InternalError("should not happen");
+    }
+  }
+
+  // this is the one that should be used by heap
+  public ElementInfo deepClone() {
+    try {
+      ElementInfo ei = (ElementInfo) super.clone();
+      ei.fields = fields.clone();
+      ei.monitor = monitor.clone();
+      
+      // referencingThreads is at least subtree global, hence doesn't need to be cloned
+      
+      ei.cachedMemento = null;
+      ei.defreeze();
+      
+      return ei;
+      
+    } catch (CloneNotSupportedException e) {
+      throw new InternalError("should not happen");
+    }
+    
+  }
+
+  public boolean instanceOf(String type) {
+    return Types.instanceOf(ci.getType(), type);
+  }
+
+  abstract public int getNumberOfFields();
+
+  abstract public FieldInfo getFieldInfo(int fieldIndex);
+
+  /**
+   * threads that will grab our lock on their next execution have to be
+   * registered, so that they can be blocked in case somebody else gets
+   * scheduled
+   */
+  public void registerLockContender (ThreadInfo ti) {
+
+    assert ti.lockRef == MJIEnv.NULL || ti.lockRef == objRef :
+      "thread " + ti + " trying to register for : " + this +
+      " ,but already blocked on: " + ti.getElementInfo(ti.lockRef);
+
+    // note that using the lockedThreads list is a bit counter-intuitive
+    // since the thread is still in RUNNING or UNBLOCKED state, but it will
+    // remove itself from there once it resumes: lock() calls setMonitorWithoutLocked(ti)
+    setMonitorWithLocked(ti);
+
+    // added to satisfy invariant implied by updateLockingInfo() -peterd
+    //ti.setLockRef(objRef);
+  }
+
+  public boolean isRegisteredLockContender (ThreadInfo ti){
+    return monitor.isLocking(ti);
+  }
+  
+  /**
+   * somebody made up his mind and decided not to enter a synchronized section
+   * of code it had registered before (e.g. INVOKECLINIT)
+   */
+  public void unregisterLockContender (ThreadInfo ti) {
+    setMonitorWithoutLocked(ti);
+
+    // moved from INVOKECLINIT -peterd
+    //ti.resetLockRef();
+  }
+
+  void blockLockContenders () {
+    // check if there are any other threads that have to change status because they
+    // require to lock us in their next exec
+    ThreadInfo[] lockedThreads = monitor.getLockedThreads();
+    for (int i=0; i<lockedThreads.length; i++) {
+      ThreadInfo ti = lockedThreads[i];
+      if (ti.isRunnable()) {
+        ti.setBlockedState(objRef);
+      }
+    }
+  }
+
+  /**
+   * from a MONITOR_ENTER or sync INVOKExx if we cannot acquire the lock
+   * note: this is not called from a NOTIFIED_UNBLOCKED state, so we don't have to restore NOTIFIED
+   */
+  public void block (ThreadInfo ti) {
+    assert (monitor.getLockingThread() != null) && (monitor.getLockingThread() != ti) :
+          "attempt to block " + ti.getName() + " on unlocked or own locked object: " + this;
+
+    setMonitorWithLocked(ti);
+    
+    ti.setBlockedState(objRef);    
+  }
+
+  /**
+   * from a MONITOR_ENTER or sync INVOKExx if we can acquire the lock
+   */
+  public void lock (ThreadInfo ti) {
+    // if we do unlock consistency checks with JPFExceptions, we should do the same here
+    if ((monitor.getLockingThread() != null) &&  (monitor.getLockingThread() != ti)){
+      throw new JPFException("thread " + ti.getName() + " tries to lock object: "
+              + this + " which is locked by: " + monitor.getLockingThread().getName());
+    }
+    
+    // the thread might be still in the lockedThreads list if this is the
+    // first step of a transition
+    setMonitorWithoutLocked(ti);
+    monitor.setLockingThread(ti);
+    monitor.incLockCount();
+
+    // before we enter anything else, mark this thread as not being blocked anymore
+    ti.resetLockRef();
+
+    ThreadInfo.State state = ti.getState();
+    if (state == ThreadInfo.State.UNBLOCKED) {
+      ti.setState(ThreadInfo.State.RUNNING);
+    }
+
+    // don't re-add if we are recursive - the lock count is avaliable in the monitor
+    if (monitor.getLockCount() == 1) {
+      ti.addLockedObject(this);
+    }
+
+    // this might set other threads blocked - make sure we lock first or the sequence
+    // of notifications is a bit screwed (i.e. the lock would appear *after* the block)
+    blockLockContenders();
+  }
+
+  /**
+   * from a MONITOR_EXIT or sync method RETURN
+   * release a possibly recursive lock if lockCount goes to zero
+   * 
+   * return true if this unblocked any waiters
+   */
+  public boolean unlock (ThreadInfo ti) {
+    boolean didUnblock = false;
+
+    checkIsModifiable();
+    
+    /* If there is a compiler bug, we need to flag it.  Most compilers should 
+     * generate balanced monitorenter and monitorexit instructions for all code 
+     * paths.  The VM is being used for more non-Java languages.  Some of these 
+     * compilers might be experimental and might generate unbalanced 
+     * instructions.  In a more likely case, dynamically generated bytecode is
+     * more likely to make a mistake and miss a code path.
+     */
+    if ((monitor.getLockCount() <= 0) || (monitor.getLockingThread() != ti)){
+      throw new JPFException("thread " + ti.getName() + " tries to release non-owned lock for object: " + this);
+    }
+
+    if (monitor.getLockCount() == 1) {
+      ti.removeLockedObject(this);
+
+      ThreadInfo[] lockedThreads = monitor.getLockedThreads();
+      for (int i = 0; i < lockedThreads.length; i++) {
+        ThreadInfo lti = lockedThreads[i];
+        switch (lti.getState()) {
+
+        case BLOCKED:
+        case NOTIFIED:
+        case TIMEDOUT:
+        case INTERRUPTED:
+          // Ok, this thread becomes runnable again
+          lti.resetLockRef();
+          lti.setState(ThreadInfo.State.UNBLOCKED);
+          didUnblock = true;
+          break;
+
+        case WAITING:
+        case TIMEOUT_WAITING:
+          // nothing to do yet, thread has to timeout, get notified, or interrupted
+          break;
+
+        default:
+          assert false : "Monitor.lockedThreads<->ThreadData.status inconsistency! " + lockedThreads[i].getStateName();
+          // why is it in the list - when someone unlocks, all others should have been blocked
+        }
+      }
+
+      // leave the contenders - we need to know whom to block on subsequent lock
+
+      monitor.decLockCount();
+      monitor.setLockingThread(null);
+
+    } else { // recursive unlock
+      monitor.decLockCount();
+    }
+    
+    return didUnblock;
+  }
+
+  /**
+   * notifies one of the waiters. Note this is a potentially non-deterministic action
+   * if we have several waiters, since we have to try all possible choices.
+   * Note that even if we notify a thread here, it still remains in the lockedThreads
+   * list until the lock is released (notified threads cannot run right away)
+   */
+  public boolean notifies(SystemState ss, ThreadInfo ti){
+    return notifies(ss, ti, true);
+  }
+  
+  
+  private void notifies0 (ThreadInfo tiWaiter){
+    if (tiWaiter.isWaitingOrTimedOut()){
+      if (tiWaiter.getLockCount() > 0) {
+        // waiter did hold the lock, but gave it up in the wait,  so it can't run yet
+        tiWaiter.setNotifiedState();
+
+      } else {
+        // waiter didn't hold the lock, set it running
+        setMonitorWithoutLocked(tiWaiter);
+        tiWaiter.resetLockRef();
+        tiWaiter.setRunning();
+      }
+    }
+  }
+
+  
+  /** return true if this did notify any waiters */
+  public boolean notifies (SystemState ss, ThreadInfo ti, boolean hasToHoldLock){
+    if (hasToHoldLock){
+      assert monitor.getLockingThread() != null : "notify on unlocked object: " + this;
+    }
+
+    ThreadInfo[] locked = monitor.getLockedThreads();
+    int i, nWaiters=0, iWaiter=0;
+
+    for (i=0; i<locked.length; i++) {
+      if (locked[i].isWaitingOrTimedOut() ) {
+        nWaiters++;
+        iWaiter = i;
+      }
+    }
+
+    if (nWaiters == 0) {
+      // no waiters, nothing to do
+    } else if (nWaiters == 1) {
+      // only one waiter, no choice point
+      notifies0(locked[iWaiter]);
+
+    } else {
+      // Ok, this is the non-deterministic case
+      ThreadChoiceGenerator cg = ss.getCurrentChoiceGeneratorOfType(ThreadChoiceGenerator.class);
+
+      assert (cg != null) : "no ThreadChoiceGenerator in notify";
+
+      notifies0(cg.getNextChoice());
+    }
+
+    ti.getVM().notifyObjectNotifies(ti, this);
+    return (nWaiters > 0);
+  }
+
+  /**
+   * notify all waiters. This is a deterministic action
+   * all waiters remain in the locked list, since they still have to be unblocked,
+   * which happens in the unlock (monitor_exit or sync return) following the notifyAll()
+   */
+  public boolean notifiesAll() {
+    assert monitor.getLockingThread() != null : "notifyAll on unlocked object: " + this;
+
+    ThreadInfo[] locked = monitor.getLockedThreads();
+    for (int i=0; i<locked.length; i++) {
+      // !!!! if there is more than one BLOCKED thread (sync call or monitor enter), only one can be
+      // unblocked
+      notifies0(locked[i]);
+    }
+
+    VM.getVM().notifyObjectNotifiesAll(ThreadInfo.currentThread, this);
+    return (locked.length > 0);
+  }
+
+
+  /**
+   * wait to be notified. thread has to hold the lock, but gives it up in the wait.
+   * Make sure lockCount can be restored properly upon notification
+   */
+  public void wait(ThreadInfo ti, long timeout){
+    wait(ti,timeout,true);
+  }
+
+  // this is used from a context where we don't require a lock, e.g. Unsafe.park()/unpark()
+  public void wait (ThreadInfo ti, long timeout, boolean hasToHoldLock){
+    checkIsModifiable();
+    
+    boolean holdsLock = monitor.getLockingThread() == ti;
+
+    if (hasToHoldLock){
+      assert holdsLock : "wait on unlocked object: " + this;
+    }
+
+    setMonitorWithLocked(ti);
+    ti.setLockRef(objRef);
+    
+    if (timeout == 0) {
+      ti.setState(ThreadInfo.State.WAITING);
+    } else {
+      ti.setState(ThreadInfo.State.TIMEOUT_WAITING);
+    }
+
+    if (holdsLock) {
+      ti.setLockCount(monitor.getLockCount());
+
+      monitor.setLockingThread(null);
+      monitor.setLockCount(0);
+
+      ti.removeLockedObject(this);
+
+      // unblock all runnable threads that are blocked on this lock
+      ThreadInfo[] lockedThreads = monitor.getLockedThreads();
+      for (int i = 0; i < lockedThreads.length; i++) {
+        ThreadInfo lti = lockedThreads[i];
+        switch (lti.getState()) {
+          case NOTIFIED:
+          case BLOCKED:
+          case INTERRUPTED:
+            lti.resetLockRef();
+            lti.setState(ThreadInfo.State.UNBLOCKED);
+            break;
+        }
+      }
+    }
+
+    // <2do> not sure if this is right if we don't hold the lock
+    ti.getVM().notifyObjectWait(ti, this);
+  }
+
+
+  /**
+   * re-acquire lock after being notified. This is the notified thread, i.e. the one
+   * that will come out of a wait()
+   */
+  public void lockNotified (ThreadInfo ti) {
+    assert ti.isUnblocked() : "resume waiting thread " + ti.getName() + " which is not unblocked";
+
+    setMonitorWithoutLocked(ti);
+    monitor.setLockingThread( ti);
+    monitor.setLockCount( ti.getLockCount());
+
+    ti.setLockCount(0);
+    ti.resetLockRef();
+    ti.setState( ThreadInfo.State.RUNNING);
+
+    blockLockContenders();
+
+    // this is important, if we later-on backtrack (reset the
+    // ThreadInfo.lockedObjects set, and then restore from the saved heap), the
+    // lock set would not include the lock when we continue to enter this thread
+    ti.addLockedObject(this); //wv: add locked object back here
+  }
+
+  /**
+   * this is for waiters that did not own the lock
+   */
+  public void resumeNonlockedWaiter (ThreadInfo ti){
+    setMonitorWithoutLocked(ti);
+
+    ti.setLockCount(0);
+    ti.resetLockRef();
+    ti.setRunning();
+  }
+
+
+  void dumpMonitor () {
+    PrintWriter pw = new PrintWriter(System.out, true);
+    pw.print( "monitor ");
+    //pw.print( mIndex);
+    monitor.printFields(pw);
+    pw.flush();
+  }
+
+  /**
+   * updates a pinDown counter. If it is > 0 the object is kept alive regardless
+   * if it is reachable from live objects or not.
+   * @return true if the new counter is 1, i.e. the object just became pinned down
+   *
+   * NOTE - this is *not* a public method and you probably want to use
+   * Heap.register/unregisterPinDown(). Pinning down an object is now
+   * done through the Heap API, which updates the counter here, but might also
+   * have to update internal caches
+   */
+  boolean incPinDown() {
+    int pdCount = (attributes & ATTR_PINDOWN_MASK);
+
+    pdCount++;
+    if ((pdCount & ~ATTR_PINDOWN_MASK) != 0){
+      throw new JPFException("pinDown limit exceeded: " + this);
+    } else {
+      int a = (attributes & ~ATTR_PINDOWN_MASK);
+      a |= pdCount;
+      a |= ATTR_ATTRIBUTE_CHANGED;
+      attributes = a;
+      
+      return (pdCount == 1);
+    }
+  }
+
+  /**
+   * see incPinDown
+   *
+   * @return true if the counter becomes 0, i.e. the object just ceased to be
+   * pinned down
+   */
+  boolean decPinDown() {
+    int pdCount = (attributes & ATTR_PINDOWN_MASK);
+
+    if (pdCount > 0){
+      pdCount--;
+      int a = (attributes & ~ATTR_PINDOWN_MASK);
+      a |= pdCount;
+      a |= ATTR_ATTRIBUTE_CHANGED;
+      attributes = a;
+
+      return (pdCount == 0);
+      
+    } else {
+      return false;
+    }
+  }
+
+  public int getPinDownCount() {
+    return (attributes & ATTR_PINDOWN_MASK);
+  }
+
+  public boolean isPinnedDown() {
+    return (attributes & ATTR_PINDOWN_MASK) != 0;
+  }
+
+
+  public boolean isConstructed() {
+    return (attributes & ATTR_CONSTRUCTED) != 0;
+  }
+
+  public void setConstructed() {
+    attributes |= (ATTR_CONSTRUCTED | ATTR_ATTRIBUTE_CHANGED);
+  }
+
+  public void restoreFields(Fields f) {
+    fields = f;
+  }
+
+  /**
+   * BEWARE - never change the returned object without knowing about the
+   * ElementInfo change status, this field is state managed!
+   */
+  public Fields getFields() {
+    return fields;
+  }
+
+  public ArrayFields getArrayFields(){
+    if (fields instanceof ArrayFields){
+      return (ArrayFields)fields;
+    } else {
+      throw new JPFException("not an array: " + ci.getName());
+    }
+  }
+  
+  public void restore(int index, int attributes, Fields fields, Monitor monitor){
+    markUnchanged();
+    
+    this.objRef = index;
+    this.attributes = attributes;
+    this.fields = fields;
+    this.monitor = monitor;
+  }
+
+  public void restoreMonitor(Monitor m) {
+    monitor = m;
+  }
+
+  /**
+   * BEWARE - never change the returned object without knowing about the
+   * ElementInfo change status, this field is state managed!
+   */
+  public Monitor getMonitor() {
+    return monitor;
+  }
+
+  public void restoreAttributes(int a) {
+    attributes = a;
+  }
+
+  public boolean isAlive(boolean liveBitValue) {
+    return ((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue;
+  }
+
+  public void setAlive(boolean liveBitValue){
+    if (liveBitValue){
+      attributes |= ATTR_LIVE_BIT;
+    } else {
+      attributes &= ~ATTR_LIVE_BIT;
+    }
+  }
+
+  public boolean isMarked() {
+    return (attributes & ATTR_IS_MARKED) != 0;
+  }
+
+  public boolean isFinalized() {
+    return (attributes & ATTR_FINALIZED) != 0;
+  }
+  
+  public void setFinalized() {
+    attributes |= ATTR_FINALIZED;
+  }
+  
+  public void setMarked() {
+    attributes |= ATTR_IS_MARKED;
+  }
+
+  public boolean isMarkedOrAlive (boolean liveBitValue){
+    return ((attributes & ATTR_IS_MARKED) != 0) | (((attributes & ATTR_LIVE_BIT) == 0) ^ liveBitValue);
+  }
+
+  public void markUnchanged() {
+    attributes &= ~ATTR_ANY_CHANGED;
+  }
+
+  public void setUnmarked() {
+    attributes &= ~ATTR_IS_MARKED;
+  }
+
+
+  protected void checkIsModifiable() {
+    if ((attributes & ATTR_IS_FROZEN) != 0) {
+      throw new JPFException("attempt to modify frozen object: " + this);
+    }
+  }
+
+  void setMonitorWithLocked( ThreadInfo ti) {
+    checkIsModifiable();
+    monitor.addLocked(ti);
+  }
+
+  void setMonitorWithoutLocked (ThreadInfo ti) {
+    checkIsModifiable();    
+    monitor.removeLocked(ti);
+  }
+
+  public boolean isLockedBy(ThreadInfo ti) {
+    return ((monitor != null) && (monitor.getLockingThread() == ti));
+  }
+
+  public boolean isLocking(ThreadInfo ti){
+    return (monitor != null) && monitor.isLocking(ti);
+  }
+  
+  void _printAttributes(String cls, String msg, int oldAttrs) {
+    if (getClassInfo().getName().equals(cls)) {
+      System.out.println(msg + " " + this + " attributes: "
+          + Integer.toHexString(attributes) + " was: "
+          + Integer.toHexString(oldAttrs));
+    }
+  }
+
+    
+  public void checkConsistency() {
+/**
+    ThreadInfo ti = monitor.getLockingThread();
+    if (ti != null) {
+      // object has to be in the lockedObjects list of this thread
+      checkAssertion( ti.getLockedObjects().contains(this), "locked object not in thread: " + ti);
+    }
+
+    if (monitor.hasLockedThreads()) {
+      checkAssertion( refTid.cardinality() > 1, "locked threads without multiple referencing threads");
+
+      for (ThreadInfo lti : monitor.getBlockedOrWaitingThreads()){
+        checkAssertion( lti.lockRef == objRef, "blocked or waiting thread has invalid lockRef: " + lti);
+      }
+
+      // we can't check for having lock contenders without being shared, since this can happen
+      // in case an object is behind a FieldInfo shared-ness firewall (e.g. ThreadGroup.threads), or
+      // is kept/used in native code (listener, peer)
+    }
+**/
+  }
+  
+  protected void checkAssertion(boolean cond, String failMsg){
+    if (!cond){
+      System.out.println("!!!!!! failed ElementInfo consistency: "  + this + ": " + failMsg);
+
+      System.out.println("object: " + this);
+      System.out.println("usingTi: " + referencingThreads);
+      
+      ThreadInfo tiLock = getLockingThread();
+      if (tiLock != null) System.out.println("locked by: " + tiLock);
+      
+      if (monitor.hasLockedThreads()){
+        System.out.println("lock contenders:");
+        for (ThreadInfo ti : monitor.getLockedThreads()){
+          System.out.println("  " + ti + " = " + ti.getState());
+        }
+      }
+      
+      VM.getVM().dumpThreadStates();
+      assert false;
+    }
+  }
+
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/ExceptionHandler.java b/src/main/gov/nasa/jpf/vm/ExceptionHandler.java
new file mode 100644 (file)
index 0000000..b14aebd
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * Stores the information about an exception handler.
+ */
+public class ExceptionHandler {
+  /**
+   * Name of the exception caught. If it is 'null', this means this is an 'any' handler
+   */
+  private String name;
+
+  /**
+   * The first instruction belonging to this handler.
+   */
+  private int begin;
+
+  /**
+   * The last instruction belonging to this handler.
+   */
+  private int end;
+
+  /**
+   * The offset of the handler.
+   */
+  private int handler;
+
+  /**
+   * Creates a new exception handler.
+   */
+  public ExceptionHandler (String n, int b, int e, int h) {
+    name = n;
+    begin = b;
+    end = e;
+    handler = h;
+  }
+
+  /**
+   * Returns the first instruction in the block.
+   */
+  public int getBegin () {
+    return begin;
+  }
+
+  /**
+   * Returns the last instruction in the block.
+   */
+  public int getEnd () {
+    return end;
+  }
+
+  /**
+   * Returns the instruction location for the handler.
+   */
+  public int getHandler () {
+    return handler;
+  }
+
+  /**
+   * Returns the name of the exception caught.
+   */
+  public String getName () {
+    return name;
+  }
+
+
+  @Override
+  public String toString() {
+    return "Handler [name="+name+",from="+begin+",to="+end+",target="+handler+"]";
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ExceptionInfo.java b/src/main/gov/nasa/jpf/vm/ExceptionInfo.java
new file mode 100644 (file)
index 0000000..0b3e4ae
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.PrintWriter;
+
+/**
+ * helper class to store context of an exception
+ */
+public class ExceptionInfo {
+  ElementInfo  ei;
+  ThreadInfo ti;
+  
+  ExceptionInfo (ThreadInfo xThread, ElementInfo xEi) {
+    ti = xThread;
+    ei = xEi;
+  }
+  
+  public ElementInfo getException() {
+    return ei;
+  }
+  
+  public int getExceptionReference () {
+    return ei.getObjectRef();
+  }
+  
+  public String getExceptionClassname() {
+    return ei.getClassInfo().getName();
+  }
+  
+  public String getDetails() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getExceptionClassname());
+    
+    int msgRef = ei.getReferenceField("detailMessage");
+    if (msgRef != MJIEnv.NULL){
+      ElementInfo eiMsg = ti.getElementInfo(msgRef);
+      sb.append(" : ");
+      sb.append(eiMsg.asString());
+    }
+      
+    return sb.toString();
+  }
+  
+  public String getCauseClassname() {
+    int causeRef = ei.getReferenceField("cause");
+    if (causeRef != MJIEnv.NULL){
+      ElementInfo eiCause = ti.getElementInfo(causeRef);
+      return eiCause.getClassInfo().getName();
+    }
+    
+    return null;
+  }
+  public String getCauseDetails() {
+    int causeRef = ei.getReferenceField("cause");
+    if (causeRef != MJIEnv.NULL){
+      ElementInfo eiCause = ti.getElementInfo(causeRef);
+      int msgRef = eiCause.getReferenceField("detailMessage");
+      if (msgRef != MJIEnv.NULL){
+        ElementInfo eiMsg = ti.getElementInfo(msgRef);
+        return eiMsg.asString();
+      }
+    }
+
+    return null;
+  }
+
+  
+  public ThreadInfo getThread() {
+    return ti;
+  }
+  
+  public void printOn (PrintWriter pw){
+    ti.printStackTrace(pw, ei.getObjectRef());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ExceptionParameterAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/ExceptionParameterAnnotationInfo.java
new file mode 100644 (file)
index 0000000..1f07873
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for catch arguments
+ */
+public class ExceptionParameterAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int exceptionIndex;
+  
+  public ExceptionParameterAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath, int throwsIndex)  {
+    super( base, targetType, typePath);
+    
+    this.exceptionIndex = exceptionIndex;
+  }
+  
+  public int getExceptionIndex(){
+    return exceptionIndex;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/FieldInfo.java b/src/main/gov/nasa/jpf/vm/FieldInfo.java
new file mode 100644 (file)
index 0000000..f32ee47
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.lang.reflect.Modifier;
+
+
+/**
+ * type, name and attribute information of a field.
+ */
+public abstract class FieldInfo extends InfoObject implements GenericSignatureHolder {
+
+  //--- FieldInfo attributes
+  // don't break transitions on get/putXX insns of this field, even if shared
+  static final int NEVER_BREAK = 0x10000;
+  
+  // always break on this field's access if object is shared
+  // (ignored if NEVER_BREAK is set)
+  static final int BREAK_SHARED = 0x20000;
+
+  // those might relate to sticky ElementInto.ATTR_*
+  protected int attributes;
+
+  
+  protected final String name;
+  protected String type;  // lazy initialized fully qualified type name as per JLS 6.7 ("int", "x.Y[]")
+  protected final String signature; // "I", "[Lx/Y;" etc.
+  protected int storageSize;
+
+  protected ClassInfo ci; // class this field belongs to
+  protected int fieldIndex; // declaration ordinal
+
+  // where in the corresponding Fields object do we store the value
+  // (note this works because of the wonderful single inheritance)
+  protected int storageOffset;
+
+  // optional initializer for this field, can't be final because it is set from
+  // classfile field_info attributes (i.e. after construction)
+  protected  Object cv;
+
+  protected String genericSignature;
+
+  protected int modifiers;
+  
+  public static FieldInfo create (String name, String signature, int modifiers){
+    switch(signature.charAt(0)){
+      case 'Z':
+        return new BooleanFieldInfo(name, modifiers);
+      case 'B':
+        return new ByteFieldInfo(name, modifiers);
+      case 'S':
+        return new ShortFieldInfo(name, modifiers);
+      case 'C':
+        return new CharFieldInfo(name, modifiers);
+      case 'I':
+        return new IntegerFieldInfo(name, modifiers);
+      case 'J':
+        return new LongFieldInfo(name, modifiers);
+      case 'F':
+        return new FloatFieldInfo(name, modifiers);
+      case 'D':
+        return new DoubleFieldInfo(name, modifiers);
+      default:
+        return new ReferenceFieldInfo(name, signature, modifiers);
+    }
+  }
+  
+  protected FieldInfo(String name, String signature, int modifiers) {
+    this.name = name;
+    this.signature = signature;
+    this.modifiers = modifiers;
+  }
+
+  protected void linkToClass (ClassInfo ci, int idx, int off){
+    this.ci = ci;
+    this.fieldIndex = idx;
+    this.storageOffset = off;
+  }
+  
+  // those are set subsequently from classfile attributes
+  public void setConstantValue(Object constValue){
+    cv = constValue;
+  }
+
+  public abstract String valueToString (Fields f);
+
+  public boolean is1SlotField(){
+    return false;
+  }
+  public boolean is2SlotField(){
+    return false;
+  }
+
+  public boolean isBooleanField() {
+    return false;
+  }
+  public boolean isByteField() {
+    return false;
+  }
+  public boolean isCharField() {
+    return false;
+  }
+  public boolean isShortField() {
+    return false;
+  }
+  public boolean isIntField() {
+    return false;
+  }
+  public boolean isLongField() {
+    return false;
+  }
+  public boolean isFloatField(){
+    return false;
+  }
+  public boolean isDoubleField(){
+    return false;
+  }
+
+  public boolean isNumericField(){
+    return false;
+  }
+
+  public boolean isFloatingPointField(){
+    return false;
+  }
+
+  public boolean isReference () {
+    return false;
+  }
+
+  public boolean isArrayField () {
+    return false;
+  }
+
+  /**
+   * Returns the class that this field is associated with.
+   */
+  public ClassInfo getClassInfo () {
+    return ci;
+  }
+
+  public Object getConstantValue () {
+    return cv;
+  }
+
+  public abstract Object getValueObject (Fields data);
+
+  public int getModifiers() {
+    return modifiers;
+  }
+
+  public int getFieldIndex () {
+    return fieldIndex;
+  }
+
+  /**
+   * is this a static field? Counter productive to the current class struct,
+   * but at some point we want to get rid of the Dynamic/Static branch (it's
+   * really just a field attribute)
+   */
+  public boolean isStatic () {
+    return (modifiers & Modifier.STATIC) != 0;
+  }
+
+  /**
+   * is this field declared `final'?
+   */
+  public boolean isFinal () {
+    return (modifiers & Modifier.FINAL) != 0;
+  }
+
+  public boolean isVolatile () {
+    return (modifiers & Modifier.VOLATILE) != 0;
+  }
+
+  public boolean isTransient () {
+    return (modifiers & Modifier.TRANSIENT) != 0;
+  }
+
+  public boolean isPublic () {
+    return (modifiers & Modifier.PUBLIC) != 0;
+  }
+
+  public boolean isPrivate () {
+    return (modifiers & Modifier.PRIVATE) != 0;
+  }
+
+  public boolean isProtected () {
+    return (modifiers & Modifier.PROTECTED) != 0;
+  }
+
+  public boolean isPackagePrivate() {
+    return (modifiers & (Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE)) == 0;
+  }
+  
+  /**
+   * Returns the name of the field.
+   */
+  public String getName () {
+    return name;
+  }
+
+  /**
+   * @return the storage size of this field, @see Types.getTypeSize
+   */
+  public int getStorageSize () {
+    return 1;
+  }
+
+  /**
+   * Returns the type of the field as a fully qualified type name according to JLS 6.7
+   * ("int", "x.Y[]")
+   */
+  public String getType () {
+    if (type == null){
+      type = Types.getTypeName(signature);
+    }
+    return type;
+  }
+  
+  public byte getTypeCode (){
+    return Types.getTypeCode(signature);
+  }
+
+  public String getSignature(){
+    return signature;
+  }
+
+  @Override
+  public String getGenericSignature() {
+    return genericSignature; 
+  }
+
+  @Override
+  public void setGenericSignature(String sig){
+    genericSignature = sig;
+  }
+
+  public ClassInfo getTypeClassInfo () {
+    return ClassLoaderInfo.getCurrentResolvedClassInfo(getType());
+  }
+
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType (){
+    return null;
+  }
+
+  /**
+   * pushClinit the corresponding data in the provided Fields instance
+   */
+  public abstract void initialize (ElementInfo ei, ThreadInfo ti);
+
+
+  /**
+   * Returns a string representation of the field.
+   */
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder();
+
+    if (isStatic()) {
+      sb.append("static ");
+    }
+    if (isFinal()) {
+      sb.append("final ");
+    }
+
+    //sb.append(Types.getTypeName(type));
+    sb.append(getType());
+    sb.append(' ');
+    if (ci != null){ // maybe the fieldinfo isn't linked yet
+      sb.append(ci.getName());
+    }
+    sb.append('.');
+    sb.append(name);
+
+    return sb.toString();
+  }
+  
+  //--- those are the JPF internal attribute flags (not to mix with free user attrs)
+
+  void setAttributes (int a) {
+    attributes = a;
+  }
+
+  public void addAttribute (int a){
+    attributes |= a;
+  }
+
+  public int getAttributes () {
+    return attributes;
+  }
+
+  public boolean breakShared() {
+    return ((attributes & BREAK_SHARED) != 0);
+  }
+  
+  public boolean neverBreak() {
+    return ((attributes & NEVER_BREAK) != 0);    
+  }
+  
+  public int getStorageOffset () {
+    return storageOffset;
+  }
+
+  public String getFullName() {
+    return ci.getName() + '.' + name;
+  }
+
+  /**
+   * Creates a field for a given class, by cloning this FieldInfo
+   * and reseting the class that the field belongs to
+   */
+  public FieldInfo getInstanceFor(ClassInfo ci) {
+    FieldInfo clone;
+
+    try {
+      clone = (FieldInfo)clone();
+      clone.ci = ci;
+    } catch (CloneNotSupportedException cnsx){
+      cnsx.printStackTrace();
+      return null;
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/FieldLockInfo.java b/src/main/gov/nasa/jpf/vm/FieldLockInfo.java
new file mode 100644 (file)
index 0000000..4655a74
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+
+import java.util.logging.Logger;
+
+/**
+ * class encapsulating the lock protection status for field access
+ * instructions. Used by on-the-fly partial order reduction in FieldInstruction
+ * to determine if a GET/PUT_FIELD/STATIC insn has to be treated as a
+ * boundary step (terminates a transition). If the field access is always
+ * protected by a lock, only the corresponding sync (INVOKExx or MONITORENTER)
+ * are boundary steps, thus the number of states can be significantly reduced.
+ * 
+ * FieldLockInfos are only used if vm.por.sync_detection is set
+ * 
+ * NOTE this might involve assumptions that can be violated in subsequent
+ * paths, and might cause potential races to go undetected
+ */
+public abstract class FieldLockInfo implements Cloneable  {
+  
+  static Logger log = JPF.getLogger("gov.nasa.jpf.vm.FieldLockInfo");
+  
+  static protected final FieldLockInfo empty = new EmptyFieldLockInfo();
+    
+  protected ThreadInfo tiLastCheck; // the thread this FieldLockInfo was last checked for
+
+  public static FieldLockInfo getEmptyFieldLockInfo(){
+    return empty;
+  }
+  
+  public abstract FieldLockInfo checkProtection (ThreadInfo ti, ElementInfo ei, FieldInfo fi);
+  public abstract boolean isProtected ();
+  
+  public abstract FieldLockInfo cleanUp (Heap heap);
+  protected abstract int[] getCandidateLockSet();
+    
+  public boolean isFinal() {
+    return isProtected();
+  }
+  
+  public boolean needsPindown (ElementInfo ei) {
+    return false;
+  }
+  
+  
+  /*
+   * we need this for faster instantiation. Make sure it gets overridden in
+   * case there is a need for per-instance parameterization
+   */
+  @Override
+  public Object clone () throws CloneNotSupportedException {
+    return super.clone();
+  }
+
+  void lockAssumptionFailed (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    String src = ti.getTopFrameMethodInfo().getClassInfo().getSourceFileName();
+    int line = ti.getLine();
+
+    StringBuilder sb = new StringBuilder( "unprotected field access of: ");
+    sb.append(ei);
+    sb.append('.');
+    sb.append(fi.getName());
+    sb.append( " in thread: ");
+    sb.append( ti.getName());
+    sb.append( " (");
+    sb.append( src);
+    sb.append(':');
+    sb.append(line);
+    sb.append(")\n[SEVERE].. last lock candidates: ");
+    appendLockSet(sb, getCandidateLockSet());
+    if (tiLastCheck != null) {
+      sb.append(" set by ");
+      sb.append(tiLastCheck);
+    }
+    sb.append( "\n[SEVERE].. current locks: ");
+    appendLockSet(sb, ti.getLockedObjectReferences());
+    sb.append("\n[SEVERE].. if this is not a race, re-run with 'vm.shared.sync_detection=false' or exclude field from checks");
+
+    log.severe(sb.toString());
+  }
+
+  void appendLockSet (StringBuilder sb, int[] lockSet) {
+    Heap heap = VM.getVM().getHeap();
+
+    if ((lockSet == null) || (lockSet.length == 0)) {
+      sb.append( "{}");
+    } else {
+      sb.append('{');
+      for (int i=0; i<lockSet.length;) {
+        int ref = lockSet[i];
+        if (ref != MJIEnv.NULL) {
+          ElementInfo ei = heap.get(ref);
+          if (ei != null) {
+            sb.append(ei);
+          } else {
+            sb.append("?@");
+            sb.append(lockSet[i]);
+          }
+        }
+        i++;
+        if (i<lockSet.length) sb.append(',');
+      }
+      sb.append('}');
+    }
+  }
+
+}
+
+/**
+ * FieldLockSet implementation for fields that are terminally considered to be unprotected
+ */
+class EmptyFieldLockInfo extends FieldLockInfo {
+  
+  @Override
+  public FieldLockInfo checkProtection (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    return this;
+  }
+      
+  @Override
+  public FieldLockInfo cleanUp (Heap heap) {
+    return this;
+  }
+  
+  @Override
+  public boolean isProtected () {
+    return false;
+  }
+    
+  @Override
+  public boolean isFinal() {
+    return true;
+  }
+  
+  @Override
+  protected int[] getCandidateLockSet() {
+    return new int[0];
+  }
+  
+  @Override
+  public String toString() {
+    return "EmptyFieldLockInfo";
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/FieldLockInfoFactory.java b/src/main/gov/nasa/jpf/vm/FieldLockInfoFactory.java
new file mode 100644 (file)
index 0000000..be82158
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * factory interface for creating concrete FieldLockInfo objects, which
+ * are configurable class sets (that might encapsulate heuristics)
+ */
+public interface FieldLockInfoFactory {
+
+  FieldLockInfo createFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi);
+}
diff --git a/src/main/gov/nasa/jpf/vm/Fields.java b/src/main/gov/nasa/jpf/vm/Fields.java
new file mode 100644 (file)
index 0000000..722f5c3
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.ObjectList;
+
+
+/**
+ * Represents the variable, hash-collapsed pooled data associated with an object
+ * that is related to the object values (as opposed to synchronization ->Monitor).
+ * Contains the values of the fields, not their descriptors.  Descriptors are represented
+ * by gov.nasa.jpf.vm.FieldInfo objects, which are stored in the ClassInfo structure.
+ *
+ * @see gov.nasa.jpf.vm.FieldInfo
+ * @see gov.nasa.jpf.vm.Monitor
+ */
+public abstract class Fields implements Cloneable {
+
+  /**
+   * we use this to store arbitrary field attributes (like symbolic values),
+   * but only create this on demand
+   */
+  protected Object[] fieldAttrs;
+
+  /**
+   * attribute attached to the object as a whole
+   */
+  protected Object objectAttr;
+
+
+  protected Fields() {}
+
+  public boolean hasFieldAttr() {
+    return fieldAttrs != null;
+  }
+
+  public boolean hasFieldAttr (Class<?> attrType){    
+    Object[] fa = fieldAttrs;
+    if (fa != null){
+      for (int i=0; i<fa.length; i++){
+        Object a = fa[i];
+        if (a != null && ObjectList.containsType(a, attrType)){
+          return true;
+        }
+      }
+    }
+    return false;
+  }
+
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getFieldAttr(int fieldOrElementIndex){
+    if (fieldAttrs != null){
+      return fieldAttrs[fieldOrElementIndex];
+    }
+    return null;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setFieldAttr (int nFieldsOrElements, int fieldOrElementIndex, Object attr){
+    if (fieldAttrs == null){
+      if (attr == null){
+        return; // no need to waste an array object for storing null
+      }
+      fieldAttrs = new Object[nFieldsOrElements];
+    }
+    fieldAttrs[fieldOrElementIndex] = attr;
+  }
+  
+  public void addFieldAttr (int nFieldsOrElements, int fieldOrElementIndex, Object attr){
+    if (attr != null){
+      if (fieldAttrs == null) {
+        fieldAttrs = new Object[nFieldsOrElements];
+      }
+      fieldAttrs[fieldOrElementIndex] = ObjectList.add(fieldAttrs[fieldOrElementIndex], attr);
+    }
+  }
+  
+  public void removeFieldAttr (int fieldOrElementIndex, Object attr){
+    if (fieldAttrs != null){
+     fieldAttrs[fieldOrElementIndex] = ObjectList.remove(fieldAttrs[fieldOrElementIndex], attr);
+    }
+  }
+  
+  public void replaceFieldAttr (int fieldOrElementIndex, Object oldAttr, Object newAttr){
+    if (fieldAttrs != null){
+     fieldAttrs[fieldOrElementIndex] = ObjectList.replace(fieldAttrs[fieldOrElementIndex], oldAttr, newAttr);
+    }
+  }
+  
+  public boolean hasFieldAttr (int fieldOrElementIndex, Class<?> type){
+    if (fieldAttrs != null){
+     return ObjectList.containsType(fieldAttrs[fieldOrElementIndex], type);
+    }
+    return false;
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getFieldAttr (int fieldOrElementIndex, Class<T> type){
+    if (fieldAttrs != null){
+     return ObjectList.getFirst(fieldAttrs[fieldOrElementIndex], type);
+    }
+    return null;    
+  }
+  
+  public <T> T getNextFieldAttr (int fieldOrElementIndex, Class<T> type, Object prev){
+    if (fieldAttrs != null){
+     return ObjectList.getNext(fieldAttrs[fieldOrElementIndex], type, prev);
+    }
+    return null;    
+  }
+  
+  public ObjectList.Iterator fieldAttrIterator(int fieldOrElementIndex){
+    Object a = (fieldAttrs != null) ? fieldAttrs[fieldOrElementIndex] : null;
+    return ObjectList.iterator(a);
+  }
+  
+  public <T> ObjectList.TypedIterator<T> fieldAttrIterator(int fieldOrElementIndex, Class<T> type){
+    Object a = (fieldAttrs != null) ? fieldAttrs[fieldOrElementIndex] : null;
+    return ObjectList.typedIterator(a, type);
+  }
+  
+
+  //--- object attributes
+  public boolean hasObjectAttr () {
+    return (objectAttr != null);
+  }
+
+  public boolean hasObjectAttr (Class<?> attrType){
+    return ObjectList.containsType(objectAttr, attrType);
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getObjectAttr(){
+    return objectAttr;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setObjectAttr (Object attr){
+    objectAttr = attr;    
+  }
+
+  public void addObjectAttr (Object attr){
+    objectAttr = ObjectList.add(objectAttr, attr);
+  }
+
+  public void removeObjectAttr (Object attr){
+    objectAttr = ObjectList.remove(objectAttr, attr);
+  }
+
+  public void replaceObjectAttr (Object oldAttr, Object newAttr){
+    objectAttr = ObjectList.replace(objectAttr, oldAttr, newAttr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getObjectAttr (Class<T> attrType) {
+    return ObjectList.getFirst(objectAttr, attrType);
+  }
+
+  public <T> T getNextObjectAttr (Class<T> attrType, Object prev) {
+    return ObjectList.getNext(objectAttr, attrType, prev);
+  }
+
+  public ObjectList.Iterator objectAttrIterator(){
+    return ObjectList.iterator(objectAttr);
+  }
+  
+  public <T> ObjectList.TypedIterator<T> objectAttrIterator(Class<T> attrType){
+    return ObjectList.typedIterator(objectAttr, attrType);
+  }
+
+
+  public abstract int[] asFieldSlots();
+
+  /**
+   * give an approximation of the heap size in bytes - we assume fields are word
+   * aligned, hence the number of values*4 should be good. Note that this is
+   * overridden by ArrayFields (arrays would be packed)
+   */
+  public abstract int getHeapSize ();
+
+
+  public boolean isReferenceArray () {
+    return false;
+  }
+
+  // our low level getters and setters
+  public abstract int getIntValue (int index);
+
+  // same as getIntValue(), just here to make intentions clear
+  public abstract int getReferenceValue (int index);
+
+  public abstract long getLongValue (int index);
+
+  public abstract boolean getBooleanValue (int index);
+
+  public abstract byte getByteValue (int index);
+
+  public abstract char getCharValue (int index);
+
+  public abstract short getShortValue (int index);
+
+  public abstract float getFloatValue (int index);
+
+  public abstract double getDoubleValue (int index);
+
+  //--- the field modifier methods (both instance and static)
+
+  public abstract void setReferenceValue (int index, int newValue);
+
+  public abstract void setBooleanValue (int index, boolean newValue);
+
+  public abstract void setByteValue (int index, byte newValue);
+
+  public abstract void setCharValue (int index, char newValue);
+
+  public abstract void setShortValue (int index, short newValue);
+
+  public abstract void setFloatValue (int index, float newValue);
+
+  public abstract void setIntValue (int index, int newValue);
+
+  public abstract void setLongValue (int index, long newValue);
+
+  public abstract void setDoubleValue (int index, double newValue);
+
+  @Override
+  public abstract Fields clone ();
+
+  protected Fields cloneFields() {
+    try {
+      Fields f = (Fields)super.clone();
+
+      if (fieldAttrs != null) {
+        f.fieldAttrs = fieldAttrs.clone();
+      }
+
+      if (objectAttr != null) {
+        f.objectAttr = objectAttr; //
+      }
+
+      return f;
+    } catch (CloneNotSupportedException cnsx){
+      return null;
+    }
+  }
+
+  @Override
+  public abstract boolean equals(Object o);
+
+  // <2do> not multi-attr aware yet
+  protected boolean compareAttrs(Fields f) {
+    if (fieldAttrs != null || f.fieldAttrs != null) {
+      if (!Misc.equals(fieldAttrs, f.fieldAttrs)) {
+        return false;
+      }
+    }
+
+    if (!ObjectList.equals(objectAttr, f.objectAttr)){
+      return false;
+    }
+
+    return true;
+  }
+
+  // serialization interface
+  public abstract void appendTo(IntVector v);
+
+
+  @Override
+  public int hashCode () {
+    HashData hd = new HashData();
+    hash(hd);
+    hashAttrs(hd);
+    return hd.getValue();
+  }
+
+  public abstract void hash(HashData hd);
+
+  /**
+   * Adds some data to the computation of an hashcode.
+   */
+  public void hashAttrs (HashData hd) {
+
+    // it's debatable if we add the attributes to the state, but whatever it
+    // is, it should be kept consistent with the StackFrame.hash()
+    Object[] a = fieldAttrs;
+    if (a != null) {
+      for (int i=0, l=a.length; i < l; i++) {
+        ObjectList.hash(a[i], hd);
+      }
+    }
+
+    if (objectAttr != null){
+      ObjectList.hash(objectAttr, hd);
+    }
+  }
+
+
+  // <2do> not multi-attr aware yet
+  public void copyAttrs(Fields other) {
+    if (other.fieldAttrs != null){
+      if (fieldAttrs == null){
+        fieldAttrs = other.fieldAttrs.clone();
+      } else {
+        System.arraycopy(other.fieldAttrs, 0, fieldAttrs, 0, fieldAttrs.length);
+      }
+    }
+
+    objectAttr = other.objectAttr;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/FieldsFactory.java b/src/main/gov/nasa/jpf/vm/FieldsFactory.java
new file mode 100644 (file)
index 0000000..5a3e417
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * abstract factory interface for creating Fields, i.e. the JPF object model
+ */
+public interface FieldsFactory {
+
+  public Fields createInstanceFields (ClassInfo ci);
+  public Fields createStaticFields (ClassInfo ci);
+  public Fields createArrayFields (String type, ClassInfo ci, int nElements,
+                                   int typeSize, boolean isReferenceArray);
+}
diff --git a/src/main/gov/nasa/jpf/vm/FinalizerThreadInfo.java b/src/main/gov/nasa/jpf/vm/FinalizerThreadInfo.java
new file mode 100644 (file)
index 0000000..8623138
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ *  
+ * This class represents the finalizer thread in VM which is responsible for
+ * executing finalize() methods upon garbage collection of finalizable objects.
+ * By a finalizable object, we mean an object, the class of which overrides the
+ * Object.finalize() method. By default, we do not allow for processing finalizers, 
+ * to enforce that one needs to set the property "vm.process_finalizers" to true.
+ * 
+ * If "vm.process_finalizers" is set to true, during vm initialization we create
+ * a FinalizerThreadInfo object per process. ApplicationContext keeps a reference
+ * to FinalizerThreadInfo of the process. This thread is alive during the entire
+ * process execution. The finalizer thread is "always" waiting on an internal
+ * private lock. The JPF Thread object corresponding to the FinalizerThreadInfo 
+ * is encapsulated by the model class gov.nasa.jpf.FinalizerThread. The thread 
+ * encapsulated by FinalizerThread has a queue of objects called "finalizeQueue"
+ * which is kept at the SUT level. This queue is initially empty, and it is 
+ * populated during the sweep() phase of the garbage collection. During sweep(), 
+ * before removing unmark objects from the heap, any unmark finalizable object is 
+ * marked and added to finalizeQueue. 
+ * 
+ * In VM.forward(), after each garbage collection, VM checks if finalizeQueue of 
+ * the current process finalizer thread is not empty. If so, VM schedules the
+ * finalizer thread to execute next, i.e. finalizer thread stops waiting and its 
+ * state becomes runnable. To accomplish that, VM replaces the next choiceGenerator 
+ * with a new choice generator from which only finalizer thread can proceed.
+ *  
+ * After finalizer thread processes the finalize() methods of all objects in
+ * finalizeQueue, the queue becomes empty and the thread waits on its internal lock
+ * again. After the process terminates, we still keep the finalizer thread to be 
+ * processed after the last garbage collection involving the process. The runnable
+ * finalizer thread terminates itself when it has processed its finalizeQueue and 
+ * there is no other alive thread in the process.
+ */
+public class FinalizerThreadInfo extends ThreadInfo {
+  
+  static final String FINALIZER_NAME = "finalizer";
+  
+  ChoiceGenerator<?> replacedCG;
+  
+  protected FinalizerThreadInfo (VM vm, ApplicationContext appCtx, int id) {
+    super(vm, id, appCtx);
+    
+    ci = appCtx.getSystemClassLoader().getResolvedClassInfo("gov.nasa.jpf.FinalizerThread");
+    threadData.name = FINALIZER_NAME;
+    
+    tempFinalizeQueue = new ArrayList<ElementInfo>();
+  }
+  
+  protected void createFinalizerThreadObject (SystemClassLoaderInfo sysCl){
+    Heap heap = getHeap();
+
+    ElementInfo eiThread = heap.newObject( ci, this);
+    objRef = eiThread.getObjectRef();
+    
+    ElementInfo eiName = heap.newString(FINALIZER_NAME, this);
+    int nameRef = eiName.getObjectRef();
+    eiThread.setReferenceField("name", nameRef);
+    
+    // Since we create FinalizerThread upon VM initialization, they are assigned to the
+    // same group as the main thread
+    int grpRef = ThreadInfo.getCurrentThread().getThreadGroupRef();
+    eiThread.setReferenceField("group", grpRef);
+    
+    eiThread.setIntField("priority", Thread.MAX_PRIORITY-2);
+
+    ClassInfo ciPermit = sysCl.getResolvedClassInfo("java.lang.Thread$Permit");
+    ElementInfo eiPermit = heap.newObject( ciPermit, this);
+    eiPermit.setBooleanField("blockPark", true);
+    eiThread.setReferenceField("permit", eiPermit.getObjectRef());
+
+    addToThreadGroup(getElementInfo(grpRef));
+    
+    addId( objRef, id);
+    
+    threadData.name = FINALIZER_NAME;
+
+    // start the thread by pushing Thread.run()
+    startFinalizerThread();
+    
+    eiThread.setBooleanField("done", false);
+    ElementInfo finalizeQueue = getHeap().newArray("Ljava/lang/Object;", 0, this);
+    eiThread.setReferenceField("finalizeQueue", finalizeQueue.getObjectRef());
+    
+    // create an internal private lock used for lock-free wait
+    ElementInfo lock = getHeap().newObject(appCtx.getSystemClassLoader().objectClassInfo, this);
+    eiThread.setReferenceField("semaphore", lock.getObjectRef());
+    
+    // make it wait on the internal private lock until its finalizeQueue is populated. This way,
+    // we avoid scheduling points from including FinalizerThreads
+    waitOnSemaphore();
+
+    assert this.isWaiting();
+  }
+  
+  /**
+   * Pushes a frame corresponding to Thread.run() into the finalizer thread stack to
+   * start the thread.
+   */
+  protected void startFinalizerThread() {
+    MethodInfo mi = ci.getMethod("run()V", false);
+    DirectCallStackFrame frame = mi.createDirectCallStackFrame(this, 0);
+    frame.setReferenceArgument(0, objRef, frame);
+    pushFrame(frame);
+  }
+  
+  public boolean hasQueuedFinalizers() {
+    ElementInfo queue = getFinalizeQueue();
+    return (queue!=null && queue.asReferenceArray().length>0);
+  }
+  
+  public ElementInfo getFinalizeQueue() {
+    ElementInfo ei = vm.getModifiableElementInfo(objRef);
+    ElementInfo queue = null;
+    
+    if(ei!=null) {
+      int queueRef = ei.getReferenceField("finalizeQueue");
+      queue = vm.getModifiableElementInfo(queueRef);
+      return queue;
+    }
+    
+    return queue;
+  }
+  
+  // all finalizable objects collected during garbage collection are temporarily added to this list
+  // when VM schedule the finalizer thread, it add all elements to FinalizerThread.finalizeQueue at
+  // the SUT level.
+  List<ElementInfo> tempFinalizeQueue;
+  
+  /** 
+   * This method is invoked by the sweep() phase of the garbage collection process (GenericHeap.sweep()).
+   * It adds a given finalizable object to the finalizeQueue array of gov.nasa.jpf.FinalizerThread.
+   * 
+   * NOTE: this might return a new ElementInfo since we have to modify it (setting the finalized flag)
+   */
+  public ElementInfo getFinalizerQueuedInstance(ElementInfo ei) {
+    ei = ei.getModifiableInstance();
+    
+    // make sure we process this object finalizer only once
+    ei.setFinalized();
+    
+    tempFinalizeQueue.add(ei);
+    
+    return ei;
+  }
+  
+  /**
+   * This method adds all finalizable objects observed during the last garbage collection
+   * to finalizeQueue of FinalizerThread corresponding to this thread
+   */
+  void processNewFinalizables() {
+    if(!tempFinalizeQueue.isEmpty()) {
+      
+      ElementInfo oldQueue = getFinalizeQueue();
+      int[] oldValues = oldQueue.asReferenceArray();    
+      int len = oldValues.length;
+      
+      int n = tempFinalizeQueue.size();
+      
+      ElementInfo newQueue = getHeap().newArray("Ljava/lang/Object;", len+n, this);
+      int[] newValues = newQueue.asReferenceArray();
+      
+      System.arraycopy(oldValues, 0, newValues, 0, len);
+      
+      for(ElementInfo ei: tempFinalizeQueue) {
+        newValues[len++] = ei.getObjectRef();
+      }
+      
+      vm.getModifiableElementInfo(objRef).setReferenceField("finalizeQueue", newQueue.getObjectRef());
+      tempFinalizeQueue.clear();
+      
+      assert hasQueuedFinalizers();
+    }
+  }
+  
+  public boolean scheduleFinalizer() {
+    if(hasQueuedFinalizers() && !isRunnable()) {
+      SystemState ss = vm.getSystemState();
+      replacedCG = ss.getNextChoiceGenerator();
+      
+      // NOTE - before we get here we have already made sure that nextCg is not Cascaded. 
+      // we have to set nextCg to null before setting the nextCG, otherwise, the new CG is 
+      // mistakenly considered as "Cascaded"
+      ss.nextCg = null;
+      
+      // this doesn't have any choice (we need to run the finalizer), and since we don't have
+      // anything to re-execute (this is called from VM.forward()), we need to be in control of 
+      // type and registration of the CG, hence this doesn't go through the Scheduler/SyncPolicy
+      ss.setNextChoiceGenerator(new BreakGenerator("finalize", this, false));
+      checkNextChoiceGeneratorSet("no transition break prior to finalize");
+      
+      // stop waiting and process finalizers
+      notifyOnSemaphore();
+      assert this.isRunnable();
+      
+      return true;
+    } 
+    
+    return false;
+  }
+  
+  protected void waitOnSemaphore() {
+    int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore");
+    ElementInfo lock = vm.getModifiableElementInfo(lockRef);
+    
+    lock.wait(this, 0, false);
+  }
+  
+  protected void notifyOnSemaphore() {
+    int lockRef = vm.getElementInfo(objRef).getReferenceField("semaphore");
+    ElementInfo lock = vm.getModifiableElementInfo(lockRef);
+    
+    lock.notifies(vm.getSystemState(), this, false);
+  }
+  
+  // It returns true if the finalizer thread is waiting and do not have any object to
+  // process
+  protected boolean isIdle() {
+    if(this.isWaiting()) {
+      if(this.lockRef == vm.getElementInfo(objRef).getReferenceField("semaphore")) {
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  @Override
+  public boolean isSystemThread() {
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/FloatArrayFields.java b/src/main/gov/nasa/jpf/vm/FloatArrayFields.java
new file mode 100644 (file)
index 0000000..d02500d
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for float[] objects
+ */
+public class FloatArrayFields extends ArrayFields {
+
+  float[] values;
+
+  public FloatArrayFields (int length) {
+    values = new float[length];
+  }
+
+  @Override
+  public float[] asFloatArray() {
+    return values;
+  }
+  
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    FloatArrayFields a = (FloatArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 4;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendRawBits(values);
+  }
+
+  @Override
+  public FloatArrayFields clone(){
+    FloatArrayFields f = (FloatArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof FloatArrayFields) {
+      FloatArrayFields other = (FloatArrayFields)o;
+
+      float[] v = values;
+      float[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void setFloatValue (int pos, float newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public float getFloatValue (int pos) {
+    return values[pos];
+  }
+
+
+  @Override
+  public void hash(HashData hd) {
+    float[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/FloatChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/FloatChoiceGenerator.java
new file mode 100644 (file)
index 0000000..c52e7b7
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * Choice Generator that creates float value - this is only an interface
+ * we can use for type checks of implementors that have their own
+ * generic hierarchy
+ */
+public interface FloatChoiceGenerator extends ChoiceGenerator<Float> {
+}
diff --git a/src/main/gov/nasa/jpf/vm/FloatFieldInfo.java b/src/main/gov/nasa/jpf/vm/FloatFieldInfo.java
new file mode 100644 (file)
index 0000000..0247d66
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+
+/**
+ * type, name, modifier info of float fields
+ */
+public class FloatFieldInfo extends SingleSlotFieldInfo {
+  float init;
+
+  public FloatFieldInfo (String name, int modifiers) {
+    super(name, "F", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Float){
+      cv = constValue;
+      init = (Float)constValue;
+
+    } else {
+      throw new JPFException("illegal float ConstValue=" + constValue);
+    }
+  }
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setFloatValue(storageOffset, init);
+  }
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType() {
+    return FloatChoiceGenerator.class;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    float v = f.getFloatValue(storageOffset);
+    return Float.toString(v);
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    float v = f.getFloatValue(storageOffset);
+    return new Float(v);
+  }
+
+  @Override
+  public boolean isFloatField(){
+    return true;
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+
+  @Override
+  public boolean isFloatingPointField(){
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/FormalParameterAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/FormalParameterAnnotationInfo.java
new file mode 100644 (file)
index 0000000..52fa242
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for formal parameters
+ */
+public class FormalParameterAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int parameterIndex;
+  
+  public FormalParameterAnnotationInfo (AnnotationInfo base,  int targetType, short[] typePath, int parameterIndex)  {
+    super( base, targetType, typePath);
+    
+    this.parameterIndex = parameterIndex;
+  }
+  
+  public int getParameterIndex(){
+    return parameterIndex;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/FullStateSet.java b/src/main/gov/nasa/jpf/vm/FullStateSet.java
new file mode 100644 (file)
index 0000000..dc99df7
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.IntArray;
+import gov.nasa.jpf.util.IntTable;
+
+/**
+ * Implements a lossless StateSet
+ */
+public class FullStateSet extends SerializingStateSet {
+  IntTable<IntArray> states = new IntTable<IntArray>(16);
+
+  @Override
+  public int size () {
+    return states.size();
+  }
+
+  @Override
+  public int add (int[] state) {
+    return states.poolIndex(new IntArray(state));
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java b/src/main/gov/nasa/jpf/vm/FunctionObjectFactory.java
new file mode 100644 (file)
index 0000000..de61be0
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class FunctionObjectFactory {
+  
+  public int getFunctionObject(int bsIdx, ThreadInfo ti, ClassInfo fiClassInfo, String samUniqueName, BootstrapMethodInfo bmi, 
+                                         String[] freeVariableTypeNames, Object[] freeVariableValues) {
+    
+    ClassLoaderInfo cli = bmi.enclosingClass.getClassLoaderInfo();
+    
+    ClassInfo funcObjType = cli.getResolvedFuncObjType(bsIdx, fiClassInfo, samUniqueName, bmi, freeVariableTypeNames);
+    
+    funcObjType.registerClass(ti);
+
+    Heap heap = ti.getHeap();
+    ElementInfo ei = heap.newObject(funcObjType, ti);
+    
+    setFuncObjFields(ei, bmi, freeVariableTypeNames, freeVariableValues);
+    
+    return ei.getObjectRef();
+  }
+  
+  public void setFuncObjFields(ElementInfo funcObj, BootstrapMethodInfo bmi, String[] freeVarTypeNames, Object[] freeVarValues) {
+    Fields fields = funcObj.getFields();
+    
+    for(int i = 0; i<freeVarTypeNames.length; i++) {
+      String typeName = freeVarTypeNames[i];
+      if (typeName.equals("byte")) {
+        fields.setByteValue(i, (Byte)freeVarValues[i]);
+      } else if (typeName.equals("char")) {
+        fields.setCharValue(i, (Character)freeVarValues[i]);
+      } else if (typeName.equals("short")) {
+        fields.setShortValue(i, (Short)freeVarValues[i]);
+      } else if (typeName.equals("int")) {
+        fields.setIntValue(i, (Integer)freeVarValues[i]);
+      } else if (typeName.equals("float")) {
+        fields.setFloatValue(i, (Float)freeVarValues[i]);
+      } else if (typeName.equals("long")) {
+        fields.setLongValue(i, (Long)freeVarValues[i]);
+      } else if (typeName.equals("double")) {
+        fields.setDoubleValue(i, (Double)freeVarValues[i]);
+      } else if (typeName.equals("boolean")) {
+        fields.setBooleanValue(i, (Boolean)freeVarValues[i]);
+      } else {
+        int val = ((ElementInfo)freeVarValues[i]).getObjectRef();
+        fields.setReferenceValue(i, val);
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/GenericHeap.java b/src/main/gov/nasa/jpf/vm/GenericHeap.java
new file mode 100644 (file)
index 0000000..4a62f95
--- /dev/null
@@ -0,0 +1,758 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.ArrayObjectQueue;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.ObjectQueue;
+import gov.nasa.jpf.util.Processor;
+
+/**
+ * this is an abstract root for Heap implementations, providing a standard
+ * mark&sweep collector, change attribute management, and generic pinDownList,
+ * weakReference and internString handling
+ * 
+ * The concrete Heap implementors have to provide the ElementInfo collection
+ * and associated getters, allocators and iterators
+ */
+public abstract class GenericHeap implements Heap, Iterable<ElementInfo> {
+  
+  static abstract class GenericHeapMemento implements Memento<Heap> {
+    // those can be simply copied
+    int attributes;
+    IntVector pinDownList;
+    Map<Integer,IntTable<String>> internStringsMap;
+    
+    protected GenericHeapMemento (GenericHeap heap){
+      // these are copy-on-first-write, so we don't have to clone
+      pinDownList = heap.pinDownList;
+      internStringsMap = heap.internStringsMap;
+      attributes = heap.attributes & ATTR_STORE_MASK;
+      
+      heap.setStored();
+    }
+    
+    @Override
+    public Heap restore (Heap inSitu) {
+      GenericHeap heap = (GenericHeap) inSitu;
+      heap.pinDownList = pinDownList;
+      heap.internStringsMap = internStringsMap;
+      heap.attributes = attributes;
+      heap.liveBitValue = false; // always start with false after a restore
+      return inSitu;
+    }
+  }
+  
+  
+  protected class ElementInfoMarker implements Processor<ElementInfo>{
+    @Override
+       public void process (ElementInfo ei) {
+      ei.markRecursive( GenericHeap.this); // this might in turn call queueMark
+    }
+  }
+  
+  protected VM vm;
+
+  // list of pinned down references (this is only efficient for a small number of objects)
+  // this is copy-on-first-write
+  protected IntVector pinDownList;
+
+  // interned Strings
+  // this is copy-on-first-write, it is created on demand upon adding the first interned string,
+  // and it includes IntTable per process.
+  protected Map<Integer,IntTable<String>> internStringsMap;
+
+  // the usual drill - the lower 2 bytes are sticky, the upper two ones 
+  // hold change status and transient (transition local) flags
+  protected int attributes;
+
+  static final int ATTR_GC            = 0x0001;
+  static final int ATTR_OUT_OF_MEMORY = 0x0002;
+  static final int ATTR_RUN_FINALIZER = 0x0004;
+
+  static final int ATTR_ELEMENTS_CHANGED  = 0x10000;
+  static final int ATTR_PINDOWN_CHANGED   = 0x20000;
+  static final int ATTR_INTERN_CHANGED    = 0x40000;
+  static final int ATTR_ATTRIBUTE_CHANGED = 0x80000;
+
+  // masks and sets
+  static final int ATTR_STORE_MASK = 0x0000ffff;
+  static final int ATTR_ANY_CHANGED = (ATTR_ELEMENTS_CHANGED | ATTR_PINDOWN_CHANGED | ATTR_INTERN_CHANGED | ATTR_ATTRIBUTE_CHANGED);
+
+
+  //--- these objects are only used during gc
+
+  // used to keep track of marked WeakRefs that might have to be updated (no need to restore, only transient use during gc)
+  protected ArrayList<ElementInfo> weakRefs;
+
+  protected ObjectQueue<ElementInfo> markQueue = new ArrayObjectQueue<ElementInfo>();
+
+  // this is set to false upon backtrack/restore
+  protected boolean liveBitValue;
+  
+  protected ElementInfoMarker elementInfoMarker = new ElementInfoMarker();
+  
+  // the number of live objects
+  // <2do> currently only defined after gc
+  protected int nLiveObjects;
+  
+  //--- constructors
+
+  public GenericHeap (Config config, KernelState ks){
+    vm = VM.getVM();
+
+    pinDownList = new IntVector(256);
+    attributes |= ATTR_PINDOWN_CHANGED; // no need to clone on next add
+
+    if (config.getBoolean("vm.finalize", true)){
+      attributes |= ATTR_RUN_FINALIZER;
+    }
+
+    if (config.getBoolean("vm.sweep",true)){
+      attributes |= ATTR_GC;
+    }
+  }
+
+
+  protected DynamicElementInfo createElementInfo (int objref, ClassInfo ci, Fields f, Monitor m, ThreadInfo ti){
+    return new DynamicElementInfo( objref,ci,f,m,ti);
+  }
+  
+  //--- pinDown handling
+  protected void addToPinDownList (int objref){
+    if ((attributes & ATTR_PINDOWN_CHANGED) == 0) {
+      pinDownList = pinDownList.clone();
+      attributes |= ATTR_PINDOWN_CHANGED;
+    }
+    pinDownList.add(objref);
+  }
+  
+  protected void removeFromPinDownList (int objref){
+    if ((attributes & ATTR_PINDOWN_CHANGED) == 0) {
+      pinDownList = pinDownList.clone();
+      attributes |= ATTR_PINDOWN_CHANGED;
+    }
+    pinDownList.removeFirst(objref);    
+  }
+
+  @Override
+  public void registerPinDown(int objref){
+    ElementInfo ei = getModifiable(objref);
+    if (ei != null) {
+      if (ei.incPinDown()){
+        addToPinDownList(objref);
+      }
+    } else {
+      throw new JPFException("pinDown reference not a live object: " + objref);
+    }
+  }
+
+  @Override
+  public void releasePinDown(int objref){
+    ElementInfo ei = getModifiable(objref);
+    if (ei != null) {
+      if (ei.decPinDown()){
+        removeFromPinDownList(objref);
+      }
+    } else {
+      throw new JPFException("pinDown reference not a live object: " + objref);
+    }
+  }  
+  void markPinDownList (){
+    if (pinDownList != null){
+      int len = pinDownList.size();
+      for (int i=0; i<len; i++){
+        int objref = pinDownList.get(i);
+        queueMark(objref);
+      }
+    }
+  }
+  
+  //--- weak reference handling
+  
+  @Override
+  public void registerWeakReference (ElementInfo ei) {
+    if (weakRefs == null) {
+      weakRefs = new ArrayList<ElementInfo>();
+    }
+
+    weakRefs.add(ei);
+  }
+  
+  /**
+   * reset all weak references that now point to collected objects to 'null'
+   * NOTE: this implementation requires our own Reference/WeakReference implementation, to
+   * make sure the 'ref' field is the first one
+   */
+  protected void cleanupWeakRefs () {
+    if (weakRefs != null) {
+      for (ElementInfo ei : weakRefs) {
+        Fields f = ei.getFields();
+        int    ref = f.getIntValue(0); // watch out, the 0 only works with our own WeakReference impl
+        if (ref != MJIEnv.NULL) {
+          ElementInfo refEi = get(ref);
+          if ((refEi == null) || (refEi.isNull())) {
+            ei = ei.getModifiableInstance();
+            // we need to make sure the Fields are properly state managed
+            ei.setReferenceField(ei.getFieldInfo(0), MJIEnv.NULL);
+          }
+        }
+      }
+
+      weakRefs = null;
+    }
+  }
+  
+  // NOTE - this is where to assert if this index isn't occupied yet, since only concrete classes know
+  // if there can be collisions, and how elements are stored
+  
+  protected abstract AllocationContext getSUTAllocationContext (ClassInfo ci, ThreadInfo ti);
+  protected abstract AllocationContext getSystemAllocationContext (ClassInfo ci, ThreadInfo ti, int anchor);
+  
+  /**
+   * this is called for newXX(..) allocations that are SUT thread specific, i.e. in response to
+   * a explicit NEW or xNEWARRAY instruction that should take the allocating thread into account 
+   */
+  protected abstract int getNewElementInfoIndex (AllocationContext ctx);
+  
+  //--- allocators
+    
+  protected ElementInfo createObject (ClassInfo ci, ThreadInfo ti, int objref) {
+    // create the thing itself
+    Fields f = ci.createInstanceFields();
+    Monitor m = new Monitor();
+    ElementInfo ei = createElementInfo( objref, ci, f, m, ti);
+    
+    set(objref, ei);
+
+    attributes |= ATTR_ELEMENTS_CHANGED;
+
+    // and do the default (const) field initialization
+    ci.initializeInstanceData(ei, ti);
+
+    vm.notifyObjectCreated(ti, ei);
+    
+    // note that we don't return -1 if 'outOfMemory' (which is handled in
+    // the NEWxx bytecode) because our allocs are used from within the
+    // exception handling of the resulting OutOfMemoryError (and we would
+    // have to override it, since the VM should guarantee proper exceptions)
+    
+    return ei;    
+  }
+    
+  @Override
+  public ElementInfo newObject(ClassInfo ci, ThreadInfo ti) {
+    AllocationContext ctx = getSUTAllocationContext( ci, ti);
+    int index = getNewElementInfoIndex( ctx);
+    ElementInfo ei = createObject( ci, ti, index);
+
+    return ei;
+  }
+
+  @Override
+  public ElementInfo newSystemObject (ClassInfo ci, ThreadInfo ti, int anchor) {
+    AllocationContext ctx = getSystemAllocationContext( ci, ti, anchor);
+    int index = getNewElementInfoIndex( ctx);
+    ElementInfo ei = createObject( ci, ti, index);
+    return ei;
+  }
+  
+  protected ElementInfo createArray (String elementType, int nElements, ClassInfo ci, ThreadInfo ti, int objref) {
+
+    Fields f = ci.createArrayFields(ci.getName(), nElements, Types.getTypeSize(elementType), Types.isReference(elementType));
+    Monitor m = new Monitor();
+    DynamicElementInfo ei = createElementInfo( objref, ci, f, m, ti);
+
+    set(objref, ei);
+
+    attributes |= ATTR_ELEMENTS_CHANGED;
+
+    vm.notifyObjectCreated(ti, ei);
+
+    return ei;
+  }
+  
+  protected ClassInfo getArrayClassInfo (ThreadInfo ti, String elementType) {
+    String type = "[" + elementType;
+    SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
+    ClassInfo ciArray = sysCl.getResolvedClassInfo(type);
+
+    if (!ciArray.isInitialized()) {
+      // we do this explicitly here since there are no clinits for array classes
+      ciArray.registerClass(ti);
+      ciArray.setInitialized();
+    }
+
+    return ciArray;
+  }
+  
+  @Override
+  public ElementInfo newArray(String elementType, int nElements, ThreadInfo ti) {
+    // see newObject for OOM simulation
+    ClassInfo ci = getArrayClassInfo(ti, elementType);
+    AllocationContext ctx = getSUTAllocationContext( ci, ti);
+    
+    int index = getNewElementInfoIndex( ctx);
+    ElementInfo ei = createArray( elementType, nElements, ci, ti, index);
+
+    return ei;
+  }
+
+  @Override
+  public ElementInfo newSystemArray(String elementType, int nElements, ThreadInfo ti, int anchor) {
+    // see newObject for OOM simulation
+    ClassInfo ci = getArrayClassInfo(ti, elementType);
+    AllocationContext ctx = getSystemAllocationContext( ci, ti, anchor);
+    
+    int index = getNewElementInfoIndex( ctx);
+    ElementInfo ei = createArray( elementType, nElements, ci, ti, index);
+
+    return ei;
+  }
+
+  
+  
+  protected ElementInfo initializeStringObject( String str, int index, int vref) {
+    ElementInfo ei = getModifiable(index);
+    ei.setReferenceField("value", vref);
+
+    ElementInfo eVal = getModifiable(vref);
+    CharArrayFields cf = (CharArrayFields)eVal.getFields();
+    cf.setCharValues(str.toCharArray());
+    
+    return ei;
+  }
+  
+  protected ElementInfo newString (ClassInfo ciString, ClassInfo ciChars, String str, ThreadInfo ti, AllocationContext ctx) {
+    
+    //--- the string object itself
+    int sRef = getNewElementInfoIndex( ctx);
+    createObject( ciString, ti, sRef);
+    
+    //--- its char[] array
+    ctx = ctx.extend(ciChars, sRef);
+    int vRef = getNewElementInfoIndex( ctx);
+    createArray( "C", str.length(), ciChars, ti, vRef);
+    
+    ElementInfo ei = initializeStringObject(str, sRef, vRef);      
+    return ei;
+  }
+
+  @Override
+  public ElementInfo newString(String str, ThreadInfo ti){
+    if (str != null) {
+      SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
+      ClassInfo ciString = sysCl.getStringClassInfo();
+      ClassInfo ciChars = sysCl.getCharArrayClassInfo();
+      
+      AllocationContext ctx = getSUTAllocationContext( ciString, ti);
+      return newString( ciString, ciChars, str, ti, ctx);
+      
+    } else {
+      return null;
+    }
+  }
+  
+  @Override
+  public ElementInfo newSystemString (String str, ThreadInfo ti, int anchor) {
+    if (str != null) {
+      SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo();
+      ClassInfo ciString = sysCl.getStringClassInfo();
+      ClassInfo ciChars = sysCl.getCharArrayClassInfo();
+      
+      AllocationContext ctx = getSystemAllocationContext( ciString, ti, anchor);
+      return newString( ciString, ciChars, str, ti, ctx);
+      
+    } else {
+      return null;
+    }    
+  }
+
+  @Override
+  public ElementInfo newInternString (String str, ThreadInfo ti) {
+    if(internStringsMap==null) {
+      internStringsMap = vm.getInitialInternStringsMap();
+    }
+    
+    int prcId = ti.getApplicationContext().getId();
+    IntTable.Entry<String> e = internStringsMap.get(prcId).get(str);
+    
+    if (e == null){
+      if (str != null) {
+        ElementInfo ei = newString( str, ti);
+        int index = ei.getObjectRef();
+        
+        // new interned Strings are always pinned down
+        ei.incPinDown();
+        addToPinDownList(index);
+        addToInternStrings(str, index, prcId);
+
+        return ei;
+      
+      } else {
+        return null;
+      }
+
+    } else {
+      return get(e.val);
+    }
+  }
+
+  protected void addToInternStrings (String str, int objref, int prcId) {
+    if ((attributes & ATTR_INTERN_CHANGED) == 0){
+      // shallow copy all interned strings tables
+      internStringsMap = new HashMap<Integer,IntTable<String>>(internStringsMap);
+      
+      // only clone the interned strings table of the current process
+      internStringsMap.put(prcId, internStringsMap.get(prcId).clone());
+      
+      // just cloned, no need to clone on the next add
+      attributes |= ATTR_INTERN_CHANGED;
+    }
+    internStringsMap.get(prcId).add(str, objref);
+  }
+  
+  
+  @Override
+  public ElementInfo newSystemThrowable (ClassInfo ciThrowable, String details, int[] stackSnapshot, int causeRef,
+                                 ThreadInfo ti, int anchor) {
+    SystemClassLoaderInfo sysCl = ti.getSystemClassLoaderInfo(); 
+    ClassInfo ciString = sysCl.getStringClassInfo();
+    ClassInfo ciChars = sysCl.getCharArrayClassInfo();
+    
+    //--- the Throwable object itself
+    AllocationContext ctx = getSystemAllocationContext( ciThrowable, ti, anchor);
+    int xRef = getNewElementInfoIndex( ctx);
+    ElementInfo eiThrowable = createObject( ciThrowable, ti, xRef);
+    
+    //--- the detailMsg field
+    if (details != null) {
+      AllocationContext ctxString = ctx.extend( ciString, xRef);
+      ElementInfo eiMsg = newString( ciString, ciChars, details, ti, ctxString);
+      eiThrowable.setReferenceField("detailMessage", eiMsg.getObjectRef());
+    }
+
+    //--- the stack snapshot field
+    ClassInfo ciSnap = getArrayClassInfo(ti, "I");
+    AllocationContext ctxSnap = ctx.extend(ciSnap, xRef);
+    int snapRef = getNewElementInfoIndex( ctxSnap);
+    ElementInfo eiSnap = createArray( "I", stackSnapshot.length, ciSnap, ti, snapRef);
+    int[] snap = eiSnap.asIntArray();
+    System.arraycopy( stackSnapshot, 0, snap, 0, stackSnapshot.length);
+    eiThrowable.setReferenceField("snapshot", snapRef);
+
+    //--- the cause field
+    eiThrowable.setReferenceField("cause", (causeRef != MJIEnv.NULL)? causeRef : xRef);
+
+    return eiThrowable;
+  }
+
+  
+  //--- abstract accessors
+
+  /*
+   * these methods abstract away the container type used in GenericHeap subclasses
+   */
+  
+  /**
+   * internal setter used during allocation
+   * @param index
+   * @param ei
+   */  
+  protected abstract void set (int index, ElementInfo ei);
+
+  /**
+   * public getter to access but not change ElementInfos
+   */
+  @Override
+  public abstract ElementInfo get (int ref);
+  
+  
+  /**
+   * public getter to access modifiable ElementInfos;
+   */
+  @Override
+  public abstract ElementInfo getModifiable (int ref);
+  
+  
+  /**
+   * internal remover used by generic sweep
+   */
+  protected abstract void remove (int ref);
+
+  
+  //--- iterators
+  
+  /**
+   * return Iterator for all non-null ElementInfo entries
+   */
+  @Override
+  public abstract Iterator<ElementInfo> iterator();
+  
+  @Override
+  public abstract Iterable<ElementInfo> liveObjects();
+  
+  
+  //--- garbage collection
+  
+  public boolean isGcEnabled (){
+    return (attributes & ATTR_GC) != 0;
+  }
+
+  public void setGcEnabled (boolean doGC) {
+    if (doGC != isGcEnabled()) {
+      if (doGC) {
+        attributes |= ATTR_GC;
+      } else {
+        attributes &= ~ATTR_GC;
+      }
+      attributes |= ATTR_ATTRIBUTE_CHANGED;
+    }
+  }
+  
+  @Override
+  public void unmarkAll(){
+    for (ElementInfo ei : liveObjects()){
+      ei.setUnmarked();
+    }
+  }
+  
+  /**
+   * add a non-null, not yet marked reference to the markQueue
+   *  
+   * called from ElementInfo.markRecursive(). We don't want to expose the
+   * markQueue since a copying collector might not have it
+   */
+  @Override
+  public void queueMark (int objref){
+    if (objref == MJIEnv.NULL) {
+      return;
+    }
+
+    ElementInfo ei = get(objref);
+    if (!ei.isMarked()){ // only add objects once
+      ei.setMarked();
+      markQueue.add(ei);
+    }
+  }
+  
+  /**
+   * called during non-recursive phase1 marking of all objects reachable
+   * from static fields
+   * @aspects: gc
+   */
+  @Override
+  public void markStaticRoot (int objref) {
+    if (objref != MJIEnv.NULL) {
+      queueMark(objref);
+    }
+  }
+
+  /**
+   * called during non-recursive phase1 marking of all objects reachable
+   * from Thread roots
+   * @aspects: gc
+   */
+  @Override
+  public void markThreadRoot (int objref, int tid) {
+    if (objref != MJIEnv.NULL) {
+      queueMark(objref);
+    }
+  }
+  
+  /**
+   * this implementation uses a generic ElementInfo iterator, it can be replaced
+   * with a more efficient container specific version
+   */
+  protected void sweep () {
+    ThreadInfo ti = vm.getCurrentThread();
+    int tid = ti.getId();
+    boolean isThreadTermination = ti.isTerminated();
+    int n = 0;
+    
+    if(vm.finalizersEnabled()) {
+      markFinalizableObjects();
+    }
+    
+    // now go over all objects, purge the ones that are not live and reset attrs for rest
+    for (ElementInfo ei : this){
+      
+      if (ei.isMarked()){ // live object, prepare for next transition & gc cycle
+        ei.setUnmarked();
+        ei.setAlive(liveBitValue);
+        
+        ei.cleanUp(this, isThreadTermination, tid);
+        n++;
+        
+      } else {
+        ei.processReleaseActions();
+        
+        vm.notifyObjectReleased(ti, ei);
+        remove(ei.getObjectRef());
+      }
+    }
+    
+    nLiveObjects = n;
+  }
+  
+  protected void markFinalizableObjects () {
+    FinalizerThreadInfo tiFinalizer = vm.getFinalizerThread();
+    
+    if (tiFinalizer != null){
+      for (ElementInfo ei : this) {
+        if (!ei.isMarked() && ei.hasFinalizer() && !ei.isFinalized()) {
+          ei = tiFinalizer.getFinalizerQueuedInstance(ei);
+          ei.setMarked(); // make sure it's not collected before the finalizerQueue has been processed
+          ei.markRecursive(this);
+        }
+      }
+    }
+  }
+  
+  protected void mark () {
+    markQueue.clear();
+    
+    //--- mark everything in our root set
+    markPinDownList();
+    vm.getThreadList().markRoots(this);      // mark thread stacks
+    vm.getClassLoaderList().markRoots(this); // mark all static references
+
+    //--- trace all entries - this gets recursive
+    markQueue.process(elementInfoMarker);    
+  }
+  
+  @Override
+  public void gc() {
+    vm.notifyGCBegin();
+
+    weakRefs = null;
+    liveBitValue = !liveBitValue;
+
+    mark();
+    
+    // at this point all live objects are marked
+    sweep();
+
+    cleanupWeakRefs(); // for potential nullification
+
+    vm.processPostGcActions();
+    vm.notifyGCEnd();
+  }
+
+  /**
+   * clean up reference values that are stored outside of reference fields 
+   * called from KernelState to process live ElementInfos after GC has finished
+   * and only live objects remain in the heap.
+   * 
+   * <2do> full heap enumeration is BAD - check if this can be moved into the sweep loop
+   */
+  @Override
+  public void cleanUpDanglingReferences() {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    int tid = ti.getId();
+    boolean isThreadTermination = ti.isTerminated();
+    
+    for (ElementInfo e : this) {
+      if (e != null) {
+        e.cleanUp(this, isThreadTermination, tid);
+      }
+    }
+  }
+  
+  /**
+   * check if object is alive. This is here and not in ElementInfo
+   * because we might own the liveness bit. In fact, the generic
+   * implementation uses bit-toggle to avoid iteration over all live
+   * objects at the end of GC
+   */
+  @Override
+  public boolean isAlive (ElementInfo ei){
+    return (ei == null || ei.isMarkedOrAlive(liveBitValue));
+  }
+  
+  //--- state management
+  
+  // since we can't provide generic implementations, we force concrete subclasses to
+  // handle volatile information
+  
+  @Override
+  public abstract void resetVolatiles();
+
+  @Override
+  public abstract void restoreVolatiles();
+  
+  @Override
+  public boolean hasChanged() {
+    return (attributes & ATTR_ANY_CHANGED) != 0;
+  }
+  
+  @Override
+  public void markChanged(int objref) {
+    attributes |= ATTR_ELEMENTS_CHANGED;
+  }
+
+  public void setStored() {
+    attributes &= ~ATTR_ANY_CHANGED;
+  }
+  
+  @Override
+  public abstract Memento<Heap> getMemento(MementoFactory factory);
+
+  @Override
+  public abstract Memento<Heap> getMemento();
+
+  
+  //--- out of memory simulation
+  
+  @Override
+  public boolean isOutOfMemory() {
+    return (attributes & ATTR_OUT_OF_MEMORY) != 0;
+  }
+
+  @Override
+  public void setOutOfMemory(boolean isOutOfMemory) {
+    if (isOutOfMemory != isOutOfMemory()) {
+      if (isOutOfMemory) {
+        attributes |= ATTR_OUT_OF_MEMORY;
+      } else {
+        attributes &= ~ATTR_OUT_OF_MEMORY;
+      }
+      attributes |= ATTR_ATTRIBUTE_CHANGED;
+    }
+  }
+
+
+  
+  //--- debugging
+
+  @Override
+  public void checkConsistency(boolean isStateStore) {
+    for (ElementInfo ei : this){
+      ei.checkConsistency();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/GenericSGOIDHeap.java b/src/main/gov/nasa/jpf/vm/GenericSGOIDHeap.java
new file mode 100644 (file)
index 0000000..1ac0983
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.IntTable;
+
+/**
+ * abstract Heap trait that implements SGOIDs by means of a search global
+ * Allocation map and a state managed allocCount map
+ * 
+ * NOTE - a reference value of 0 represents null and therefore is not a valid SGOID
+ */
+public abstract class GenericSGOIDHeap extends GenericHeap {
+
+  static class GenericSGOIDHeapMemento extends GenericHeapMemento {
+    IntTable.Snapshot<AllocationContext> ctxSnap;
+    
+    GenericSGOIDHeapMemento (GenericSGOIDHeap heap) {
+      super(heap);
+      
+      ctxSnap = heap.allocCounts.getSnapshot();
+    }
+
+    @Override
+    public Heap restore(Heap inSitu) {
+      super.restore( inSitu);
+      
+      GenericSGOIDHeap heap = (GenericSGOIDHeap) inSitu;
+      heap.allocCounts.restore(ctxSnap);
+      
+      return heap;
+    }
+  }
+  
+  // these are search global
+  protected int nextSgoid;
+  protected IntTable<Allocation> sgoids;
+  
+  // this is state managed 
+  // NOTE - this has to be included in the mementos of concrete Heap implementations 
+  protected IntTable<AllocationContext> allocCounts;
+  
+  protected GenericSGOIDHeap (Config config, KernelState ks){
+    super(config, ks);
+    
+    // static inits
+    initAllocationContext(config);
+    sgoids = new IntTable<Allocation>();
+    nextSgoid = 0;
+    
+    allocCounts = new IntTable<AllocationContext>();
+  }
+  
+  
+  //--- to be overridden by subclasses that use different AllocationContext implementations
+  
+  protected void initAllocationContext(Config config) {
+    HashedAllocationContext.init(config);
+    //PreciseAllocationContext.init(config);
+  }
+  
+  // these are always called directly from the allocation primitive, i.e. the allocating site is at a fixed
+  // stack offset (callers caller)
+  @Override
+  protected AllocationContext getSUTAllocationContext (ClassInfo ci, ThreadInfo ti) {
+    return HashedAllocationContext.getSUTAllocationContext(ci, ti);
+    //return PreciseAllocationContext.getSUTAllocationContext(ci, ti);
+  }
+  @Override
+  protected AllocationContext getSystemAllocationContext (ClassInfo ci, ThreadInfo ti, int anchor) {
+    return HashedAllocationContext.getSystemAllocationContext(ci, ti, anchor);
+    //return PreciseAllocationContext.getSystemAllocationContext(ci, ti, anchor);
+  }
+  
+
+  @Override
+  protected int getNewElementInfoIndex (AllocationContext ctx) {
+    int idx;
+    int cnt;
+    
+    IntTable.Entry<AllocationContext> cntEntry = allocCounts.getInc(ctx);
+    cnt = cntEntry.val;
+    
+    Allocation alloc = new Allocation(ctx, cnt);
+    
+    IntTable.Entry<Allocation> sgoidEntry = sgoids.get(alloc);
+    if (sgoidEntry != null) { // we already had this one
+      idx = sgoidEntry.val;
+      
+    } else { // new entry
+      idx = ++nextSgoid;
+      sgoids.put(alloc, idx);
+    }
+    
+    // sanity check - we do this here (and not in our super class) since we know how elements are stored
+//    assert get(idx) == null;
+    
+    return idx;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/GenericSharednessPolicy.java b/src/main/gov/nasa/jpf/vm/GenericSharednessPolicy.java
new file mode 100644 (file)
index 0000000..0de85e8
--- /dev/null
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.SystemAttribute;
+import gov.nasa.jpf.util.FieldSpecMatcher;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.MethodSpecMatcher;
+import gov.nasa.jpf.util.TypeSpecMatcher;
+import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet;
+
+/**
+ * an abstract SharednessPolicy implementation that makes use of both
+ * shared field access CGs and exposure CGs.
+ * 
+ * This class is highly configurable, both in terms of using exposure CGs and filters.
+ * The *never_break filters should be used with care to avoid missing defects, especially
+ * the (transitive) method filters.
+ * NOTE - the default settings from jpf-core/jpf.properties include several
+ * java.util.concurrent* and java.lang.* fields/methods that can in fact contribute to
+ * concurrency defects, esp. in SUTs that explicitly use Thread/ThreadGroup objects, in
+ * which case they should be removed.
+ * 
+ * The *always_break field filter should only be used for white box SUT analysis if JPF
+ * fails to detect sharedness (e.g. because no exposure is used). This should only
+ * go into application property files
+ */
+public abstract class GenericSharednessPolicy implements SharednessPolicy, Attributor {
+  
+  //--- auxiliary types to configure filters
+  static class NeverBreakIn implements SystemAttribute {
+    static NeverBreakIn singleton = new NeverBreakIn();
+  } 
+  static class NeverBreakOn implements SystemAttribute {
+    static NeverBreakOn singleton = new NeverBreakOn();
+  } 
+  static class AlwaysBreakOn implements SystemAttribute {
+    static AlwaysBreakOn singleton = new AlwaysBreakOn();
+  } 
+  
+  protected static JPFLogger logger = JPF.getLogger("shared");
+  
+  
+  //--- options used for concurrent field access detection
+  
+  protected TypeSpecMatcher neverBreakOnTypes;
+  
+  protected TypeSpecMatcher alwaysBreakOnTypes;
+  
+  /**
+   * never break or expose if a matching method is on the stack
+   */
+  protected MethodSpecMatcher neverBreakInMethods;
+  
+  /**
+   * never break on matching fields 
+   */  
+  protected FieldSpecMatcher neverBreakOnFields;
+    
+  /**
+   * always break matching fields, no matter if object is already shared or not
+   */  
+  protected FieldSpecMatcher alwaysBreakOnFields;
+  
+
+  /**
+   * do we break on final field access 
+   */
+  protected boolean skipFinals;
+  protected boolean skipConstructedFinals;
+  protected boolean skipStaticFinals;
+  
+  /**
+   * do we break inside of constructors
+   * (note that 'this' references could leak from ctors, but
+   * this is rather unusual)
+   */
+  protected boolean skipInits;
+
+  /**
+   * do we add CGs for objects that could become shared, e.g. when storing
+   * a reference to a non-shared object in a shared object field.
+   * NOTE: this is a conservative measure since we don't know yet at the
+   * point of exposure if the object will ever be shared, which means it
+   * can cause state explosion.
+   */
+  protected boolean breakOnExposure;
+  
+  /**
+   * options to filter out lock protected field access, which is not
+   * supposed to cause shared CGs
+   * (this has no effect on exposure though)
+   */
+  protected boolean useSyncDetection;
+  protected int lockThreshold;  
+  
+  protected VM vm;
+  
+  
+  protected GenericSharednessPolicy (Config config){
+    neverBreakInMethods = MethodSpecMatcher.create( config.getStringArray("vm.shared.never_break_methods"));
+    
+    neverBreakOnTypes = TypeSpecMatcher.create(config.getStringArray("vm.shared.never_break_types"));
+    alwaysBreakOnTypes = TypeSpecMatcher.create(config.getStringArray("vm.shared.always_break_types"));
+    
+    neverBreakOnFields = FieldSpecMatcher.create( config.getStringArray("vm.shared.never_break_fields"));
+    alwaysBreakOnFields = FieldSpecMatcher.create( config.getStringArray("vm.shared.always_break_fields"));
+    
+    skipFinals = config.getBoolean("vm.shared.skip_finals", true);
+    skipConstructedFinals = config.getBoolean("vm.shared.skip_constructed_finals", false);
+    skipStaticFinals = config.getBoolean("vm.shared.skip_static_finals", true);
+    skipInits = config.getBoolean("vm.shared.skip_inits", true);
+    
+    breakOnExposure = config.getBoolean("vm.shared.break_on_exposure", true);
+    
+    useSyncDetection = config.getBoolean("vm.shared.sync_detection", true);
+    lockThreshold = config.getInt("vm.shared.lockthreshold", 5);  
+  }
+  
+  //--- internal methods (potentially overridden by subclass)
+  
+  
+  //--- attribute management
+
+  protected void setTypeAttributes (TypeSpecMatcher neverMatcher, TypeSpecMatcher alwaysMatcher, ClassInfo ciLoaded){
+    // we flatten this for performance reasons
+    for (ClassInfo ci = ciLoaded; ci!= null; ci = ci.getSuperClass()){
+      if (alwaysMatcher != null && alwaysMatcher.matches(ci)){
+        ciLoaded.addAttr(AlwaysBreakOn.singleton);
+        return;
+      }
+      if (neverMatcher != null && neverMatcher.matches(ci)){
+        ciLoaded.addAttr( NeverBreakOn.singleton);
+        return;
+      }
+    }
+  }
+  
+  protected void setFieldAttributes (FieldSpecMatcher neverMatcher, FieldSpecMatcher alwaysMatcher, ClassInfo ci){
+    for (FieldInfo fi : ci.getDeclaredInstanceFields()) {
+      // invisible fields (created by compiler)
+      if (fi.getName().startsWith("this$")) {
+        fi.addAttr( NeverBreakOn.singleton);
+        continue;
+      }        
+
+      // configuration
+      if (neverMatcher != null && neverMatcher.matches(fi)) {
+        fi.addAttr( NeverBreakOn.singleton);
+      }
+      if (alwaysMatcher != null && alwaysMatcher.matches(fi)) {
+        fi.addAttr( AlwaysBreakOn.singleton);
+      }
+      
+      // annotation
+      if (fi.hasAnnotation("gov.nasa.jpf.annotation.NeverBreak")){
+        fi.addAttr( NeverBreakOn.singleton);        
+      }
+    }
+
+    for (FieldInfo fi : ci.getDeclaredStaticFields()) {
+      // invisible fields (created by compiler)
+      if ("$assertionsDisabled".equals(fi.getName())) {
+        fi.addAttr( NeverBreakOn.singleton);
+        continue;
+      }
+
+      // configuration
+      if (neverMatcher != null && neverMatcher.matches(fi)) {
+        fi.addAttr( NeverBreakOn.singleton);
+      }
+      if (alwaysMatcher != null && alwaysMatcher.matches(fi)) {
+        fi.addAttr( AlwaysBreakOn.singleton);
+      }
+      
+      // annotation
+      if (fi.hasAnnotation("gov.nasa.jpf.annotation.NeverBreak")){
+        fi.addAttr( NeverBreakOn.singleton);        
+      }
+    }
+  }
+  
+  protected boolean isInNeverBreakMethod (ThreadInfo ti){
+    for (StackFrame frame = ti.getTopFrame(); frame != null; frame=frame.getPrevious()){
+      MethodInfo mi = frame.getMethodInfo();
+      if (mi.hasAttr( NeverBreakIn.class)){
+        return true;
+      }
+    }
+
+    return false;
+  }
+  
+  protected abstract boolean checkOtherRunnables (ThreadInfo ti);
+  
+  // this needs a three-way return value, hence Boolean
+  protected Boolean canHaveSharednessCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi){
+    //--- thread
+    if (ti.isFirstStepInsn()){ // no empty transitions
+      return Boolean.FALSE;
+    }
+    
+    if (!checkOtherRunnables(ti)){ // nothing to reschedule
+      return Boolean.FALSE;
+    }
+    
+    if (ti.hasAttr( NeverBreakIn.class)){
+      return Boolean.FALSE;
+    }
+    
+    //--- method
+    if (isInNeverBreakMethod(ti)){
+      return false;
+    }
+    
+    //--- type
+    ClassInfo ciFieldOwner = eiFieldOwner.getClassInfo();
+    if (ciFieldOwner.hasAttr(NeverBreakOn.class)){
+      return Boolean.FALSE;
+    }
+    if (ciFieldOwner.hasAttr(AlwaysBreakOn.class)){
+      return Boolean.TRUE;
+    }
+    
+    //--- field
+    if (fi != null){
+      if (fi.hasAttr(AlwaysBreakOn.class)) {
+        return Boolean.TRUE;
+      }
+      if (fi.hasAttr(NeverBreakOn.class)) {
+        return Boolean.FALSE;
+      }
+    }
+    
+    return null;    
+  }
+
+  //--- FieldLockInfo management
+  
+  /**
+   * static attribute filters that determine if the check..Access() and check..Exposure() methods should be called.
+   * This is only called once per instruction execution since it filters all cases that would set a CG.
+   * Filter conditions have to apply to both field access and object exposure.
+   */
+  protected abstract FieldLockInfo createFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi);
+
+  
+  /**
+   * generic version of FieldLockInfo update, which relies on FieldLockInfo implementation to determine
+   * if ElementInfo needs to be cloned
+   */  
+  protected ElementInfo updateFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    FieldLockInfo fli = ei.getFieldLockInfo(fi);
+    if (fli == null){
+      fli = createFieldLockInfo(ti, ei, fi);
+      ei = ei.getModifiableInstance();
+      ei.setFieldLockInfo(fi, fli);
+      
+    } else {
+      FieldLockInfo newFli = fli.checkProtection(ti, ei, fi);
+      if (newFli != fli) {
+        ei = ei.getModifiableInstance();
+        ei.setFieldLockInfo(fi,newFli);
+      }
+    }
+    
+    return ei;
+  }
+  
+  
+  //--- runnable computation & CG creation
+
+  // NOTE - we don't schedule threads outside this process since field access if process local
+  
+  protected ThreadInfo[] getRunnables (ApplicationContext appCtx){
+    return vm.getThreadList().getProcessTimeoutRunnables(appCtx);
+  }
+  
+  protected ChoiceGenerator<ThreadInfo> getRunnableCG (String id, ThreadInfo tiCurrent){
+    if (vm.getSystemState().isAtomic()){ // no CG if we are in a atomic section
+      return null;
+    }
+    
+    ThreadInfo[] choices = getRunnables(tiCurrent.getApplicationContext());
+    if (choices.length <= 1){ // field access doesn't block, i.e. the current thread is always runnable
+      return null;
+    }
+    
+    return new ThreadChoiceFromSet( id, choices, true);
+  }
+  
+  protected boolean setNextChoiceGenerator (ChoiceGenerator<ThreadInfo> cg){
+    if (cg != null){
+      return vm.getSystemState().setNextChoiceGenerator(cg); // listeners could still remove CGs
+    }
+    
+    return false;
+  }
+  
+  
+  //--- internal policy methods that can be overridden by subclasses
+  
+  protected ElementInfo updateSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    ThreadInfoSet tis = ei.getReferencingThreads();
+    ThreadInfoSet newTis = tis.add(ti);
+    
+    if (tis != newTis){
+      ei = ei.getModifiableInstance();
+      ei.setReferencingThreads(newTis);
+    }
+      
+    // we only change from non-shared to shared
+    if (newTis.isShared(ti, ei) && !ei.isShared() && !ei.isSharednessFrozen()) {
+      ei = ei.getModifiableInstance();
+      ei.setShared(ti, true);
+    }
+
+    if (ei.isShared() && fi != null){
+      ei = updateFieldLockInfo(ti,ei,fi);
+    }
+    
+    return ei;
+  }
+
+  protected boolean setsExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed){
+    if (breakOnExposure){
+      ClassInfo ciExposed = eiExposed.getClassInfo();
+      
+      //--- exposed type
+      if (ciExposed.hasAttr(NeverBreakOn.class)){
+        return false;
+      }      
+      if (ciExposed.hasAttr(AlwaysBreakOn.class)){
+        logger.info("type exposure CG setting field ", fi, " to ", eiExposed);
+        return setNextChoiceGenerator(getRunnableCG("EXPOSE", ti));
+      }        
+        
+      // we can't filter on immutability since the race subject could be a reference
+      // that is exposed through the exposed object
+      
+      if (isInNeverBreakMethod(ti)){
+        return false;
+      }
+      
+      if (eiFieldOwner.isExposedOrShared() && isFirstExposure(eiFieldOwner, eiExposed)){        
+        // don't check against the 'old' field value because this might get called after the field was already updated
+        // we should solely depend on different object sharedness
+        eiExposed = eiExposed.getExposedInstance(ti, eiFieldOwner);
+        logger.info("exposure CG setting field ", fi, " to ", eiExposed);
+        return setNextChoiceGenerator(getRunnableCG("EXPOSE", ti));
+      }
+    }
+
+    return false;
+  }
+
+  protected boolean isFirstExposure (ElementInfo eiFieldOwner, ElementInfo eiExposed){
+    if (!eiExposed.isImmutable()){
+      if (!eiExposed.isExposedOrShared()) {
+         return (eiFieldOwner.isExposedOrShared());
+      }
+    }
+        
+    return false;
+  }
+
+  
+  //------------------------------------------------ Attributor interface
+    
+  /**
+   * this can be used to initializeSharednessPolicy per-application mechanisms such as ClassInfo attribution
+   */
+  @Override
+  public void initializeSharednessPolicy (VM vm, ApplicationContext appCtx){
+    this.vm = vm;
+    
+    SystemClassLoaderInfo sysCl = appCtx.getSystemClassLoader();
+    sysCl.addAttributor(this);
+  }
+  
+  
+  @Override
+  public void setAttributes (ClassInfo ci){
+    setTypeAttributes( neverBreakOnTypes, alwaysBreakOnTypes, ci);
+    
+    setFieldAttributes( neverBreakOnFields, alwaysBreakOnFields, ci);
+    
+    // this one is more expensive to iterate over and should be avoided
+    if (neverBreakInMethods != null){
+      for (MethodInfo mi : ci.getDeclaredMethods().values()){
+        if (neverBreakInMethods.matches(mi)){
+          mi.setAttr( NeverBreakIn.singleton);
+        }
+      }
+    }
+    
+  }
+    
+  //------------------------------------------------ SharednessPolicy interface
+  
+  @Override
+  public ElementInfo updateObjectSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    return updateSharedness(ti, ei, fi);
+  }
+  @Override
+  public ElementInfo updateClassSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    return updateSharedness(ti, ei, fi);
+  }
+  @Override
+  public ElementInfo updateArraySharedness (ThreadInfo ti, ElementInfo ei, int idx){
+    // NOTE - we don't support per-element FieldLockInfos (yet)
+    return updateSharedness(ti, ei, null);
+  }
+
+  
+  /**
+   * check to determine if call site, object/class attributes and thread execution state
+   * could cause CGs. This is called before sharedness is updated, i.e. can be used to
+   * filter objects/classes that should not be sharedness tracked
+   */
+  @Override
+  public boolean canHaveSharedObjectCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi){
+    Boolean ret = canHaveSharednessCG( ti, insn, eiFieldOwner, fi);
+    if (ret != null){
+      return ret;
+    }
+    
+    if  (eiFieldOwner.isImmutable()){
+      return false;
+    }
+    
+    if (skipFinals && fi.isFinal()){
+      return false;
+    }
+        
+    //--- mixed (dynamic) attributes
+    if (skipConstructedFinals && fi.isFinal() && eiFieldOwner.isConstructed()){
+      return false;
+    }
+    
+    if (skipInits && insn.getMethodInfo().isInit()){
+      return false;
+    }
+    
+    return true;
+  }
+  
+  @Override
+  public boolean canHaveSharedClassCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi){
+    Boolean ret = canHaveSharednessCG( ti, insn, eiFieldOwner, fi);
+    if (ret != null){
+      return ret;
+    }
+
+    if  (eiFieldOwner.isImmutable()){
+      return false;
+    }
+    
+    if (skipStaticFinals && fi.isFinal()){
+      return false;
+    }
+
+    // call site. This could be transitive, in which case it has to be dynamic and can't be moved to isRelevant..()
+    MethodInfo mi = insn.getMethodInfo();
+    if (mi.isClinit() && (fi.getClassInfo() == mi.getClassInfo())) {
+      // clinits are all synchronized, so they are lock protected per se
+      return false;
+    }
+    
+    return true;
+  }
+  
+  @Override
+  public boolean canHaveSharedArrayCG (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int idx){
+    Boolean ret = canHaveSharednessCG( ti, insn, eiArray, null);
+    if (ret != null){
+      return ret;
+    }
+
+    // more array specific checks here
+    
+    return true;
+  }
+  
+  
+  /**
+   * <2do> explain why not transitive
+   * 
+   * these are the public interfaces towards FieldInstructions. Callers have to be aware this will 
+   * change the /referenced/ ElementInfo in case the respective object becomes exposed
+   */
+  @Override
+  public boolean setsSharedObjectCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi){
+    if (eiFieldOwner.getClassInfo().hasAttr(AlwaysBreakOn.class) ||
+            (eiFieldOwner.isShared() && !eiFieldOwner.isLockProtected(fi))) {
+      logger.info("CG accessing shared instance field ", fi);
+      return setNextChoiceGenerator( getRunnableCG("SHARED_OBJECT", ti));
+    }
+    
+    return false;
+  }
+
+  @Override
+  public boolean setsSharedClassCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi){
+    if (eiFieldOwner.getClassInfo().hasAttr(AlwaysBreakOn.class) ||
+            (eiFieldOwner.isShared() && !eiFieldOwner.isLockProtected(fi))) {
+      logger.info("CG accessing shared static field ", fi);
+      return setNextChoiceGenerator( getRunnableCG("SHARED_CLASS", ti));
+    }
+    
+    return false;
+  }
+  
+  @Override
+  public boolean setsSharedArrayCG (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int index){
+    if (eiArray.isShared()){
+      // <2do> we should check lock protection for the whole array here
+      logger.info("CG accessing shared array ", eiArray);
+      return setNextChoiceGenerator( getRunnableCG("SHARED_ARRAY", ti));
+    }
+    
+    return false;
+  }
+
+  
+  //--- internal policy methods that can be overridden by subclasses
+    
+  protected boolean isRelevantStaticFieldAccess (ThreadInfo ti, Instruction insn, ElementInfo ei, FieldInfo fi){
+    if (!ei.isShared()){
+      return false;
+    }
+    
+    if  (ei.isImmutable()){
+      return false;
+    }
+    
+    if (skipStaticFinals && fi.isFinal()){
+      return false;
+    }    
+    
+    if (!ti.hasOtherRunnables()){ // nothing to break for
+      return false;
+    }
+
+    // call site. This could be transitive, in which case it has to be dynamic and can't be moved to isRelevant..()
+    MethodInfo mi = insn.getMethodInfo();
+    if (mi.isClinit() && (fi.getClassInfo() == mi.getClassInfo())) {
+      // clinits are all synchronized, so they are lock protected per se
+      return false;
+    }
+    
+    return true;
+  }
+
+  
+  protected boolean isRelevantArrayAccess (ThreadInfo ti, Instruction insn, ElementInfo ei, int index){
+    // <2do> this is too simplistic, we should support filters for array objects
+    
+    if (!ti.hasOtherRunnables()){
+      return false;
+    }
+    
+    if (!ei.isShared()){
+      return false;
+    }
+    
+    if (ti.isFirstStepInsn()){ // we already did break
+      return false;
+    }
+
+    return true;
+  }
+  
+  //--- object exposure 
+
+  // <2do> explain why not transitive
+  
+  @Override
+  public boolean setsSharedObjectExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed){
+    return setsExposureCG(ti,insn,eiFieldOwner,fi,eiExposed);
+  }
+
+  @Override
+  public boolean setsSharedClassExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed){
+    return setsExposureCG(ti,insn,eiFieldOwner,fi,eiExposed);
+  }  
+
+  // since exposure is about the object being exposed (the element), there is no separate setsSharedArrayExposureCG
+  
+  
+  @Override
+  public void cleanupThreadTermination(ThreadInfo ti) {
+    // default action is to do nothing
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/GenericSignatureHolder.java b/src/main/gov/nasa/jpf/vm/GenericSignatureHolder.java
new file mode 100644 (file)
index 0000000..775ddc6
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * something that can have a GenericSignature classfile attribute
+ */
+public interface GenericSignatureHolder {
+
+  void setGenericSignature(String signature);
+
+  String getGenericSignature();
+}
diff --git a/src/main/gov/nasa/jpf/vm/GlobalSchedulingPoint.java b/src/main/gov/nasa/jpf/vm/GlobalSchedulingPoint.java
new file mode 100644 (file)
index 0000000..475ff9b
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.SystemAttribute;
+
+/**
+ * a SystemAttribute to mark global scheduling points.
+ * 
+ * While we can identify process context switches through the ThreadInfo/AooCtx
+ * of respective CG instances, we could not identify process global scheduling
+ * choices that include threads of the current process
+ */
+public class GlobalSchedulingPoint implements SystemAttribute {
+  
+  static final GlobalSchedulingPoint singleton = new GlobalSchedulingPoint();
+  
+  public static void setGlobal (ChoiceGenerator<?> cg){
+    cg.addAttr(singleton);
+  }
+  
+  public static boolean isGlobal (ChoiceGenerator<?> cg){
+    return (cg != null) && cg.hasAttr(GlobalSchedulingPoint.class);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/GlobalSharednessPolicy.java b/src/main/gov/nasa/jpf/vm/GlobalSharednessPolicy.java
new file mode 100644 (file)
index 0000000..1a77e8d
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.SparseObjVector;
+
+/**
+ * a SharedObjectPolicy that uses search global ThreadInfoSets and FieldLockInfos,
+ * i.e. we remember thread access of all previously executed paths.
+ * 
+ * Use this policy for bug finding modes that don't have to create replay-able traces.
+ * 
+ * Note that this policy requires search global object ids (SGOID), i.e. only works
+ * with Heap implementations providing SGOIDs
+ */
+
+public class GlobalSharednessPolicy extends GenericSharednessPolicy {
+  // our global caches
+  protected SparseObjVector<ThreadInfoSet> globalTisCache = new SparseObjVector<ThreadInfoSet>(1024);
+  protected SparseObjVector<FieldLockInfo> globalFliCache = new SparseObjVector<FieldLockInfo>(1024);
+  
+
+  public GlobalSharednessPolicy (Config config){
+    super(config);
+  }
+  
+  protected ThreadInfoSet getRegisteredThreadInfoSet (int key, ThreadInfo allocThread) {
+    ThreadInfoSet tis = globalTisCache.get(key);
+    if (tis == null) {
+      tis = new TidSet(allocThread);
+      globalTisCache.set(key, tis);
+    }
+    
+    return tis;    
+  }
+  
+  protected FieldLockInfo getRegisteredFieldLockInfo (int key, ThreadInfo ti){
+    FieldLockInfo fli = globalFliCache.get(key);
+    
+    if (fli == null){
+      int[] lockRefs = ti.getLockedObjectReferences();
+      if (lockRefs.length == 0) {
+        fli = FieldLockInfo.getEmptyFieldLockInfo();
+      } else if (lockRefs.length == 1){
+        fli = new SingleLockThresholdFli(ti, lockRefs[0], lockThreshold);
+      } else {
+        fli = new PersistentLockSetThresholdFli(ti, lockRefs, lockThreshold);
+      }
+      
+      globalFliCache.set(key, fli);
+    }
+    
+    return fli;
+  }
+  
+  @Override
+  protected boolean checkOtherRunnables (ThreadInfo ti){
+    // this is a search global policy we don't care if other threads are runnable or already terminated
+    return true;
+  }
+  
+  @Override
+  public void initializeObjectSharedness (ThreadInfo allocThread, DynamicElementInfo ei) {
+    ThreadInfoSet tis = getRegisteredThreadInfoSet(ei.getObjectRef(), allocThread);
+    ei.setReferencingThreads( tis);
+  }
+
+  @Override
+  public void initializeClassSharedness (ThreadInfo allocThread, StaticElementInfo ei) {
+    ThreadInfoSet tis;
+    int ref = ei.getClassObjectRef();
+    if (ref == MJIEnv.NULL) { // startup class, we don't have a class object yet
+      // note that we don't have to store this in our globalCache since we can never
+      // backtrack to a point where the startup classes were not initialized yet.
+      // <2do> is this true for MultiProcessVM ?
+      tis = new TidSet(allocThread);
+    } else {
+      tis = getRegisteredThreadInfoSet(ref, allocThread);
+    }
+    
+    ei.setReferencingThreads(tis);
+    ei.setExposed(); // static fields are per se exposed
+  }
+
+  @Override
+  protected FieldLockInfo createFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    int id;
+    
+    if (ei instanceof StaticElementInfo){
+      id = ((StaticElementInfo)ei).getClassObjectRef();
+      if (id == MJIEnv.NULL){
+        return null;
+      }
+    } else {
+      id = ei.getObjectRef();
+    }
+    
+    return getRegisteredFieldLockInfo( id, ti);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/HandlerContext.java b/src/main/gov/nasa/jpf/vm/HandlerContext.java
new file mode 100644 (file)
index 0000000..ff32c48
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * utility wrapper for exception handlers that /would/ handle
+ * a given exception type
+ * 
+ * <2do> This should be a class hierarchy to properly distinguish between
+ * ordinary catch handlers and UncaughtHandler objects, but so far
+ * this isn't worth it
+ */
+public class HandlerContext {
+  public enum UncaughtHandlerType { INSTANCE, GROUP, GLOBAL } 
+  
+  ThreadInfo ti;
+  ClassInfo ciException;
+  
+  StackFrame frame;
+  ExceptionHandler handler;
+  // - or -
+  int uncaughtHandlerRef;
+  UncaughtHandlerType uncaughtHandlerType;
+
+  HandlerContext (ThreadInfo ti, ClassInfo ciException, StackFrame frame, ExceptionHandler handler) {
+    this.ti = ti;
+    this.ciException = ciException;
+    this.frame = frame;
+    this.handler = handler;
+  }
+  
+  HandlerContext (ThreadInfo ti, ClassInfo ciException, UncaughtHandlerType uncaughtHandlerType, int uncaughtHandlerRef){
+    this.ti = ti;
+    this.ciException = ciException;
+    this.uncaughtHandlerType = uncaughtHandlerType;
+    this.uncaughtHandlerRef = uncaughtHandlerRef;
+  }
+
+  public ThreadInfo getThreadInfo(){
+    return ti;
+  }
+  
+  public StackFrame getFrame () {
+    return frame;
+  }
+
+  public ExceptionHandler getHandler () {
+    return handler;
+  }
+
+  public boolean isUncaughtHandler(){
+    return uncaughtHandlerType != null;
+  }
+  
+  public UncaughtHandlerType getUncaughtHandlerType(){
+    return uncaughtHandlerType;
+  }
+  
+  public int getUncaughtHandlerRef(){
+    return uncaughtHandlerRef;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/HashedAllocationContext.java b/src/main/gov/nasa/jpf/vm/HashedAllocationContext.java
new file mode 100644 (file)
index 0000000..5b24d03
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+// see mixinJPFStack() comments
+import sun.misc.SharedSecrets;
+import sun.misc.JavaLangAccess;
+
+import gov.nasa.jpf.Config;
+import static gov.nasa.jpf.util.OATHash.*;
+
+/**
+ * an AllocationContext that uses a hash value for comparison. This is
+ * lossy - heap implementations using this class have to check/handle
+ * collisions.
+ * 
+ * However, given that we have very good hash data (search global object
+ * references), the probability of collisions is low enough that heap
+ * implementations might simply report this as a problem requiring a
+ * non-lossy AllocationContext.
+ * 
+ * Ideally, we would like to hash the host VM thread context too (esp.
+ * for system allocations), but host VM stack traces are expensive, and it is
+ * arguable if would be too strict (e.g. when using a dedicated allocator
+ * method called from alternative branches of the caller) 
+ * 
+ * note - this is a HashMap key type which has to obey the hashCode/equals contract
+ */
+public class HashedAllocationContext implements AllocationContext {
+    
+  static final Throwable throwable = new Throwable(); // to avoid frequent allocations
+    
+  static int mixinSUTStack (int h, ThreadInfo ti) {
+    h = hashMixin( h, ti.getId());
+
+    // we don't want to mixin the stack slots (locals and operands) because this would
+    // cause state leaks (different hash) if there are changed slot values that do not
+    // relate to the allocation
+    
+    for (StackFrame frame = ti.getTopFrame(); frame != null; frame = frame.getPrevious() ) {
+      if (!(frame instanceof DirectCallStackFrame)) {
+        Instruction insn = frame.getPC();
+        
+        //h = hashMixin(h, insn.hashCode()); // this is the Instruction object system hash - not reproducible between runs
+        
+        h = hashMixin( h, insn.getMethodInfo().getGlobalId()); // the method
+        h = hashMixin( h, insn.getInstructionIndex()); // the position within the method code
+        h = hashMixin( h, insn.getByteCode()); // the instruction type
+      }
+    }
+    
+    return h;
+  }
+  
+  /*
+   * this is an optimization to cut down on host VM StackTrace acquisition, since we just need one
+   * element.
+   * 
+   * NOTE: this is more fragile than Throwable.getStackTrace() and String.equals() since it assumes
+   * availability of the sun.misc.JavaLangAccess SharedSecret and invariance of classname strings.
+   * 
+   * The robust version would be
+   *   ..
+   *   throwable.fillInStackTrace();
+   *   StackTraceElement[] ste = throwable.getStackTrace();
+   *   StackTraceElement e = ste[4];
+   *   if (e.getClassName().equals("gov.nasa.jpf.vm.MJIEnv") && e.getMethodName().startsWith("new")){ ..
+   */ 
+  
+   static final JavaLangAccess JLA = SharedSecrets.getJavaLangAccess();
+   static final String ENV_CLSNAME = MJIEnv.class.getName();
+  
+  // <2do> this method is problematic - we should not assume a fixed stack position
+  // but we can't just mixin the whole stack since this would cause different class object
+  // allocation contexts (registerClass can happen from lots of locations).
+  // At the other end of the spectrum, MJIEnv.newXX() is not differentiating enough since
+  // those are convenience methods used from a gazillion of places that might share
+   // the same SUT state
+  static int mixinJPFStack (int h) {
+    throwable.fillInStackTrace();
+    
+    // we know the callstack is at least 4 levels deep:
+    //   0: mixinJPFStack
+    //   1: getXAllocationContext
+    //   2: heap.getXAllocationContext
+    //   3: heap.newObject/newArray/newString
+    //   4: <allocating method>
+    //   ...
+
+    // note that it is not advisable to mixin more than the immediate newX() caller since
+    // this would create state leaks for allocations that are triggered by SUT threads and
+    // have different native paths (e.g. Class object creation caused by different SUT thread context)
+    
+    StackTraceElement e = JLA.getStackTraceElement(throwable, 4); // see note below regarding fixed call depth fragility
+    // <2do> this sucks - MJIEnv.newObject/newArray/newString are used from a gazillion of places that might not differ in SUT state
+    if (e.getClassName() == ENV_CLSNAME && e.getMethodName().startsWith("new")){
+      // there is not much use to loop, since we don't have a good end condition
+      e = JLA.getStackTraceElement(throwable, 5);
+    }
+          
+    // NOTE - this is fragile since it is implementation dependent and differs
+    // between JPF runs
+    // the names are interned string from the class object
+    // h = hashMixin( h, System.identityHashCode(e.getClassName()));
+    // h = hashMixin( h, System.identityHashCode(e.getMethodName()));
+
+    // this should be reproducible, but the string hash is bad
+    h = hashMixin(h, e.getClassName().hashCode());
+    h = hashMixin(h, e.getMethodName().hashCode());
+    h = hashMixin(h, e.getLineNumber());
+    
+    return h;
+  }
+  
+  /*
+   * !! NOTE: these always have to be at a fixed call distance of the respective Heap.newX() call:
+   * 
+   *  ConcreteHeap.newX()
+   *    ConcreteHeap.getXAllocationContext()
+   *      ConcreteAllocationContext.getXAllocationContext()
+   *      
+   * that means the allocation site is at stack depth 4. This is not nice, but there is no
+   * good heuristic we could use instead, other than assuming there is a newObject/newArray/newString
+   * call on the stack
+   */
+  
+  /**
+   * this one is for allocations that should depend on the SUT thread context (such as all
+   * explicit NEW executions)
+   */
+  public static AllocationContext getSUTAllocationContext (ClassInfo ci, ThreadInfo ti) {
+    int h = 0;
+    
+    //--- the type that gets allocated
+    h = hashMixin(h, ci.getUniqueId()); // ClassInfo instances can change upon backtrack
+    
+    //--- the SUT execution context (allocating ThreadInfo and its stack)
+    h = mixinSUTStack( h, ti);
+    
+    //--- the JPF execution context (from where in the JPF code the allocation happens)
+    h = mixinJPFStack( h);
+    
+    h = hashFinalize(h);
+    HashedAllocationContext ctx = new HashedAllocationContext(h);
+
+    return ctx;
+  }
+  
+  /**
+   * this one is for allocations that should NOT depend on the SUT thread context (such as
+   * automatic allocation of java.lang.Class objects by the VM)
+   * 
+   * @param anchor a value that can be used to provide a context that is heap graph specific (such as
+   * a classloader or class object reference)
+   */
+  public static AllocationContext getSystemAllocationContext (ClassInfo ci, ThreadInfo ti, int anchor) {
+    int h = 0;
+    
+    h = hashMixin(h, ci.getUniqueId()); // ClassInfo instances can change upon backtrack
+    
+    // in lieu of the SUT stack, add some magic salt and the anchor
+    h = hashMixin(h, 0x14040118);
+    h = hashMixin(h, anchor);
+    
+    //--- the JPF execution context (from where in the JPF code the allocation happens)
+    h = mixinJPFStack( h);
+    
+    h = hashFinalize(h);
+    HashedAllocationContext ctx = new HashedAllocationContext(h);
+
+    return ctx;
+  }
+
+  public static boolean init (Config conf) {
+    //pool = new SparseObjVector<HashedAllocationContext>();
+    return true;
+  }
+  
+  //--- instance data
+  
+  // rolled up hash value for all context components
+  protected final int id;
+
+  
+  //--- instance methods
+  
+  protected HashedAllocationContext (int id) {
+    this.id = id;
+  }
+  
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof HashedAllocationContext) {
+      HashedAllocationContext other = (HashedAllocationContext)o;
+      return id == other.id; 
+    }
+    
+    return false;
+  }
+  
+  /**
+   * @pre: must be the same for two objects that result in equals() returning true
+   */
+  @Override
+  public int hashCode() {
+    return id;
+  }
+  
+  // for automatic field init allocations
+  @Override
+  public AllocationContext extend (ClassInfo ci, int anchor) {
+    //int h = hash( id, anchor, ci.hashCode());
+    int h = hashMixin(id, anchor);
+    h = hashMixin(h, ci.getUniqueId());
+    h = hashFinalize(h);
+    
+    return new HashedAllocationContext(h);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/Heap.java b/src/main/gov/nasa/jpf/vm/Heap.java
new file mode 100644 (file)
index 0000000..a3aa958
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * this is our implementation independent model of the heap
+ */
+public interface Heap extends Iterable<ElementInfo> {
+
+  //--- this is the common heap client API
+
+  ElementInfo get (int objref);
+  ElementInfo getModifiable (int objref);
+
+  void gc();
+
+  boolean isOutOfMemory();
+
+  void setOutOfMemory(boolean isOutOfMemory);
+
+  //--- the allocator primitives
+  ElementInfo newArray (String elementType, int nElements, ThreadInfo ti);
+  ElementInfo newObject (ClassInfo ci, ThreadInfo ti);
+  
+  ElementInfo newSystemArray (String elementType, int nElements, ThreadInfo ti, int anchor);
+  ElementInfo newSystemObject (ClassInfo ci, ThreadInfo ti, int anchor);
+
+  //--- convenience allocators that avoid constructor calls
+  // (those are mostly used for their reference values since they already have initialized fields,
+  // but to keep it consistent we use ElementInfo return types)
+  ElementInfo newString (String str, ThreadInfo ti);
+  ElementInfo newSystemString (String str, ThreadInfo ti, int anchor);
+  
+  ElementInfo newInternString (String str, ThreadInfo ti);
+  
+  ElementInfo newSystemThrowable (ClassInfo ci, String details, int[] stackSnapshot, int causeRef,
+                          ThreadInfo ti, int anchor);
+  
+  Iterable<ElementInfo> liveObjects();
+
+  int size();
+
+  //--- system internal interface
+
+
+  //void updateReachability( boolean isSharedOwner, int oldRef, int newRef);
+
+  void markThreadRoot (int objref, int tid);
+
+  void markStaticRoot (int objRef);
+
+  // these update per-object counters - object will be gc'ed if it goes to zero
+  void registerPinDown (int objRef);
+  void releasePinDown (int objRef);
+
+  void unmarkAll();
+
+  void cleanUpDanglingReferences();
+
+  boolean isAlive (ElementInfo ei);
+
+  void registerWeakReference (ElementInfo ei);
+
+  // to be called from ElementInfo.markRecursive(), to avoid exposure of
+  // mark implementation
+  void queueMark (int objref);
+
+  boolean hasChanged();
+
+
+  // <2do> this will go away
+  void markChanged(int objref);
+
+  void resetVolatiles();
+
+  void restoreVolatiles();
+
+  void checkConsistency (boolean isStateStore);
+
+
+  Memento<Heap> getMemento(MementoFactory factory);
+  Memento<Heap> getMemento();
+}
diff --git a/src/main/gov/nasa/jpf/vm/IncrementalChangeTracker.java b/src/main/gov/nasa/jpf/vm/IncrementalChangeTracker.java
new file mode 100644 (file)
index 0000000..81552cb
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * This should be implemented by classes that read & reset "hasChanged"-type
+ * information in the KernelState.  Example: CollapsingSerializer
+ * 
+ * @author peterd
+ */
+public interface IncrementalChangeTracker extends KernelState.ChangeListener {
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/InfoObject.java b/src/main/gov/nasa/jpf/vm/InfoObject.java
new file mode 100644 (file)
index 0000000..ab29dca
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.ObjectList;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * common root for ClassInfo, MethodInfo, FieldInfo (and maybe more to follow)
+ * 
+ * so far, it's used to factorize the annotation support, but we can also
+ * move the attributes up here
+ * 
+ * Note this is used for both declaration- and type- annotations since there is
+ * a cross-over (type annotations of classes/interfaces are visible through the
+ * reflection API that is otherwise just reserved for declaration annotations)
+ * 
+ * 2do - there are 3 annotation positions that are valid for both type and declaration
+ * annotations: class/interface, field and formal method parameters. Of these,
+ * only class/interface type annotations can be queried at runtime, i.e. are
+ * treated similarly to declaration annotations. It is not clear if this is a
+ * Java 8 implementation artifact or intentional
+ * 
+ * Other than annotating classes/interfaces, type and declaration annotations
+ * are kept separate and only the latter ones can be queried from Java, hence
+ * we provide a query API that is only accessible from instructions,
+ * native peers and listeners. Type annotations are used
+ */
+public abstract class InfoObject implements Cloneable {
+
+  static AnnotationInfo[] NO_ANNOTATIONS = new AnnotationInfo[0];
+  static AbstractTypeAnnotationInfo[] NO_TYPE_ANNOTATIONS = new AbstractTypeAnnotationInfo[0];
+  
+  // the number of annotations per class/method/field is usually
+  // small enough so that simple arrays are more efficient than HashMaps
+  protected AnnotationInfo[] annotations = NO_ANNOTATIONS;
+
+  protected AbstractTypeAnnotationInfo[] typeAnnotations = NO_TYPE_ANNOTATIONS;
+  
+  /** 
+   * user defined attribute objects.
+   * Note - this is NOT automatically state restored upon backtracking,
+   * subclasses have to do this on their own if required
+   */
+  protected Object attr;
+  
+  
+  public void setAnnotations (AnnotationInfo[] annotations){
+    this.annotations = annotations;
+  }
+  
+  public void addAnnotations (AnnotationInfo[] annotations){
+    if (annotations == null){
+      this.annotations = annotations;
+    } else {
+      AnnotationInfo[] newAi = new AnnotationInfo[this.annotations.length + annotations.length];
+      System.arraycopy(this.annotations,0,newAi, 0, this.annotations.length);
+      System.arraycopy(annotations, 0, newAi, this.annotations.length, annotations.length);
+      this.annotations = newAi;
+    }
+  }
+  
+  public void addAnnotation (AnnotationInfo newAnnotation){
+    AnnotationInfo[] ai = annotations;
+    if (ai == null){
+      ai = new AnnotationInfo[1];
+      ai[0] = newAnnotation;
+
+    } else {
+      int len = annotations.length;
+      ai = new AnnotationInfo[len+1];
+      System.arraycopy(annotations, 0, ai, 0, len);
+      ai[len] = newAnnotation;
+    }
+
+    annotations = ai;
+  }
+
+  // to be overridden by ClassInfo because of superclass inhertited annotations
+  public boolean hasAnnotations(){
+    return (annotations != NO_ANNOTATIONS);
+  }
+  
+  // to be overridden by ClassInfo because of superclass inhertited annotations
+  public AnnotationInfo[] getAnnotations() {
+    return annotations;
+  }
+      
+  // to be overridden by ClassInfo because of superclass inhertited annotations
+  public AnnotationInfo getAnnotation (String name){    
+    AnnotationInfo[] ai = annotations;
+    if (ai != NO_ANNOTATIONS){
+      for (int i=0; i<ai.length; i++){
+        if (ai[i].getName().equals(name)){
+          return ai[i];
+        }
+      }
+    }
+    return null;
+  }
+  
+  public boolean hasAnnotation (String name){
+    return getAnnotation(name) != null;    
+  }
+  
+  public AnnotationInfo[] getDeclaredAnnotations(){
+    return annotations;
+  }
+
+  //--- type annotations
+  
+  public void setTypeAnnotations (AbstractTypeAnnotationInfo[] typeAnnotations){
+    this.typeAnnotations = typeAnnotations;
+  }
+
+  public void addTypeAnnotations (AbstractTypeAnnotationInfo[] tas){
+    if (typeAnnotations == NO_TYPE_ANNOTATIONS){
+      typeAnnotations = tas;
+      
+    } else {
+      int oldLen = typeAnnotations.length;
+      AbstractTypeAnnotationInfo[] newTA = new AbstractTypeAnnotationInfo[oldLen + tas.length];
+      System.arraycopy(typeAnnotations, 0, newTA, 0, oldLen);
+      System.arraycopy(tas, 0, newTA, oldLen, tas.length);
+      typeAnnotations = newTA;
+    }
+  }
+  
+  public void addTypeAnnotation (AbstractTypeAnnotationInfo newAnnotation){
+    AbstractTypeAnnotationInfo[] ai = typeAnnotations;
+    if (ai == null){
+      ai = new AbstractTypeAnnotationInfo[1];
+      ai[0] = newAnnotation;
+
+    } else {
+      int len = annotations.length;
+      ai = new AbstractTypeAnnotationInfo[len+1];
+      System.arraycopy(annotations, 0, ai, 0, len);
+      ai[len] = newAnnotation;
+    }
+
+    typeAnnotations = ai;
+  }
+
+  
+  public AbstractTypeAnnotationInfo[] getTypeAnnotations() {
+    return typeAnnotations;
+  }
+  
+  public boolean hasTypeAnnotations(){
+    return (typeAnnotations != NO_TYPE_ANNOTATIONS);    
+  }
+  
+  public boolean hasTypeAnnotation (String name){
+    return getTypeAnnotation(name) != null;    
+  }
+  
+  public AbstractTypeAnnotationInfo getTypeAnnotation (String annoClsName){    
+    AbstractTypeAnnotationInfo[] ai = typeAnnotations;
+    if (ai != NO_TYPE_ANNOTATIONS){
+      for (int i=0; i<ai.length; i++){
+        if (ai[i].getName().equals(annoClsName)){
+          return ai[i];
+        }
+      }
+    }
+    return null;
+  }
+
+  public <T extends AbstractTypeAnnotationInfo> List<T> getTargetTypeAnnotations (Class<T> targetType){
+    List<T> list = null;
+    
+    AbstractTypeAnnotationInfo[] ais = typeAnnotations;
+    if (ais != NO_TYPE_ANNOTATIONS){
+      for (AbstractTypeAnnotationInfo ai : ais){
+        if (targetType.isAssignableFrom(ai.getClass())){
+          if (list == null){
+            list = new ArrayList();
+          }
+          list.add((T)ai);
+        }
+      }
+    }
+    
+    if (list != null){
+      return list;
+    } else {
+      return Collections.emptyList();
+    }
+  }
+  
+  //--- the generic attribute API
+
+  public boolean hasAttr () {
+    return (attr != null);
+  }
+
+  public boolean hasAttr (Class<?> attrType){
+    return ObjectList.containsType(attr, attrType);
+  }
+
+  public boolean hasAttrValue (Object a){
+    return ObjectList.contains(attr, a);
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  public Object getAttr(){
+    return attr;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setAttr (Object a){
+    attr = a;    
+  }
+
+  public void addAttr (Object a){
+    attr = ObjectList.add(attr, a);
+  }
+
+  public void removeAttr (Object a){
+    attr = ObjectList.remove(attr, a);
+  }
+
+  public void replaceAttr (Object oldAttr, Object newAttr){
+    attr = ObjectList.replace(attr, oldAttr, newAttr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getAttr (Class<T> attrType) {
+    return ObjectList.getFirst(attr, attrType);
+  }
+
+  public <T> T getNextAttr (Class<T> attrType, Object prev) {
+    return ObjectList.getNext(attr, attrType, prev);
+  }
+
+  public ObjectList.Iterator attrIterator(){
+    return ObjectList.iterator(attr);
+  }
+  
+  public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType){
+    return ObjectList.typedIterator(attr, attrType);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/Instruction.java b/src/main/gov/nasa/jpf/vm/Instruction.java
new file mode 100644 (file)
index 0000000..0c1a2c3
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.ObjectList;
+import gov.nasa.jpf.util.Source;
+import gov.nasa.jpf.vm.bytecode.InstructionInterface;
+
+
+
+/**
+ * common root of all JPF bytecode instruction classes 
+ * 
+ */
+public abstract class Instruction implements Cloneable, InstructionInterface {
+
+  protected int insnIndex;        // code[] index of instruction
+  protected int position;     // accumulated bytecode position (prev pos + prev bc-length)
+  protected MethodInfo mi;    // the method this insn belongs to
+
+  // property/mode specific attributes
+  protected Object attr;
+  
+  // this is for changing from InstructionInterface types to Instruction types
+  @Override
+  public Instruction asInstruction(){
+    return this;
+  }
+  
+  // to allow a classname and methodname context for each instruction
+  public void setContext(String className, String methodName, int lineNumber,
+          int offset) {
+  }
+
+  /**
+   * is this the first instruction in a method
+   */
+  @Override
+  public boolean isFirstInstruction() {
+    return (insnIndex == 0);
+  }
+
+
+  /**
+   * answer if this is a potential loop closing jump
+   */
+  @Override
+  public boolean isBackJump() {
+    return false;
+  }
+
+  /**
+   * is this instruction part of a monitorenter code pattern 
+   */
+  public boolean isMonitorEnterPrologue(){
+    return false;
+  }
+
+  /**
+   * is this one of our own, artificial insns?
+   */
+  @Override
+  public boolean isExtendedInstruction() {
+    return false;
+  }
+
+
+  @Override
+  public MethodInfo getMethodInfo() {
+    return mi;
+  }
+
+
+  /**
+   * that's used for explicit construction of MethodInfos (synthetic methods)
+   */
+  public void setMethodInfo(MethodInfo mi) {
+    this.mi = mi;
+  }
+
+  /**
+   * this returns the instruction at the following code insnIndex within the same
+   * method, which might or might not be the next one to enter (branches, overlay calls etc.).
+   */
+  @Override
+  public Instruction getNext() {
+    return mi.getInstruction(insnIndex + 1);
+  }
+
+  @Override
+  public int getInstructionIndex() {
+    return insnIndex;
+  }
+
+  @Override
+  public int getPosition() {
+    return position;
+  }
+
+  public void setLocation(int insnIdx, int pos) {
+    insnIndex = insnIdx;
+    position = pos;
+  }
+
+  /**
+   * return the length in bytes of this instruction.
+   * override if this is not 1
+   */
+  @Override
+  public int getLength() {
+    return 1;
+  }
+
+  @Override
+  public Instruction getPrev() {
+    if (insnIndex > 0) {
+      return mi.getInstruction(insnIndex - 1);
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * this is for listeners that process instructionExecuted(), but need to
+   * determine if there was a CG registration, an overlayed direct call
+   * (like clinit) etc.
+   * The easy case is the instruction not having been executed yet, in
+   * which case ti.getNextPC() == null
+   * There are two cases for re-execution: either nextPC was set to the
+   * same insn (which is what CG creators usually use), or somebody just
+   * pushed another stackframe that executes something which will return to the
+   * same insn (that is what automatic <clinit> calls and the like do - we call
+   * it overlays)
+   */
+  @Override
+  public boolean isCompleted(ThreadInfo ti) {
+    Instruction nextPc = ti.getNextPC();
+
+    if (nextPc == null) {
+      return ti.isTerminated();
+
+    } else {
+
+      return (nextPc != this) && (ti.getStackFrameExecuting(this, 1) == null);
+    }
+
+    // <2do> how do we account for exceptions? 
+  }
+
+  /**
+   * this method can be overridden if instruction classes have to store
+   * information for instructionExecuted() notifications, and this information
+   * should not be stored persistent to avoid memory leaks (e.g. via traces).
+   * Called by ThreadInfo.executeInstruction
+   */
+  public void cleanupTransients(){
+    // nothing here
+  }
+  
+  public boolean isSchedulingRelevant(SystemState ss, KernelState ks, ThreadInfo ti) {
+    return false;
+  }
+
+  /**
+   * this is the real workhorse
+   * returns next instruction to enter in this thread
+   * 
+   * <2do> it's unfortunate we roll every side effect into this method, because
+   * it diminishes the value of the 'executeInstruction' notification: all
+   * insns that require some sort of late binding (InvokeVirtual, GetField, ..)
+   * are not yet fully analyzable (e.g. the callee of InvokeVirtuals is not
+   * known yet), putting the burden of duplicating the related code of
+   * enter() in the listener. It would be better if we factor this
+   * 'prepareExecution' out of enter()
+   */
+  @Override
+  public abstract Instruction execute(ThreadInfo ti);
+
+  @Override
+  public String toString() {
+    return getMnemonic();
+  }
+
+  /**
+   * this can contain additional info that was gathered/cached during execution 
+   */
+  @Override
+  public String toPostExecString(){
+    return toString();
+  }
+  
+  @Override
+  public String getMnemonic() {
+    String s = getClass().getSimpleName();
+    return s.toLowerCase();
+  }
+
+  @Override
+  public int getLineNumber() {
+    return mi.getLineNumber(this);
+  }
+
+  @Override
+  public String getSourceLine() {
+    ClassInfo ci = mi.getClassInfo();
+    if (ci != null) {
+      int line = mi.getLineNumber(this);
+      String fileName = ci.getSourceFileName();
+
+      Source src = Source.getSource(fileName);
+      if (src != null) {
+        String srcLine = src.getLine(line);
+        if (srcLine != null) {
+          return srcLine;
+        }
+      }
+    }
+    
+    return null;
+  }
+
+  /**
+   * this is for debugging/logging if we always want something back telling
+   * us where this insn came from
+   */
+  public String getSourceOrLocation(){
+    ClassInfo ci = mi.getClassInfo();
+    if (ci != null) {
+      int line = mi.getLineNumber(this);
+      String file = ci.getSourceFileName();
+
+      Source src = Source.getSource(file);
+      if (src != null) {
+        String srcLine = src.getLine(line);
+        if (srcLine != null) {
+          return srcLine;
+        }
+      }
+
+      return "(" + file + ':' + line + ')'; // fallback
+
+    } else {
+      return "[synthetic] " + mi.getName();
+    }
+  }
+  
+  
+  /**
+   * this returns a "pathname:line" string
+   */
+  @Override
+  public String getFileLocation() {
+    ClassInfo ci = mi.getClassInfo();
+    if (ci != null) {
+      int line = mi.getLineNumber(this);
+      String fname = ci.getSourceFileName();
+      return (fname + ':' + line);
+    } else {
+      return "[synthetic] " + mi.getName();
+    }
+  }
+
+  /**
+   * this returns a "filename:line" string
+   */
+  @Override
+  public String getFilePos() {
+    String file = null;
+    int line = -1;
+    ClassInfo ci = mi.getClassInfo();
+
+    if (ci != null){
+      line = mi.getLineNumber(this);
+      file = ci.getSourceFileName();
+      if (file != null){
+        int i = file.lastIndexOf('/'); // ClassInfo.sourceFileName is using '/'
+        if (i >= 0) {
+          file = file.substring(i + 1);
+        }
+      }
+    }
+
+    if (file != null) {
+      if (line != -1){
+        return (file + ':' + line);
+      } else {
+        return file;
+      }
+    } else {
+      return ("pc " + position);
+    }
+  }
+
+  /**
+   * this returns a "class.method(line)" string
+   */
+  @Override
+  public String getSourceLocation() {
+    ClassInfo ci = mi.getClassInfo();
+
+    if (ci != null) {
+      String s = ci.getName() + '.' + mi.getName() +
+              '(' + getFilePos() + ')';
+      return s;
+
+    } else {
+      return null;
+    }
+  }
+
+  public void init(MethodInfo mi, int offset, int position) {
+    this.mi = mi;
+    this.insnIndex = offset;
+    this.position = position;
+  }
+
+  /**
+   * this is a misnomer - we actually push the clinit calls here in case
+   * we need some. 'causedClinitCalls' might be more appropriate, but it is
+   * used in a number of external projects
+   */
+  public boolean requiresClinitExecution(ThreadInfo ti, ClassInfo ci) {
+    return ci.initializeClass(ti);
+  }
+
+  /**
+   * this is returning the next Instruction to enter, to be called to obtain
+   * the return value of enter() if this is not a branch insn
+   *
+   * Be aware of that we might have had exceptions caused by our execution
+   * (-> lower frame), or we might have had overlaid calls (-> higher frame),
+   * i.e. we can't simply assume it's the following insn. We have to
+   * acquire this through the top frame of the ThreadInfo.
+   *
+   * note: the System.exit() problem should be gone, now that it is implemented
+   * as ThreadInfo state (TERMINATED), rather than purged stacks
+   */
+  @Override
+  public Instruction getNext (ThreadInfo ti) {
+    return ti.getPC().getNext();
+  }
+
+  
+  //--- the generic attribute API
+
+  @Override
+  public boolean hasAttr () {
+    return (attr != null);
+  }
+
+  @Override
+  public boolean hasAttr (Class<?> attrType){
+    return ObjectList.containsType(attr, attrType);
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */
+  @Override
+  public Object getAttr(){
+    return attr;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  @Override
+  public void setAttr (Object a){
+    attr = ObjectList.set(attr, a);    
+  }
+
+  @Override
+  public void addAttr (Object a){
+    attr = ObjectList.add(attr, a);
+  }
+
+  @Override
+  public void removeAttr (Object a){
+    attr = ObjectList.remove(attr, a);
+  }
+
+  @Override
+  public void replaceAttr (Object oldAttr, Object newAttr){
+    attr = ObjectList.replace(attr, oldAttr, newAttr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  @Override
+  public <T> T getAttr (Class<T> attrType) {
+    return ObjectList.getFirst(attr, attrType);
+  }
+
+  @Override
+  public <T> T getNextAttr (Class<T> attrType, Object prev) {
+    return ObjectList.getNext(attr, attrType, prev);
+  }
+
+  @Override
+  public ObjectList.Iterator attrIterator(){
+    return ObjectList.iterator(attr);
+  }
+  
+  @Override
+  public <T> ObjectList.TypedIterator<T> attrIterator(Class<T> attrType){
+    return ObjectList.typedIterator(attr, attrType);
+  }
+
+  // -- end attrs --
+
+  /**
+   * this is overridden by any Instruction that use a cache for class or 
+   * method to provide a type safe cloning
+   */
+  public Instruction typeSafeClone(MethodInfo mi) {
+    Instruction clone = null;
+
+    try {
+      clone = (Instruction) super.clone();
+
+      // reset the method that this insn belongs to
+      clone.mi = mi;
+    } catch (CloneNotSupportedException e) {
+      e.printStackTrace();
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/IntArrayFields.java b/src/main/gov/nasa/jpf/vm/IntArrayFields.java
new file mode 100644 (file)
index 0000000..ccb219b
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for int[] objects
+ */
+public class IntArrayFields extends ArrayFields {
+
+  int[] values;
+
+  public IntArrayFields (int length) {
+    values = new int[length];
+  }
+
+  @Override
+  public int[] asIntArray() {
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    IntArrayFields a = (IntArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+  
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 4;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.append(values);
+  }
+
+  @Override
+  public IntArrayFields clone(){
+    IntArrayFields f = (IntArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof IntArrayFields) {
+      IntArrayFields other = (IntArrayFields)o;
+
+      int[] v = values;
+      int[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void setIntValue (int pos, int newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public int getIntValue (int pos) {
+    return values[pos];
+  }
+
+
+  @Override
+  public void hash(HashData hd) {
+    int[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/IntChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/IntChoiceGenerator.java
new file mode 100644 (file)
index 0000000..09360a7
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+/**
+* Choice Generator that creates Integer values - this is just an interface
+* so that we can type test for implementors that have their own generic hierarchy
+*/
+public interface IntChoiceGenerator extends ChoiceGenerator<Integer> {
+}
diff --git a/src/main/gov/nasa/jpf/vm/IntegerFieldInfo.java b/src/main/gov/nasa/jpf/vm/IntegerFieldInfo.java
new file mode 100644 (file)
index 0000000..9c5ca79
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+
+/**
+ * type, name, mod info about integer fields
+ */
+public class IntegerFieldInfo extends SingleSlotFieldInfo {
+  int init;
+
+  public IntegerFieldInfo (String name, int modifiers) {
+     super(name, "I", modifiers);
+  }
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setIntValue( storageOffset, init);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Integer){
+      cv = constValue;
+      init = (Integer)constValue;
+
+    } else {
+      throw new JPFException("illegal int ConstValue=" + constValue);
+    }
+  }
+
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType() {
+    return IntChoiceGenerator.class;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    int i = f.getIntValue(storageOffset);
+    return Integer.toString(i);
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    return new Integer(i);
+  }
+
+  @Override
+  public boolean isIntField(){
+    // booleans, byte, char and short are too
+    return true;
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/IsEndStateProperty.java b/src/main/gov/nasa/jpf/vm/IsEndStateProperty.java
new file mode 100644 (file)
index 0000000..1727f14
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.GenericProperty;
+import gov.nasa.jpf.search.Search;
+
+
+/**
+ * property class to check if we have reached the end state of the program
+ */
+class IsEndStateProperty extends GenericProperty {
+  @Override
+  public String getErrorMessage () {
+    return "End State Reached";
+  }
+
+  @Override
+  public boolean check (Search search, VM vm) {
+    return vm.isEndState();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/JPFOutputStream.java b/src/main/gov/nasa/jpf/vm/JPFOutputStream.java
new file mode 100644 (file)
index 0000000..71dfb9f
--- /dev/null
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PrintStream;
+
+import gov.nasa.jpf.util.FinalBitSet;
+import gov.nasa.jpf.util.PrintUtils;
+
+/**
+ * stream to write program state info in a readable and diff-able format.
+ * This is mostly intended for debugging, but could also at some point be
+ * used to restore such states.
+ * 
+ * Currently supports heap objects, classes (static fields), threads and stack frames
+ */
+public class JPFOutputStream extends OutputStream {
+  
+  PrintStream ps;
+  
+  boolean useSid = false;
+  int maxElements = -1;
+  
+  public JPFOutputStream (OutputStream os){
+    ps = new PrintStream(os);
+  }
+  
+  public JPFOutputStream (PrintStream ps){
+    this.ps = ps;
+  }
+  
+  public JPFOutputStream (){
+    this(System.out);
+  }
+  
+  @Override
+  public void close(){
+    ps.flush();
+    
+    if (ps != System.err && ps != System.out){
+      ps.close();
+    }
+  }
+  
+  public void printCommentLine(String msg){
+    ps.print("// ");
+    ps.println(msg);
+  }
+  
+  public void print (ElementInfo ei, FieldInfo fi, boolean isFiltered){
+    ps.print(fi.getName());
+    ps.print(':');
+
+    if (isFiltered){
+      ps.print("X");
+      
+    } else {
+      switch (fi.getTypeCode()) {
+      case Types.T_BOOLEAN:
+        ps.print(ei.getBooleanField(fi));
+        break;
+      case Types.T_BYTE:
+        ps.print(ei.getByteField(fi));
+        break;
+      case Types.T_CHAR:
+        PrintUtils.printCharLiteral(ps, ei.getCharField(fi));
+        break;
+      case Types.T_SHORT:
+        ps.print(ei.getShortField(fi));
+        break;
+      case Types.T_INT:
+        ps.print(ei.getIntField(fi));
+        break;
+      case Types.T_LONG:
+        ps.print(ei.getLongField(fi));
+        break;
+      case Types.T_FLOAT:
+        ps.print(ei.getFloatField(fi));
+        break;
+      case Types.T_DOUBLE:
+        ps.print(ei.getDoubleField(fi));
+        break;
+
+      case Types.T_REFERENCE:
+      case Types.T_ARRAY:
+        PrintUtils.printReference(ps, ei.getReferenceField(fi));
+        break;
+      }
+    }
+  }
+  
+  protected void printFields (ElementInfo ei, FieldInfo[] fields, FinalBitSet filterMask){
+    if (fields != null){
+      for (int i = 0; i < fields.length; i++) {
+        if (i > 0) {
+          ps.print(',');
+        }
+        print(ei, fields[i], (filterMask != null && filterMask.get(i)));
+      }
+    }
+  }
+  
+  public void print (ElementInfo ei, FinalBitSet filterMask){
+    boolean isObject = ei.isObject();
+    ClassInfo ci = ei.getClassInfo();
+    
+    int ref = (useSid) ? ei.getSid() : ei.getObjectRef();
+    ps.printf("@%x ", ref);
+    
+    if (isObject){
+      ps.print("object ");
+      if (ei.isArray()){
+        ps.print( Types.getTypeName(ci.getName()));
+      } else {
+        ps.print(ci.getName());
+      }
+    } else {
+      ps.print("class ");
+      ps.print(ci.getName());
+    }
+    
+    ps.print(':');
+    
+    if (isObject){
+      if (ei.isArray()){
+        ps.print('[');
+        ei.getArrayFields().printElements(ps, maxElements);
+        ps.print(']');
+            
+      } else {
+        ps.print('{');
+        printFields(ei, ci.getInstanceFields(), filterMask);
+        ps.print('}');
+      }
+      
+    } else {
+      ps.print('{');
+      printFields( ei, ci.getDeclaredStaticFields(), filterMask);        
+      ps.print('}');
+    }
+  }
+  
+  public void print (ThreadInfo ti){
+    PrintUtils.printReference(ps, ti.getThreadObjectRef());
+    ps.print(' ');
+    ps.print(ti.getStateDescription());
+  }
+  
+  public void print (StackFrame frame){
+    MethodInfo mi = frame.getMethodInfo();
+  
+    ps.print('@');
+    ps.print(frame.getDepth());
+    
+    ps.print(" frame ");
+    ps.print( mi.getFullName());
+    ps.print( ":{" );
+    
+    if (!mi.isStatic()){
+      ps.print("this:");
+      PrintUtils.printReference(ps, frame.getThis());
+      ps.print(',');
+    }
+    
+    ps.print("pc:");
+    ps.print(frame.getPC().getInstructionIndex());
+    
+    ps.print(",slots:[");
+    frame.printSlots(ps);
+    ps.print(']');
+    
+    ps.print('}');
+  }
+  
+  public void println(){
+    ps.println();
+  }
+  
+  public void print (NativeStateHolder nsh){
+    ps.print(nsh);
+    ps.print(":");
+    ps.print(nsh.getHash());
+  }
+  
+  @Override
+  public void write(int b) throws IOException {
+    ps.write(b);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_vm_Verify.java b/src/main/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_vm_Verify.java
new file mode 100644 (file)
index 0000000..d9f98f9
--- /dev/null
@@ -0,0 +1,1283 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.ObjectConverter;
+import gov.nasa.jpf.util.ObjectList;
+import gov.nasa.jpf.util.RunListener;
+import gov.nasa.jpf.util.RunRegistry;
+import gov.nasa.jpf.util.json.CGCall;
+import gov.nasa.jpf.util.json.JSONLexer;
+import gov.nasa.jpf.util.json.JSONObject;
+import gov.nasa.jpf.util.json.JSONParser;
+import gov.nasa.jpf.vm.choice.DoubleChoiceFromList;
+import gov.nasa.jpf.vm.choice.FloatChoiceFromList;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
+import gov.nasa.jpf.vm.choice.LongChoiceFromList;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.PrintStream;
+import java.util.BitSet;
+import java.util.List;
+
+/**
+ * native peer class for programmatic JPF interface (that can be used inside
+ * of apps to verify - if you are aware of the danger that comes with it)
+ * 
+ * this peer is a bit different in that it only uses static fields and methods because
+ * its use is supposed to be JPF global (without classloader namespaces)
+ */
+public class JPF_gov_nasa_jpf_vm_Verify extends NativePeer {
+  static final int MAX_COUNTERS = 127;
+
+  static boolean isInitialized;
+  
+  // those are used to store search global int values (e.g. from TestJPF derived classes)
+  static int[] counter;
+  static IntTable<String> map;
+
+  public static int heuristicSearchValue;
+  
+  static boolean supportIgnorePath;
+  static boolean breakSingleChoice;
+  static boolean enableAtomic;
+
+  static Config config;  // we need to keep this around for CG creation
+
+  // our const ChoiceGenerator ctor argtypes
+  static Class[] cgArgTypes = { Config.class, String.class };
+  // this is our cache for ChoiceGenerator ctor parameters
+  static Object[] cgArgs = { null, null };
+
+  static BitSet[] bitSets;
+  static int nextBitSet;
+
+  static PrintStream out;
+  
+  public static boolean init (Config conf) {
+
+    if (!isInitialized){
+      supportIgnorePath = conf.getBoolean("vm.verify.ignore_path");
+      breakSingleChoice = conf.getBoolean("cg.break_single_choice");
+      enableAtomic = conf.getBoolean("cg.enable_atomic", true);
+
+      heuristicSearchValue = conf.getInt("search.heuristic.default_value");
+
+      counter = null;
+      map = null;
+      
+      config = conf;
+
+      String outFile = conf.getString("vm.verify.output_file");
+      if (outFile != null){
+        try {
+          out = new PrintStream(outFile);
+        } catch (FileNotFoundException fnx){
+          System.err.println("error: could not open verify output file " + outFile + ", using System.out");
+          out = System.out;
+        }
+      } else {
+        out = System.out;
+      }
+      
+      Verify.setPeerClass( JPF_gov_nasa_jpf_vm_Verify.class);
+
+      RunRegistry.getDefaultRegistry().addListener( new RunListener() {
+        @Override
+                   public void reset (RunRegistry reg){
+          isInitialized = false;
+        }
+      });
+    }
+    return true;
+  }
+
+  
+  public static final int NO_VALUE = -1;
+  
+  @MJI
+  public static int getValue__Ljava_lang_String_2__I (MJIEnv env, int clsObjRef, int keyRef) {
+    if (map == null) {
+      return NO_VALUE;
+    } else {
+      String key = env.getStringObject(keyRef);
+      IntTable.Entry<String> e = map.get(key);
+      if (e != null) {
+        return e.val;
+      } else {
+        return NO_VALUE;
+      }
+    }
+  }
+  
+  @MJI
+  public static void putValue__Ljava_lang_String_2I__V (MJIEnv env, int clsObjRef, int keyRef, int val) {
+    if (map == null) {
+      map = new IntTable<String>();
+    }
+    
+    String key = env.getStringObject(keyRef);
+    map.put(key, val);
+  }
+  
+  @MJI
+  public static int getCounter__I__I (MJIEnv env, int clsObjRef, int counterId) {
+    if ((counter == null) || (counterId < 0) || (counterId >= counter.length)) {
+      return 0;
+    }
+
+    return counter[counterId];
+  }
+
+  private static void ensureCounterCapacity (int counterId){
+    if (counter == null) {
+      counter = new int[(counterId >= MAX_COUNTERS) ? counterId+1 : MAX_COUNTERS];
+    } else if (counterId >= counter.length) {
+      int[] newCounter = new int[counterId+1];
+      System.arraycopy(counter, 0, newCounter, 0, counter.length);
+      counter = newCounter;
+    }    
+  }
+  
+  @MJI
+  public static void resetCounter__I__V (MJIEnv env, int clsObjRef, int counterId) {
+    if ((counter == null) || (counterId < 0) || (counterId >= counter.length)) {
+      return;
+    }
+    counter[counterId] = 0;
+  }
+
+  @MJI
+  public static void setCounter__II__V (MJIEnv env, int clsObjRef, int counterId, int val) {
+    if (counterId < 0){
+      return;
+    }
+    
+    ensureCounterCapacity(counterId);
+    counter[counterId] = val;
+  }
+  
+  @MJI
+  public static int incrementCounter__I__I (MJIEnv env, int clsObjRef, int counterId) {
+    if (counterId < 0) {
+      return 0;
+    }
+
+    ensureCounterCapacity(counterId);
+    return ++counter[counterId];
+  }
+
+  private static void checkBitSetId(int id) {
+    if (bitSets == null) {
+      bitSets = new BitSet[id + 1];
+    } else if (id >= bitSets.length) {
+      BitSet[] newBitSets = new BitSet[id + 1];
+      System.arraycopy(bitSets, 0, newBitSets, 0, bitSets.length);
+      bitSets = newBitSets;
+    }
+
+    if (bitSets[id] == null) {
+      bitSets[id] = new BitSet();
+    }
+  }
+
+  @MJI
+  public static void setBitInBitSet__IIZ__V(MJIEnv env, int clsObjRef, int id, int bitNum, boolean value) {
+    checkBitSetId(id);
+    bitSets[id].set(bitNum, value);
+  }
+
+  @MJI
+  public static boolean getBitInBitSet__II__Z(MJIEnv env, int clsObjRef, int id, int bitNum) {
+    checkBitSetId(id);
+    return bitSets[id].get(bitNum);
+  }
+
+  @MJI
+  public static long currentTimeMillis____J (MJIEnv env, int clsObjRef) {
+    return System.currentTimeMillis();
+  }
+
+  @MJI
+  public static String getType (int objRef, MJIEnv env) {
+    return Types.getTypeName(env.getElementInfo(objRef).getType());
+  }
+
+  @MJI
+  public static void addComment__Ljava_lang_String_2__V (MJIEnv env, int clsObjRef, int stringRef) {
+    SystemState ss = env.getSystemState();
+    String      cmt = env.getStringObject(stringRef);
+    ss.getTrail().setAnnotation(cmt);
+  }
+
+  @MJI
+  public static void assertTrue__Z__V (MJIEnv env, int clsObjRef, boolean b) {
+    if (!b) {
+      env.throwException("java.lang.AssertionError", "assertTrue failed");
+    }
+  }
+
+  // those are evil - use with extreme care. If something blocks inside of
+  // an atomic section we have to raise an exception
+  
+  @MJI
+  public static void beginAtomic____V (MJIEnv env, int clsObjRef) {
+    if (enableAtomic){
+      ThreadInfo tiAtomic = env.getThreadInfo();
+      
+      if (tiAtomic.getScheduler().setsBeginAtomicCG(tiAtomic)){
+        env.repeatInvocation();
+        return;
+      }
+
+      env.getSystemState().incAtomic();
+    }
+  }
+  
+  @MJI
+  public static void endAtomic____V (MJIEnv env, int clsObjRef) {
+    if (enableAtomic){
+      ThreadInfo tiAtomic = env.getThreadInfo();
+
+      if (!tiAtomic.isFirstStepInsn()){
+        env.getSystemState().decAtomic();
+      }
+      
+      if (tiAtomic.getScheduler().setsEndAtomicCG(tiAtomic)){
+        env.repeatInvocation();
+        return;
+      }
+    }
+  }
+
+  @MJI
+  public static void busyWait__J__V (MJIEnv env, int clsObjRef, long duration) {
+    // nothing required here (we systematically explore scheduling
+    // sequences anyway), but we need to intercept the call
+  }
+
+  @MJI
+  public static void ignoreIf__Z__V (MJIEnv env, int clsObjRef, boolean cond) {
+    if (supportIgnorePath) {
+      env.getSystemState().setIgnored(cond);
+    }
+  }
+
+  @MJI
+  public static void interesting__Z__V (MJIEnv env, int clsObjRef, boolean cond) {
+    env.getSystemState().setInteresting(cond);
+  }
+
+  @MJI
+  public static void breakTransition__Ljava_lang_String_2__V (MJIEnv env, int clsObjRef, int reasonRef){
+    ThreadInfo ti = env.getThreadInfo();
+    String reason = env.getStringObject(reasonRef);
+    ti.breakTransition(reason);
+  }
+
+  /**
+   * mostly for debugging purposes - this does not optimize away single choice CGs 
+   */
+  @MJI
+  public int breakTransition__Ljava_lang_String_2II__I (MJIEnv env, int clsObjRef, int reasonRef, int min, int max) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+    String reason = env.getStringObject(reasonRef);
+    
+    if (!ti.isFirstStepInsn()) { // first time around
+      IntChoiceGenerator cg = new IntIntervalGenerator( reason, min,max);
+      if (ss.setNextChoiceGenerator(cg)){
+        env.repeatInvocation();
+      }
+      return -1;
+      
+    } else {
+      return getNextChoice(ss, reason, IntChoiceGenerator.class, Integer.class);
+    } 
+  }
+
+  @MJI
+  public static boolean isCalledFromClass__Ljava_lang_String_2__Z (MJIEnv env, int clsObjRef,
+                                           int clsNameRef) {
+    String refClassName = env.getStringObject(clsNameRef);
+    ThreadInfo ti = env.getThreadInfo();
+
+    StackFrame caller = ti.getLastInvokedStackFrame();
+    if (caller != null){
+      ClassInfo ci = caller.getClassInfo();
+      return ci.isInstanceOf(refClassName);
+    }
+
+    return false;
+  }
+
+
+  static <T extends ChoiceGenerator<?>> T createChoiceGenerator (Class<T> cgClass, SystemState ss, String id) {
+    T gen = null;
+
+    cgArgs[0] = config;
+    cgArgs[1] = id; // good thing we are not multithreaded (other fields are const)
+
+    String key = id + ".class";
+    gen = config.getEssentialInstance(key, cgClass, cgArgTypes, cgArgs);
+    return gen;
+  }
+
+  static <T> T registerChoiceGenerator (MJIEnv env, SystemState ss, ThreadInfo ti, ChoiceGenerator<T> cg, T dummyVal){
+
+    int n = cg.getTotalNumberOfChoices();
+    if (n == 0) {
+      // nothing, just return the default value
+
+    } else if (n == 1 && !breakSingleChoice) {
+      // no choice -> no CG optimization
+      cg.advance();
+      return cg.getNextChoice();
+
+    } else {
+      if (ss.setNextChoiceGenerator(cg)){
+        env.repeatInvocation();
+      }
+    }
+
+    return dummyVal;
+  }
+
+  static <T,C extends ChoiceGenerator<T>> T getNextChoice (SystemState ss, String id, Class<C> cgClass, Class<T> choiceClass){
+    ChoiceGenerator<?> cg = ss.getCurrentChoiceGenerator(id, cgClass);
+
+    assert (cg != null) : "no ChoiceGenerator of type " + cgClass.getName();
+    return ((ChoiceGenerator<T>)cg).getNextChoice();
+  }
+
+  @MJI
+  public static boolean getBoolean____Z (MJIEnv env, int clsObjRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+    ChoiceGenerator<?> cg;
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      cg = new BooleanChoiceGenerator(config, "verifyGetBoolean");
+      if (ss.setNextChoiceGenerator(cg)){
+        env.repeatInvocation();
+      }
+      return true;  // not used if we repeat
+
+    } else {  // this is what really returns results
+      return getNextChoice(ss,"verifyGetBoolean", BooleanChoiceGenerator.class,Boolean.class);
+    }
+  }
+
+  @MJI
+  public static boolean getBoolean__Z__Z (MJIEnv env, int clsObjRef, boolean falseFirst) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+    ChoiceGenerator<?> cg;
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      cg = new BooleanChoiceGenerator( "verifyGetBoolean(Z)", falseFirst );
+      if (ss.setNextChoiceGenerator(cg)){
+        env.repeatInvocation();
+      }
+      return true;  // not used if we repeat
+
+    } else {  // this is what really returns results
+      return getNextChoice(ss,"verifyGetBoolean(Z)", BooleanChoiceGenerator.class, Boolean.class);
+    }
+  }
+
+  @MJI
+  public static int getInt__II__I (MJIEnv env, int clsObjRef, int min, int max) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+
+      if (min > max){
+        int t = max;
+        max = min;
+        min = t;
+      }
+
+      IntChoiceGenerator cg = new IntIntervalGenerator( "verifyGetInt(II)", min,max);
+      return registerChoiceGenerator(env,ss,ti,cg,0);
+
+    } else {
+      return getNextChoice(ss, "verifyGetInt(II)", IntChoiceGenerator.class, Integer.class);
+    }
+  }
+
+  static int getIntFromList (MJIEnv env, int[] values){
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      ChoiceGenerator<Integer> cg = new IntChoiceFromSet( "verifyGetIntSet([I)", values);
+      return registerChoiceGenerator(env,ss,ti,cg,0);
+
+    } else {
+      return getNextChoice(ss, "verifyGetIntSet([I)", IntChoiceGenerator.class, Integer.class);
+    }    
+  }
+  
+  @MJI
+  public static int getIntFromList___3I__I (MJIEnv env, int clsObjRef, int valArrayRef){
+    int[] values = env.getIntArrayObject(valArrayRef);
+    return getIntFromList( env, values);
+  }
+
+  @MJI
+  public static int getInt__Ljava_lang_String_2__I (MJIEnv env, int clsObjRef, int idRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      String id = env.getStringObject(idRef);
+      IntChoiceGenerator cg = createChoiceGenerator( IntChoiceGenerator.class, ss, id);
+      return registerChoiceGenerator(env,ss,ti,cg, 0);
+
+    } else {
+      String id = env.getStringObject(idRef);
+      return getNextChoice(ss, id, IntChoiceGenerator.class,Integer.class);
+    }
+  }
+
+  static long getLongFromList (MJIEnv env, long[] values){
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      ChoiceGenerator<Long> cg = new LongChoiceFromList( "verifyLongList([J)", values);
+      return registerChoiceGenerator(env,ss,ti,cg,0L);
+
+    } else {
+      return getNextChoice(ss, "verifyLongList([J)", LongChoiceGenerator.class, Long.class);
+    }    
+  }
+  
+  @MJI
+  public static long getLongFromList___3J__J (MJIEnv env, int clsObjRef, int valArrayRef){
+    long[] values = env.getLongArrayObject(valArrayRef);
+    return getLongFromList( env, values);    
+  }
+  
+  @MJI
+  public static int getObject__Ljava_lang_String_2__Ljava_lang_Object_2 (MJIEnv env, int clsObjRef, int idRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      String id = env.getStringObject(idRef);
+      ReferenceChoiceGenerator cg = createChoiceGenerator( ReferenceChoiceGenerator.class, ss, id);
+      return registerChoiceGenerator(env,ss,ti,cg, MJIEnv.NULL);
+
+    } else {
+      String id = env.getStringObject(idRef);
+      return getNextChoice(ss, id, ReferenceChoiceGenerator.class,Integer.class);
+    }
+  }
+
+  @MJI
+  public static double getDouble__Ljava_lang_String_2__D (MJIEnv env, int clsObjRef, int idRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      String id = env.getStringObject(idRef);
+      DoubleChoiceGenerator cg = createChoiceGenerator( DoubleChoiceGenerator.class, ss, id);
+      return registerChoiceGenerator(env,ss,ti,cg, 0.0);
+
+    } else {
+      String id = env.getStringObject(idRef);
+      return getNextChoice(ss, id, DoubleChoiceGenerator.class,Double.class);
+    }
+  }
+
+  @MJI
+  public static double getDoubleFromList (MJIEnv env, double[] values){
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      ChoiceGenerator<Double> cg = new DoubleChoiceFromList("verifyDoubleList([D)", values);
+      return registerChoiceGenerator(env,ss,ti,cg, 0.0);
+
+    } else {
+      return getNextChoice(ss, "verifyDoubleList([D)", DoubleChoiceFromList.class,Double.class);
+    }    
+  }
+  
+  @MJI
+  public static double getDoubleFromList___3D__D (MJIEnv env, int clsObjRef, int valArrayRef){
+    double[] values = env.getDoubleArrayObject(valArrayRef);
+    return getDoubleFromList( env, values);
+  }
+
+  @MJI
+  public static float getFloatFromList (MJIEnv env, float[] values){
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+
+    if (!ti.isFirstStepInsn()) { // first time around
+      ChoiceGenerator<Float> cg = new FloatChoiceFromList("verifyFloatList([F)", values);
+      return registerChoiceGenerator(env,ss,ti,cg, 0.0f);
+
+    } else {
+      return getNextChoice(ss, "verifyFloatList([F)", FloatChoiceFromList.class, Float.class);
+    }    
+  }
+  
+  @MJI
+  public static float getFloatFromList___3F__F (MJIEnv env, int clsObjRef, int valArrayRef){
+    float[] values = env.getFloatArrayObject(valArrayRef);
+    return getFloatFromList( env, values);
+  }
+
+  @MJI
+  public static void threadPrint__Ljava_lang_String_2__V (MJIEnv env, int clsRef, int sRef){
+    String s = env.getStringObject(sRef);
+    ThreadInfo ti = env.getThreadInfo();
+    System.out.print(ti.getName());
+    System.out.print(s);
+  }
+  
+  @MJI
+  public static void print__Ljava_lang_String_2I__V (MJIEnv env, int clsRef, int sRef, int val){
+    String s = env.getStringObject(sRef);
+    System.out.print(s + " : " + val);
+  }
+
+  @MJI
+  public static void print__Ljava_lang_String_2Z__V (MJIEnv env, int clsRef, int sRef, boolean val){
+    String s = env.getStringObject(sRef);
+    System.out.print(s + " : " + val);
+  }
+
+  @MJI
+  public static void print___3Ljava_lang_String_2__V (MJIEnv env, int clsRef, int argsRef){
+    int n = env.getArrayLength(argsRef);
+    for (int i=0; i<n; i++){
+      int aref = env.getReferenceArrayElement(argsRef, i);
+      String s = env.getStringObject(aref);
+      System.out.print(s);
+    }
+  }
+  
+  @MJI
+  public static void print__Ljava_lang_String_2__V (MJIEnv env, int clsRef, int sRef){
+    String s = env.getStringObject(sRef);
+    System.out.print(s);
+  }
+
+  @MJI
+  public static void println__Ljava_lang_String_2__V (MJIEnv env, int clsRef, int sRef){
+    String s = env.getStringObject(sRef);
+    System.out.println(s);
+  }
+
+  @MJI
+  public static void threadPrintln__Ljava_lang_String_2__V (MJIEnv env, int clsRef, int sRef){
+    threadPrint__Ljava_lang_String_2__V(env, clsRef, sRef);
+    System.out.println();
+  }
+
+  
+  @MJI
+  public static void println____V (MJIEnv env, int clsRef){
+    System.out.println();
+  }
+  
+  //--- various attribute test methods
+  
+  private static int getAttribute (MJIEnv env, Object a){
+    if (a != null) {
+      if (a instanceof Integer) {
+        return ((Integer) a).intValue();
+      } else {
+        env.throwException("java.lang.RuntimeException", "element attribute not an Integer: " + a);
+      }
+    }
+
+    return 0;
+  }
+  
+  private static int getAttributeList (MJIEnv env, Object a){
+    if (a != null) {
+      int l = ObjectList.size(a);
+      int[] attrs = new int[l];
+      int i = 0;
+      for (Integer v : ObjectList.typedIterator(a, Integer.class)) {
+        attrs[i++] = v;
+      }
+      if (i != l) {
+        env.throwException("java.lang.RuntimeException", "found non-Integer attributes");
+        return 0;
+      }
+
+      return env.newIntArray(attrs);
+      
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+  
+  @MJI
+  public static void setObjectAttribute__Ljava_lang_Object_2I__V (MJIEnv env, int clsRef, int oRef, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      ei.setObjectAttr(Integer.valueOf(attr));
+    }
+  }
+  
+  @MJI
+  public static int getObjectAttribute__Ljava_lang_Object_2__I (MJIEnv env, int clsRef, int oRef){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      return getAttribute( env, ei.getObjectAttr());
+    }
+
+    return 0;
+  }
+  
+  @MJI
+  public static void addObjectAttribute__Ljava_lang_Object_2I__V (MJIEnv env, int clsRef, int oRef, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      ei.addObjectAttr(Integer.valueOf(attr));
+    }
+  }
+  
+  @MJI
+  public static int getObjectAttributes__Ljava_lang_Object_2___3I (MJIEnv env, int clsRef, int oRef){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      return getAttributeList( env, ei.getObjectAttr());
+    }
+
+    return MJIEnv.NULL;
+  }
+  
+  @MJI
+  public static void setFieldAttribute__Ljava_lang_Object_2Ljava_lang_String_2I__V (MJIEnv env, int clsRef,
+                                                                                    int oRef, int fnRef, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      if (ei != null){
+        String fname = env.getStringObject(fnRef);
+        FieldInfo fi = ei.getFieldInfo(fname);
+
+        if (fi != null) {
+          ei.setFieldAttr(fi, Integer.valueOf(attr));
+        } else {
+          env.throwException("java.lang.NoSuchFieldException",
+                  ei.getClassInfo().getName() + '.' + fname);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+  }
+  
+  @MJI
+  public static int getFieldAttribute__Ljava_lang_Object_2Ljava_lang_String_2__I (MJIEnv env, int clsRef,
+                                                                                    int oRef, int fnRef){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      if (ei != null){
+        String fname = env.getStringObject(fnRef);
+        FieldInfo fi = ei.getFieldInfo(fname);
+
+        if (fi != null) {
+          return getAttribute( env, ei.getFieldAttr(fi));
+        } else {
+          env.throwException("java.lang.NoSuchFieldException",
+                  ei.toString() + '.' + fname);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+
+    return 0;
+  }
+  
+  @MJI
+  public static void addFieldAttribute__Ljava_lang_Object_2Ljava_lang_String_2I__V (MJIEnv env, int clsRef,
+                                                                                    int oRef, int fnRef, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      if (ei != null){
+        String fname = env.getStringObject(fnRef);
+        FieldInfo fi = ei.getFieldInfo(fname);
+
+        if (fi != null) {
+          ei.addFieldAttr(fi, Integer.valueOf(attr));
+        } else {
+          env.throwException("java.lang.NoSuchFieldException",
+                  ei.getClassInfo().getName() + '.' + fname);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+  }
+
+  @MJI
+  public static int getFieldAttributes__Ljava_lang_Object_2Ljava_lang_String_2___3I (MJIEnv env, int clsRef,
+                                                                                    int oRef, int fnRef){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      if (ei != null){
+        String fname = env.getStringObject(fnRef);
+        FieldInfo fi = ei.getFieldInfo(fname);
+
+        if (fi != null) {
+          return getAttributeList( env, ei.getFieldAttr(fi));          
+        } else {
+          env.throwException("java.lang.NoSuchFieldException",
+                  ei.toString() + '.' + fname);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public static void setLocalAttribute__Ljava_lang_String_2I__V (MJIEnv env, int clsRef, int varRef, int attr) {
+    String slotName = env.getStringObject(varRef);
+    StackFrame frame = env.getModifiableCallerStackFrame(); // we are executing in a NativeStackFrame
+
+    if (!frame.getMethodInfo().isStatic() &&  slotName.equals("this")) {
+      frame.setLocalAttr(0, Integer.valueOf(attr)); // only for instance methods of course
+
+    } else {
+      int slotIdx = frame.getLocalVariableSlotIndex(slotName);
+      if (slotIdx >= 0) {
+        frame.setLocalAttr(slotIdx, Integer.valueOf(attr));
+      } else {
+        env.throwException("java.lang.RuntimeException", "local variable not found: " + slotName);
+      }
+    }
+  }
+
+  @MJI
+  public static int getLocalAttribute__Ljava_lang_String_2__I (MJIEnv env, int clsRef, int varRef) {
+    String slotName = env.getStringObject(varRef);
+    ThreadInfo ti = env.getThreadInfo();
+    StackFrame frame = env.getCallerStackFrame();
+
+    int slotIdx = frame.getLocalVariableSlotIndex(slotName);
+    if (slotIdx >= 0) {
+      return getAttribute( env, frame.getLocalAttr(slotIdx));
+    } else {
+      env.throwException("java.lang.RuntimeException", "local variable not found: " + slotName);
+      return 0;
+    }
+  }
+
+  @MJI
+  public static void addLocalAttribute__Ljava_lang_String_2I__V (MJIEnv env, int clsRef, int varRef, int attr) {
+    String slotName = env.getStringObject(varRef);
+    StackFrame frame = env.getModifiableCallerStackFrame(); // we are executing in a NativeStackFrame
+
+    if (!frame.getMethodInfo().isStatic() &&  slotName.equals("this")) {
+      frame.addLocalAttr(0, Integer.valueOf(attr)); // only for instance methods of course
+
+    } else {
+      int slotIdx = frame.getLocalVariableSlotIndex(slotName);
+      if (slotIdx >= 0) {
+        frame.addLocalAttr(slotIdx, Integer.valueOf(attr));
+      } else {
+        env.throwException("java.lang.RuntimeException", "local variable not found: " + slotName);
+      }
+    }
+  }
+  
+  @MJI
+  public static int getLocalAttributes__Ljava_lang_String_2___3I (MJIEnv env, int clsRef, int varRef) {
+    String slotName = env.getStringObject(varRef);
+    ThreadInfo ti = env.getThreadInfo();
+    StackFrame frame = env.getCallerStackFrame();
+
+    int slotIdx = frame.getLocalVariableSlotIndex(slotName);
+    if (slotIdx >= 0) {
+      return getAttributeList( env, frame.getLocalAttr(slotIdx));
+    } else {
+      env.throwException("java.lang.RuntimeException", "local variable not found: " + slotName);
+    }
+    
+    return MJIEnv.NULL;
+  }
+  
+  @MJI
+  public static void setElementAttribute__Ljava_lang_Object_2II__V (MJIEnv env, int clsRef,
+                                                                    int oRef, int idx, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+
+      if (ei != null){
+        if (ei.isArray()) {
+          if (idx < ei.arrayLength()) {
+            ei.setElementAttr(idx, Integer.valueOf(attr));
+          } else {
+            env.throwException("java.lang.ArrayIndexOutOfBoundsException",
+                    Integer.toString(idx));
+          }
+        } else {
+          env.throwException("java.lang.RuntimeException",
+                  "not an array: " + ei);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+  }
+
+  @MJI
+  public static int getElementAttribute__Ljava_lang_Object_2I__I (MJIEnv env, int clsRef,
+                                                                  int oRef, int idx){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+
+      if (ei != null) {
+        if (ei.isArray()) {
+          if (idx < ei.arrayLength()) {
+            return getAttribute( env, ei.getElementAttr( idx));
+          } else {
+            env.throwException("java.lang.ArrayIndexOutOfBoundsException",
+                    Integer.toString(idx));
+          }
+        } else {
+          env.throwException("java.lang.RuntimeException",
+                  "not an array: " + ei);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+
+    return 0;
+  }
+
+  @MJI
+  public static void addElementAttribute__Ljava_lang_Object_2II__V (MJIEnv env, int clsRef,
+                                                                    int oRef, int idx, int attr){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+
+      if (ei != null){
+        if (ei.isArray()) {
+          if (idx < ei.arrayLength()) {
+            ei.addElementAttr(idx, Integer.valueOf(attr));
+          } else {
+            env.throwException("java.lang.ArrayIndexOutOfBoundsException",
+                    Integer.toString(idx));
+          }
+        } else {
+          env.throwException("java.lang.RuntimeException",
+                  "not an array: " + ei);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+  }
+
+  @MJI
+  public static int getElementAttributes__Ljava_lang_Object_2I___3I (MJIEnv env, int clsRef,
+                                                                        int oRef, int idx){
+    if (oRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(oRef);
+      if (ei != null) {
+        if (ei.isArray()) {
+          if (idx < ei.arrayLength()) {
+            return getAttributeList( env, ei.getElementAttr( idx));
+          } else {
+            env.throwException("java.lang.ArrayIndexOutOfBoundsException",
+                    Integer.toString(idx));
+          }
+        } else {
+          env.throwException("java.lang.RuntimeException",
+                  "not an array: " + ei);
+        }
+      } else {
+        env.throwException("java.lang.RuntimeException", "illegal reference value: " + oRef);
+      }
+    }
+
+    return MJIEnv.NULL;
+  }
+
+  
+  /**
+   *  deprecated, use getBoolean()
+   */
+  @MJI
+  public static boolean randomBool (MJIEnv env, int clsObjRef) {
+    //SystemState ss = env.getSystemState();
+    //return (ss.random(2) != 0);
+
+    return getBoolean____Z(env, clsObjRef);
+  }
+
+
+
+  /**
+   * deprecated, use getInt
+   */
+  @MJI
+  public static int random__I__I (MJIEnv env, int clsObjRef, int x) {
+   return getInt__II__I(env, clsObjRef, 0, x);
+  }
+
+  static void boring__Z__V (MJIEnv env, int clsObjRef, boolean b) {
+    env.getSystemState().setBoring(b);
+  }
+
+  @MJI
+  public static boolean isRunningInJPF____Z(MJIEnv env, int clsObjRef) {
+    return true;
+  }
+
+  @MJI
+  public static boolean vmIsMatchingStates____Z(MJIEnv env, int clsObjRef) {
+    return env.getVM().getStateSet() != null;
+  }
+
+  @MJI
+  public static void storeTrace__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int clsObjRef,
+                                      int filenameRef, int commentRef) {
+    String fileName = env.getStringObject(filenameRef);
+    String comment = env.getStringObject(commentRef);
+    env.getVM().storeTrace(fileName, comment, config.getBoolean("trace.verbose", false));
+  }
+
+  @MJI
+  public static void terminateSearch____V (MJIEnv env, int clsObjRef) {
+    JPF jpf = env.getVM().getJPF();
+    jpf.getSearch().terminate();
+  }
+
+  @MJI public static void setHeuristicSearchValue__I__V (MJIEnv env, int clsObjRef, int val){
+    heuristicSearchValue =  val;
+  }
+
+  @MJI public static int getHeuristicSearchValue____I (MJIEnv env, int clsObjRef){
+    return heuristicSearchValue;
+  }
+
+  @MJI public static void resetHeuristicSearchValue____V (MJIEnv env, int clsObjRef){
+    heuristicSearchValue = config.getInt("search.heuristic.default_value");
+  }
+
+
+
+  @MJI
+  public static boolean isTraceReplay____Z (MJIEnv env, int clsObjRef) {
+    return env.getVM().isTraceReplay();
+  }
+
+  @MJI
+  public static boolean isShared__Ljava_lang_Object_2__Z (MJIEnv env, int clsObjRef, int objRef){
+    if (objRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(objRef);
+      if (ei != null){
+        return ei.isShared();
+      }
+    }
+    
+    return false;
+  }
+  
+  @MJI
+  public static void setShared__Ljava_lang_Object_2Z__V (MJIEnv env, int clsObjRef, int objRef, boolean isShared) {
+    if (objRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(objRef);
+      if (ei != null){
+        if (ei.getClassInfo() == ClassLoaderInfo.getCurrentSystemClassLoader().getClassClassInfo()) {
+          // it's a class object, set static fields shared
+          ei = env.getStaticElementInfo(objRef);
+        }
+        
+        if (ei.isShared() != isShared) {
+          ei = ei.getModifiableInstance();
+          ei.setShared( env.getThreadInfo(), isShared);
+        }
+      }
+    }    
+  }
+
+  @MJI
+  public static void freezeSharedness__Ljava_lang_Object_2Z__V (MJIEnv env, int clsObjRef, int objRef, boolean freeze) {
+    if (objRef != MJIEnv.NULL){
+      ElementInfo ei = env.getElementInfo(objRef);
+      if (ei != null) {
+        if (ei.getClassInfo() == ClassLoaderInfo.getCurrentSystemClassLoader().getClassClassInfo()) { 
+          // it's a class object, freeze sharedness of static fields
+          ei = env.getStaticElementInfo(objRef);
+        }
+
+        if (ei.isSharednessFrozen() != freeze) {
+          ei = ei.getModifiableInstance();
+          ei.freezeSharedness(freeze);
+        }
+      }
+    }    
+  }
+
+  @MJI
+  public static void setProperties___3Ljava_lang_String_2__V (MJIEnv env, int clsObjRef, int argRef) {
+    if (argRef != MJIEnv.NULL) {
+      Config conf = env.getConfig();
+
+      int n = env.getArrayLength(argRef);
+      for (int i=0; i<n; i++) {
+        int pRef = env.getReferenceArrayElement(argRef, i);
+        if (pRef != MJIEnv.NULL) {
+          String p = env.getStringObject(pRef);
+          config.parse(p);
+        }
+      }
+    }
+  }
+
+  @MJI
+  public static int getProperty__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int keyRef) {
+    if (keyRef != MJIEnv.NULL){
+      Config conf = env.getConfig();
+
+      String key = env.getStringObject(keyRef);
+      String val = config.getString(key);
+
+      if (val != null){
+        return env.newString(val);
+      } else {
+        return MJIEnv.NULL;
+      }
+
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public static void printPathOutput__ZLjava_lang_String_2__V (MJIEnv env, int clsObjRef, boolean cond, int msgRef){
+    if (cond){
+      printPathOutput__Ljava_lang_String_2__V(env,clsObjRef,msgRef);
+    }
+  }
+
+  @MJI
+  public static void printPathOutput__Ljava_lang_String_2__V (MJIEnv env, int clsObjRef, int msgRef){
+    VM vm = env.getVM();
+
+    System.out.println();
+    if (msgRef != MJIEnv.NULL){
+      String msg = env.getStringObject(msgRef);
+      System.out.println("~~~~~~~~~~~~~~~~~~~~~~~ begin program output at: " + msg);
+    } else {
+      System.out.println("~~~~~~~~~~~~~~~~~~~~~~~ begin path output");
+    }
+
+    for (Transition t : vm.getPath()) {
+      String s = t.getOutput();
+      if (s != null) {
+        System.out.print(s);
+      }
+    }
+
+    // we might be in the middle of a transition that isn't stored yet in the path
+    String s = vm.getPendingOutput();
+    if (s != null) {
+      System.out.print(s);
+    }
+
+    System.out.println("~~~~~~~~~~~~~~~~~~~~~~~ end path output");
+  }
+
+
+  // the JSON object initialization
+  @MJI
+  public static int createFromJSON__Ljava_lang_Class_2Ljava_lang_String_2__Ljava_lang_Object_2(
+          MJIEnv env, int clsObjRef, int newObjClsRef, int jsonStringRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    SystemState ss = env.getSystemState();
+    
+    String jsonString = env.getStringObject(jsonStringRef);    
+    JSONLexer lexer = new JSONLexer(jsonString);
+    JSONParser parser = new JSONParser(lexer);
+    JSONObject jsonObject = parser.parse();
+        
+    if (jsonObject != null) {
+      ClassInfo ci = env.getReferredClassInfo( newObjClsRef);
+      
+      // check if we need any class init (and hence reexecution) before creating any CGs
+      if (jsonObject.requiresClinitExecution(ci,ti)){
+        env.repeatInvocation();
+        return MJIEnv.NULL;
+      }
+
+      if (!ti.isFirstStepInsn()) {
+        // Top half - get and register CGs we need to set to fill object from JSON
+        List<ChoiceGenerator<?>> cgList = CGCall.createCGList(jsonObject);
+        if (cgList.isEmpty()){
+          return jsonObject.fillObject(env, ci, null, "");
+          
+        } else {
+          for (ChoiceGenerator<?> cg : cgList) {
+            ss.setNextChoiceGenerator(cg);
+          }
+
+          env.repeatInvocation();
+          return MJIEnv.NULL;
+        }
+        
+      } else {
+        // Bottom half - fill object with JSON and current values of CGs
+        ChoiceGenerator<?>[] cgs = ss.getChoiceGenerators();
+
+        return jsonObject.fillObject(env, ci, cgs, "");
+      }
+
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+  
+  @MJI
+  public static int readObjectFromFile__Ljava_lang_Class_2Ljava_lang_String_2__Ljava_lang_Object_2(
+          MJIEnv env, int clsObjRef, int newObjClsRef, int fileNameRef) {
+    int typeNameRef = env.getReferenceField(newObjClsRef, "name");
+    String typeName = env.getStringObject(typeNameRef);
+    String fileName = env.getStringObject(fileNameRef);
+
+    try {
+
+      FileInputStream fis = new FileInputStream(fileName);
+      ObjectInputStream ois = new ObjectInputStream(fis);
+      Object javaObject = ois.readObject();
+      String readObjectTypeName = javaObject.getClass().getCanonicalName();
+      
+      int readObjRef = ObjectConverter.JPFObjectFromJavaObject(env, javaObject);
+
+      return readObjRef;
+      
+    } catch (ClinitRequired clix){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+      
+    } catch (IOException iox){
+      throw new JPFException("failure reading object from file: " + fileName, iox);
+    } catch (ClassNotFoundException cnfx){
+      throw new JPFException("failure reading object from file: " + fileName, cnfx);      
+    }
+  }
+  
+  //--- those need to be kept in sync with the model side
+  public static final int SEVERE = 1;
+  public static final int WARNING = 2;
+  public static final int INFO = 3;
+  public static final int FINE = 4;
+  public static final int FINER = 5;
+  public static final int FINEST = 6;
+
+  
+  private static void log (JPFLogger logger, int logLevel, String msg){
+    switch (logLevel){
+    case SEVERE:
+      logger.severe( msg);
+      break;
+    case WARNING:
+      logger.warning( msg);
+      break;
+    case INFO:
+      logger.info( msg);
+      break;
+    case FINE:
+      logger.fine( msg);
+      break;
+    case FINER:
+      logger.finer( msg);
+      break;
+    case FINEST:
+      logger.finest( msg);
+      break;
+    default:
+      throw new JPFException("unknown log level " + logLevel + " for logger " + logger.getName());
+    }    
+  }
+  
+  @MJI
+  public static void log__Ljava_lang_String_2ILjava_lang_String_2__V (MJIEnv env, int clsObjRef,
+      int loggerIdRef, int logLevel, int msgRef){
+    String loggerId = env.getStringObject(loggerIdRef);
+    String msg = env.getStringObject(msgRef);
+    JPFLogger logger = JPF.getLogger(loggerId);
+    
+    log( logger, logLevel, msg);
+  }
+
+  @MJI
+  public static void log__Ljava_lang_String_2ILjava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int clsObjRef,
+      int loggerIdRef, int logLevel, int arg1Ref, int arg2Ref){
+    String loggerId = env.getStringObject(loggerIdRef);
+    String msg = env.getStringObject(arg1Ref) + env.getStringObject(arg2Ref);
+    JPFLogger logger = JPF.getLogger(loggerId);
+    
+    log( logger, logLevel, msg);
+  }
+
+  @MJI
+  public static void log__Ljava_lang_String_2ILjava_lang_String_2_3Ljava_lang_Object_2__V (MJIEnv env, int clsObjRef,
+      int loggerIdRef, int logLevel, int fmtRef, int argsRef){
+    String loggerId = env.getStringObject(loggerIdRef);
+    String fmt = env.getStringObject(fmtRef);
+    JPFLogger logger = JPF.getLogger(loggerId);
+
+    int[] argRefs = env.getReferenceArrayObject( argsRef);
+    Object[] args = new Object[argRefs.length];
+    for (int i=0; i<args.length; i++){
+      ElementInfo eiArg = env.getElementInfo(argRefs[i]);
+      if (eiArg.isStringObject()){
+        args[i] = env.getStringObject(argRefs[i]);
+      } else if (eiArg.isBoxObject()){
+        args[i] = eiArg.asBoxObject(); 
+      } else {
+        args[i] = eiArg.toString();
+      }
+    }
+    
+    String msg = String.format(fmt, args);
+    
+    log( logger, logLevel, msg);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/JenkinsStateSet.java b/src/main/gov/nasa/jpf/vm/JenkinsStateSet.java
new file mode 100644 (file)
index 0000000..8f68716
--- /dev/null
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+//
+// Contributed by Peter C. Dillinger and the Georgia Tech Research Corporation
+//
+// Portions drawn from public domain work by Bob Jenkins, May 2006
+//
+// Modified by Peter C. Dillinger working under Mission Critical Technologies
+//
+
+//import gov.nasa.jpf.util.LongVector;
+
+/**
+ * Implements StateSet based on Jenkins hashes.
+ */
+public class JenkinsStateSet extends SerializingStateSet {
+  static final double MAX_LOAD = 0.7;
+  static final int INIT_SIZE = 65536;
+
+  
+  int lastStateId = -1;
+
+  //LongVector fingerprints;
+  long[] fingerprints;
+
+  int[] hashtable;
+
+  int nextRehash;
+
+  public JenkinsStateSet() {
+    lastStateId = -1;
+    hashtable = new int[INIT_SIZE];
+    nextRehash = (int) (MAX_LOAD * INIT_SIZE);
+    
+    //fingerprints = new LongVector(nextRehash / 2);
+    fingerprints = new long[nextRehash/2];
+    
+  }
+  
+  @Override
+  public int size () {
+    return lastStateId + 1;
+  }
+  public static long longLookup3Hash(int[] val) {
+    // Jenkins' LOOKUP3 hash  (May 2006)
+    int a = 0x510fb60d;
+    int b = 0xa4cb30d9 + (val.length);
+    int c = 0x9e3779b9;
+
+    int i;
+    int max = val.length - 2;
+    for (i = 0; i < max; i += 3) {
+      a += val[i];
+      b += val[i + 1];
+      c += val[i + 2];
+      a -= c;  a ^= (c << 4) ^ (c >>> 28);  c += b;
+      b -= a;  b ^= (a << 6) ^ (a >>> 26);  a += c;
+      c -= b;  c ^= (b << 8) ^ (b >>> 24);  b += a;
+      a -= c;  a ^= (c << 16)^ (c >>> 16);  c += b;
+      b -= a;  b ^= (a << 19)^ (a >>> 13);  a += c;
+      c -= b;  c ^= (b << 4) ^ (b >>> 28);  b += a;
+    }
+    switch (val.length - i) {
+    case 2:
+      c += val[val.length - 2];
+      b += val[val.length - 1];
+      break;
+    case 1:
+      b += val[val.length - 1];
+      break;
+    }
+    c ^= b; c -= (b << 14) ^ (b >>> 18);
+    a ^= c; a -= (c << 11) ^ (c >>> 21);
+    b ^= a; b -= (a << 25) ^ (a >>>  7);
+    c ^= b; c -= (b << 16) ^ (b >>> 16);
+    a ^= c; a -= (c <<  4) ^ (c >>> 28);
+    b ^= a; b -= (a << 14) ^ (a >>> 18);
+    c ^= b; c -= (b << 24) ^ (b >>>  8);
+    
+    return ((long)c << 32) ^ b ^ a;
+  }
+  
+  
+  @Override
+  public int add (int[] val) {
+    long hash = longLookup3Hash(val); // this is the expensive part
+    int i;
+    
+    // hash table lookup & add; open-addressed, double hashing
+    int mask = hashtable.length - 1;
+    int idx = (int)(hash >> 32) & mask;
+    int delta = (int)hash | 1; // must be odd!
+    int oidx = idx;
+
+    while (hashtable[idx] != 0) {
+      int id = hashtable[idx] - 1; // in table, 1 higher
+      //if (fingerprints.get(id) == hash) {
+      if (fingerprints[id] == hash){
+        return id;
+      }
+      idx = (idx + delta) & mask;
+      assert (idx != oidx); // should never wrap around
+    }
+    assert (hashtable[idx] == 0); // should never fill up
+
+    if (lastStateId >= nextRehash) { // too full
+      hashtable = null;
+      // run GC here?
+      hashtable = new int[(mask + 1) << 1];
+      mask = hashtable.length - 1;
+      nextRehash = (int) (MAX_LOAD * mask);
+
+      for (i = 0; i <= lastStateId; i++) {
+        //long h = fingerprints.get(i);
+        long h = fingerprints[i];
+        idx = (int)(h >> 32) & mask;
+        delta = (int)h | 1;
+        while (hashtable[idx] != 0) { // we know enough slots exist
+          idx = (idx + delta) & mask;
+        }
+        hashtable[idx] = i + 1; // in table, add 1
+      }
+      // done with rehash; now get idx to empty slot
+      idx = (int)(hash >> 32) & mask;
+      delta = (int)hash | 1; // must be odd!
+      while (hashtable[idx] != 0) { // we know enough slots exist and state is
+                                    // new
+        idx = (idx + delta) & mask;
+      }
+    } else {
+      // idx already pointing to empty slot
+    }
+
+    //--- only reached if state is new
+    
+    lastStateId++;
+    hashtable[idx] = lastStateId + 1; // in table, add 1
+
+    //fingerprints.set(lastStateId, hash);
+    try { // this happens rarely enough to save on nominal branch instructions
+      fingerprints[lastStateId] = hash;
+    } catch (ArrayIndexOutOfBoundsException ix){
+      growFingerprint(lastStateId);
+      fingerprints[lastStateId] = hash;      
+    }
+    
+    return lastStateId;
+  }
+  
+  void growFingerprint (int minSize){
+    // we don't try to be fancy here
+    int newSize = fingerprints.length *2;
+    if (newSize < minSize) {
+      newSize = minSize;
+    }
+    
+    long[] newFingerprints = new long[newSize];
+    System.arraycopy( fingerprints, 0, newFingerprints, 0, fingerprints.length);
+    fingerprints = newFingerprints;
+  }
+  
+  /**
+   * Main for testing speed, mostly.
+   */
+  public static void main(String[] args) {
+    try {
+      int vlen = Integer.parseInt(args[0]);
+      int adds = Integer.parseInt(args[1]);
+      int queries = Integer.parseInt(args[2]);
+      if (queries > adds) {
+        queries = adds;
+        System.err.println("Truncating queries to " + queries);
+      }
+      
+      int[] v = new int[vlen];
+      int i;
+      for (i = 0; i < vlen; i++) {
+        v[i] = i - 42;
+      }
+      
+      JenkinsStateSet set = new JenkinsStateSet();
+      
+      long t1 = System.currentTimeMillis();
+      for (i = 0; i < adds; i++) {
+        v[0] = i * 3;
+        set.add(v);
+        assert set.size() == i+1;
+      }
+      
+      for (i = 0; i < queries; i++) {
+        v[0] = i * 3;
+        set.add(v);
+        assert set.size() == adds;
+      }
+      long t2 = System.currentTimeMillis();
+      System.out.println("duration: " + (t2 - t1));
+      
+      
+    } catch (RuntimeException re) {
+      re.printStackTrace();
+      System.err.println("args:  vector_length  #adds  #queries");
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/KernelState.java b/src/main/gov/nasa/jpf/vm/KernelState.java
new file mode 100644 (file)
index 0000000..01d3b70
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import java.util.Iterator;
+import java.util.Stack;
+
+
+/**
+ * This class represents the SUT program state (statics, heap and threads)
+ */
+public class KernelState implements Restorable<KernelState> {
+
+  /** The area containing the heap */
+  public Heap heap;
+
+  /** The list of the threads */
+  public ThreadList threads;
+
+  /** the list of the class loaders */
+  public ClassLoaderList classLoaders;
+
+  /**
+   * current listeners waiting for notification of next change.
+   */
+  private Stack<ChangeListener> listeners = new Stack<ChangeListener>();
+
+
+  static class KsMemento implements Memento<KernelState> {
+    // note - order does matter: threads need to be restored before the heap
+    Memento<ThreadList> threadsMemento;
+    Memento<ClassLoaderList> cloadersMemento;
+    Memento<Heap> heapMemento;
+
+    KsMemento (KernelState ks){
+      threadsMemento = ks.threads.getMemento();
+      cloadersMemento = ks.classLoaders.getMemento();
+      heapMemento = ks.heap.getMemento();
+    }
+
+    @Override
+       public KernelState restore (KernelState ks) {
+      // those are all in-situ objects, no need to set them in ks
+      threadsMemento.restore(ks.threads);
+      cloadersMemento.restore(ks.classLoaders);
+      heapMemento.restore(ks.heap);
+
+      return ks;
+    }
+  }
+
+  /**
+   * Creates a new kernel state object.
+   */
+  public KernelState (Config config) {
+    Class<?>[] argTypes = { Config.class, KernelState.class };
+    Object[] args = { config, this };
+
+    classLoaders = new ClassLoaderList();  
+    heap = config.getEssentialInstance("vm.heap.class", Heap.class, argTypes, args);
+    threads = config.getEssentialInstance("vm.threadlist.class", ThreadList.class, argTypes, args);
+  }
+
+  @Override
+  public Memento<KernelState> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  public Memento<KernelState> getMemento(){
+    return new KsMemento(this);
+  }
+
+  /**
+   * Adds the given loader to the list of existing class loaders. 
+   */
+  public void addClassLoader(ClassLoaderInfo cl) {
+    classLoaders.add(cl);
+  }
+
+  /**
+   * Returns the ClassLoader with the given globalId
+   */
+  protected ClassLoaderInfo getClassLoader(int gid) {
+    Iterator<ClassLoaderInfo> it = classLoaders.iterator();
+
+    while(it.hasNext()) {
+      ClassLoaderInfo cl = it.next();
+      if(cl.getId() == gid) {
+        return cl;
+      }
+    }
+
+    return null;
+  }
+
+  public Heap getHeap() {
+    return heap;
+  }
+
+  public ThreadList getThreadList() {
+    return threads;
+  }
+
+  public ClassLoaderList getClassLoaderList() {
+    return classLoaders;
+  }
+  
+  /**
+   * interface for getting notified of changes to KernelState and everything
+   * "below" it.
+   */
+  public interface ChangeListener {
+    void kernelStateChanged(KernelState ks);
+  }
+
+  /**
+   * called by internals to indicate a change in KernelState.  list of listeners
+   * is emptied.
+   */
+  public void changed() {
+    while (!listeners.empty()) {
+      listeners.pop().kernelStateChanged(this);
+    }
+  }
+
+  /**
+   * push a listener for notification of the next change.  further notification
+   * requires re-pushing.
+   */
+  public void pushChangeListener(ChangeListener cl) {
+    if (cl instanceof IncrementalChangeTracker && listeners.size() > 0) {
+      for (ChangeListener l : listeners) {
+        if (l instanceof IncrementalChangeTracker) {
+          throw new IllegalStateException("Only one IncrementalChangeTracker allowed!");
+        }
+      }
+    }
+    listeners.push(cl);
+  }
+
+  public int getThreadCount () {
+    return threads.length();
+  }
+
+  public void gc () {
+        
+    heap.gc();
+
+    // we might have stored stale references in live objects
+    // (ElementInfos on the heap have already been cleaned up in the gc)
+    cleanUpDanglingStaticReferences();
+  }
+
+  
+  
+  private void cleanUpDanglingStaticReferences() {
+    for(ClassLoaderInfo cl: classLoaders) {
+      Statics sa = cl.getStatics();
+      sa.cleanUpDanglingReferences(heap);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/LoadOnJPFRequired.java b/src/main/gov/nasa/jpf/vm/LoadOnJPFRequired.java
new file mode 100644 (file)
index 0000000..52331c9
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * This is used where a customized classloader loads a class, and needs
+ * to resolve it. To do the resolve, a standard VM invokes the classloader 
+ * loadClass() method on the superclass. Therefore to mimic this JPF need
+ * to do a round trip back and forth to JPF, to execute the user implemented
+ * user code.
+ */
+public class LoadOnJPFRequired extends RuntimeException {
+  String className;
+
+  public LoadOnJPFRequired (String className){
+    this.className = className;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/LocalVarInfo.java b/src/main/gov/nasa/jpf/vm/LocalVarInfo.java
new file mode 100644 (file)
index 0000000..cb6c7e2
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+public class LocalVarInfo  extends InfoObject {
+  private final String name;
+  private String type;       // lazy initialized FQN according to JLS 6.7 (e.g. "int", "x.Y[]")
+  private final String signature;  // e.g. "I", "[Lx/Y;"
+  private final String genericSignature;  // non-type erased generic signature(s)
+  private final int    startPC;
+  private final int    endPC;
+  private final int slotIndex;
+    
+  public LocalVarInfo(String name, String signature, String genericSignature, int startPC, int endPC, int slotIndex){
+   
+    this.name             = name;
+    this.signature        = signature;
+    this.genericSignature = genericSignature;
+    this.startPC          = startPC;
+    this.endPC            = endPC;
+    this.slotIndex        = slotIndex;
+  }
+    
+  @Override
+  public String toString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append("LocalVarInfo[");
+    sb.append("name=\"");
+    sb.append(name);
+    sb.append("\",signature=\"");
+    sb.append(signature);
+    sb.append("\",startPC=");
+    sb.append(startPC);
+    sb.append(",endPC=");
+    sb.append(endPC);
+    sb.append(",slotIndex=");
+    sb.append(slotIndex);
+    sb.append("]");
+    
+    return sb.toString();
+  }
+  
+  public String getName() {
+    return name; 
+  }
+
+  public String getType() {
+    if (type == null){
+      type = Types.getTypeName(signature);
+    }
+    return type; 
+  }
+
+  public String getSignature() {
+    return signature;
+  }
+
+  public String getGenericSignature() {
+    return genericSignature;
+  }
+  
+  public int getStartPC() {
+    return startPC; 
+  }
+     
+  public int getLength() {
+    return endPC - startPC +1;
+  }
+
+  public int getSlotIndex() {
+    return slotIndex;
+  }
+
+  public boolean matches (String name, int pc){
+    return (startPC <= pc && endPC >= pc && this.name.equals(name));
+  }
+
+  public boolean matches (int slotIdx, int pc){
+    return (slotIdx == slotIndex) && (pc >= startPC) && (pc <= endPC);
+  }
+
+  public boolean isNumeric(){
+    char c = signature.charAt(0);
+    return (c == 'B' || c == 'S' || c == 'I' || c == 'J' || c == 'F' || c == 'D');
+  }
+
+  public boolean isBoolean(){
+    char c = signature.charAt(0);
+    return (c == 'Z');
+  }  
+
+  public int getSlotSize(){
+    char c = signature.charAt(0);
+    if (c == 'J' || c == 'D'){
+      return 2;
+    } else {
+      return 1;
+    }
+  }
+
+  public boolean isFloatingPoint(){
+    char c = signature.charAt(0);
+    return (c == 'F' || c == 'D');
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/LockSetThresholdFli.java b/src/main/gov/nasa/jpf/vm/LockSetThresholdFli.java
new file mode 100644 (file)
index 0000000..886d8d1
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a threshold FieldLockInfo with a set of lock candidates
+ * 
+ * this is the destructive update version. Override singleLockThresholdFli() and lockSetthresholdFli() for
+ * a persistent version
+ */
+public class LockSetThresholdFli extends ThresholdFieldLockInfo {
+
+  protected int[] lockRefSet;
+
+  // this is only used once during prototype generation
+  public LockSetThresholdFli(ThreadInfo ti, int[] currentLockRefs, int checkThreshold) {
+    super(checkThreshold);
+
+    tiLastCheck = ti;
+    lockRefSet = currentLockRefs;
+  }
+
+  @Override
+  protected int[] getCandidateLockSet() {
+    return lockRefSet;
+  }
+
+  /**
+   * override this for search global FieldLockInfos
+   */
+  protected SingleLockThresholdFli singleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks) {
+    return new SingleLockThresholdFli(ti, lockRef, remainingChecks);
+  }
+  
+  protected LockSetThresholdFli lockSetThresholdFli (ThreadInfo ti, int[] lockRefs, int remainingChecks){
+    this.lockRefSet = lockRefs;
+    this.tiLastCheck = ti;
+    this.remainingChecks = remainingChecks;
+    
+    return this;
+  }
+  
+  @Override
+  public FieldLockInfo checkProtection(ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    int[] currentLockRefs = ti.getLockedObjectReferences();
+    int nLocks = currentLockRefs.length;
+    int nRemaining = Math.max(0, remainingChecks--);
+
+    if (nLocks == 0) { // no current locks, so intersection is empty
+      checkFailedLockAssumption(ti, ei, fi);
+      return empty;
+
+    } else { // we had a lock set, and there currently is at least one lock held
+      int l = 0;
+      int[] newLset = new int[lockRefSet.length];
+
+      for (int i = 0; i < nLocks; i++) { // get the set intersection
+        int leidx = currentLockRefs[i];
+
+        for (int j = 0; j < lockRefSet.length; j++) {
+          if (lockRefSet[j] == leidx) {
+            newLset[l++] = leidx;
+            break; // sets don't contain duplicates
+          }
+        }
+      }
+
+      if (l == 0) { // intersection empty
+        checkFailedLockAssumption(ti, ei, fi);
+        return empty;
+
+      } else if (l == 1) { // only one candidate left 
+        return singleLockThresholdFli(ti, newLset[0], nRemaining);
+
+      } else if (l < newLset.length) { // candidate set did shrink
+        int[] newLockRefSet = new int[l];
+        System.arraycopy(newLset, 0, newLockRefSet, 0, l);
+        return lockSetThresholdFli(ti, newLockRefSet, nRemaining);
+
+      } else {
+        return lockSetThresholdFli(ti, lockRefSet, nRemaining);
+      }
+    }
+  }
+
+  /**
+   * only called at the end of the gc on all live objects. The recycled ones are
+   * either already nulled in the heap, or are not marked as live
+   */
+  @Override
+  public FieldLockInfo cleanUp(Heap heap) {
+    int[] newSet = null;
+    int l = 0;
+
+    if (lockRefSet != null) {
+      for (int i = 0; i < lockRefSet.length; i++) {
+        ElementInfo ei = heap.get(lockRefSet[i]);
+
+        if (!heap.isAlive(ei)) { // we got a stale one, so we have to change us
+          if (newSet == null) { // first one, copy everything up to it
+            newSet = new int[lockRefSet.length - 1];
+            if (i > 0) {
+              System.arraycopy(lockRefSet, 0, newSet, 0, i);
+              l = i;
+            }
+          }
+
+        } else {
+          if (newSet != null) { // we already had a dangling ref, now copy the live ones
+            newSet[l++] = lockRefSet[i];
+          }
+        }
+      }
+    }
+
+    if (l == 1) {
+      assert (newSet != null);
+      return new SingleLockThresholdFli(tiLastCheck, newSet[0], remainingChecks);
+
+    } else {
+      if (newSet != null) {
+        if (l == newSet.length) { // we just had one stale ref
+          lockRefSet = newSet;
+        } else { // several stales - make a new copy
+          if (l == 0) {
+            return empty;
+          } else {
+            lockRefSet = new int[l];
+            System.arraycopy(newSet, 0, lockRefSet, 0, l);
+          }
+        }
+      }
+      return this;
+    }
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append("LockSetThresholdFli {");
+    sb.append("remainingChecks=");
+    sb.append(remainingChecks);
+    sb.append(",lset=[");
+    if (lockRefSet != null) {
+      for (int i = 0; i < lockRefSet.length; i++) {
+        if (i > 0) {
+          sb.append(',');
+        }
+        sb.append(lockRefSet[i]);
+      }
+    }
+    sb.append("]}");
+
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/LongArrayFields.java b/src/main/gov/nasa/jpf/vm/LongArrayFields.java
new file mode 100644 (file)
index 0000000..51c7b26
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for long[] objects
+ */
+public class LongArrayFields extends ArrayFields {
+
+  long[] values;
+
+  public LongArrayFields (int length) {
+    values = new long[length];
+  }
+
+  @Override
+  public long[] asLongArray() {
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    LongArrayFields a = (LongArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 8;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendBits(values);
+  }
+
+  @Override
+  public LongArrayFields clone(){
+    LongArrayFields f = (LongArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof LongArrayFields) {
+      LongArrayFields other = (LongArrayFields)o;
+
+      long[] v = values;
+      long[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void setLongValue (int pos, long newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public long getLongValue (int pos) {
+    return values[pos];
+  }
+
+
+  @Override
+  public void hash(HashData hd) {
+    long[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/LongChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/LongChoiceGenerator.java
new file mode 100644 (file)
index 0000000..b118996
--- /dev/null
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+
+/**
+ * ChoiceGenerator for Long values - this is only an interface so that
+ * we can use it for type checks of implementors that use their own
+ * generic hierarchy
+ */
+public interface LongChoiceGenerator extends ChoiceGenerator<Long> {
+}
diff --git a/src/main/gov/nasa/jpf/vm/LongFieldInfo.java b/src/main/gov/nasa/jpf/vm/LongFieldInfo.java
new file mode 100644 (file)
index 0000000..11c9c09
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+
+/**
+ *
+ */
+public class LongFieldInfo extends DoubleSlotFieldInfo {
+  long init;
+
+  public LongFieldInfo (String name, int modifiers) {
+    super(name, "J", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Long){
+      cv = constValue;
+      init = (Long)constValue;
+
+    } else {
+      throw new JPFException("illegal long ConstValue=" + constValue);
+    }
+  }
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setLongValue( storageOffset, init);
+  }
+
+  @Override
+  public int getStorageSize() {
+    return 2;
+  }
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType() {
+    return LongChoiceGenerator.class;
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    long v = f.getLongValue(storageOffset);
+    return Long.toString(v);
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    long v = f.getLongValue(storageOffset);
+    return new Long(v);
+  }
+
+  @Override
+  public boolean isLongField(){
+    return true;
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/MJIEnv.java b/src/main/gov/nasa/jpf/vm/MJIEnv.java
new file mode 100644 (file)
index 0000000..2bac12a
--- /dev/null
@@ -0,0 +1,1785 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import com.sun.org.apache.bcel.internal.generic.InstructionConstants;
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.JPFListener;
+import gov.nasa.jpf.vm.serialize.UnknownJPFClass;
+
+import java.util.Date;
+import java.util.Locale;
+
+
+/**
+ * MJIEnv is the call environment for "native" methods, i.e. code that
+ * is executed by the VM, not by JPF.
+ *
+ * Since library abstractions are supposed to be "user code", we provide
+ * this class as a (little bit of) insulation towards the inner JPF workings.
+ *
+ * There are two APIs exported by this class. The public methods (like
+ * getStringObject) don't expose JPF internals, and can be used from non
+ * gov.nasa.jpf.vm NativePeer classes). The rest is package-default
+ * and can be used to fiddle around as much as you like to (if you are in
+ * the ..jvm package)
+ *
+ * Note that MJIEnv objects are now per-ThreadInfo (i.e. the variable
+ * call envionment only includes MethodInfo and ClassInfo), which means
+ * MJIEnv can be used in non-native methods (but only carefully, if you
+ * don't need mi or ciMth).
+ *
+ * Note also this only works because we are not getting recursive in
+ * native method calls. In fact, the whole DirectCallStackFrame / repeatTopInstruction
+ * mechanism is there to turn logial recursion (JPF calling native, calling
+ * JPF, calling native,..) into iteration. Otherwise we couldn't backtrack
+ */
+public class MJIEnv {
+  public static final int NULL = 0;
+
+  VM                     vm;
+  ClassInfo               ciMth;  // the ClassInfo of the method this is called from
+  MethodInfo              mi;
+  ThreadInfo              ti;
+  Heap                    heap;
+
+  // those are various attributes set by the execution. note that
+  // NativePeer.invoke never gets recursive in a roundtrip (at least if
+  // used correctly, so we don't have to be afraid to overwrite any of these
+  boolean                 repeat;
+  Object                  returnAttr;
+
+  // exception to be thrown upon return from native method
+  // NOTE: this is only transient - don't expect this to be preserved over
+  // transition boundaries
+  int                     exceptionRef;
+
+  MJIEnv (ThreadInfo ti) {
+    this.ti = ti;
+
+    // set those here so that we don't have an inconsistent state between
+    // creation of an MJI object and the first native method call in
+    // this thread (where any access to the heap or sa would bomb)
+    vm = ti.getVM();
+    heap = vm.getHeap();
+
+    exceptionRef = NULL;
+  }
+
+  public VM getVM () {
+    return vm;
+  }
+
+  public JPF getJPF () {
+    return vm.getJPF();
+  }
+
+  public boolean isBigEndianPlatform(){
+    return vm.isBigEndianPlatform();
+  }
+  
+  public void addListener (JPFListener l){
+    vm.getJPF().addListener(l);
+  }
+
+  public void removeListener (JPFListener l){
+    vm.getJPF().removeListener(l);
+  }
+
+  public Config getConfig() {
+    return vm.getConfig();
+  }
+
+  public void gc() {
+    heap.gc();
+  }
+
+  public void forceState (){
+    getSystemState().setForced(true);
+  }
+
+  public void ignoreTransition () {
+    getSystemState().setIgnored(true);
+  }
+
+  public boolean isArray (int objref) {
+    return heap.get(objref).isArray();
+  }
+
+  public int getArrayLength (int objref) {
+    if (isArray(objref)) {
+      return heap.get(objref).arrayLength();
+    } else {
+      throwException("java.lang.IllegalArgumentException");
+
+      return 0;
+    }
+  }
+
+  public String getArrayType (int objref) {
+    return heap.get(objref).getArrayType();
+  }
+
+  public int getArrayTypeSize (int objref) {
+    return Types.getTypeSize(getArrayType(objref));
+  }
+
+  //=== various attribute accessors ============================================
+  // we only support some attribute APIs here, since MJIEnv adds little value
+  // other than hiding the ElementInfo access. If the client already has
+  // an ElementInfo reference, it should use that one to retrieve/enumerate/set
+  // attributes since this avoids repeated Heap.get() calls
+  
+  //--- object attributes
+
+  public boolean hasObjectAttr (int objref){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasObjectAttr();
+    }
+
+    return false;
+  }
+
+  public boolean hasObjectAttr (int objref, Class<?> type){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasObjectAttr(type);
+    }
+
+    return false;    
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */  
+  public Object getObjectAttr (int objref){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.getObjectAttr();
+    }
+    return null;
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setObjectAttr (int objref, Object a){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      ei.setObjectAttr(a);
+    }
+  }
+
+  public void addObjectAttr (int objref, Object a){
+    if (objref != NULL){
+      ElementInfo ei = heap.getModifiable(objref);
+      ei.addObjectAttr(a);
+    }
+  }
+
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getObjectAttr (int objref, Class<T> attrType){
+    ElementInfo ei = heap.get(objref);
+    return ei.getObjectAttr(attrType);
+  }
+  
+  //--- field attributes
+
+  public boolean hasFieldAttr (int objref){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasFieldAttr();
+    }
+
+    return false;
+  }
+  
+  public boolean hasFieldAttr (int objref, Class<?> type){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasFieldAttr(type);
+    }
+
+    return false;    
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */  
+  public Object getFieldAttr (int objref, String fname){
+    ElementInfo ei = heap.get(objref);
+    FieldInfo fi = ei.getFieldInfo(fname);
+    if (fi != null){
+      return ei.getFieldAttr(fi);
+    } else {
+      throw new JPFException("no such field: " + fname);
+    }
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setFieldAttr (int objref, String fname, Object a){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      FieldInfo fi = ei.getFieldInfo(fname);
+      ei.setFieldAttr(fi, a);
+    }
+  }
+
+  public void addFieldAttr (int objref, String fname, Object a){
+    if (objref != NULL){
+      ElementInfo ei = heap.getModifiable(objref);
+      FieldInfo fi = ei.getFieldInfo(fname);
+      ei.addFieldAttr(fi, a);
+    }
+  }
+
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getFieldAttr (int objref, String fname, Class<T> attrType){
+    ElementInfo ei = heap.get(objref);
+    FieldInfo fi = ei.getFieldInfo(fname);
+    if (fi != null){
+      return ei.getFieldAttr(fi, attrType);
+    } else {
+      throw new JPFException("no such field: " + fname);
+    }
+  }
+
+  
+  //--- element attrs
+
+  public boolean hasElementdAttr (int objref){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasElementAttr();
+    }
+
+    return false;
+  }
+  
+  public boolean hasElementAttr (int objref, Class<?> type){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.hasElementAttr(type);
+    }
+
+    return false;    
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at a time, or check/process result with ObjectList
+   */  
+  public Object getElementAttr (int objref, int idx){
+    ElementInfo ei = heap.get(objref);
+    return ei.getElementAttr(idx);
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at a time
+   *  - you obtained the value you set by a previous getXAttr()
+   *  - you constructed a multi value list with ObjectList.createList()
+   */
+  public void setElementAttr (int objref, int idx, Object a){
+    ElementInfo ei = heap.get(objref);
+    ei.setElementAttr(idx, a);
+  }
+
+  public void addElementAttr (int objref, int idx, Object a){
+    ElementInfo ei = heap.getModifiable(objref);
+    ei.addElementAttr(idx, a);
+  }
+
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getElementAttr (int objref, int idx, Class<T> attrType){
+    if (objref != NULL){
+      ElementInfo ei = heap.get(objref);
+      return ei.getElementAttr(idx, attrType);
+    }
+    return null;
+  }
+
+  
+
+  // == end attrs ==  
+
+
+  
+  // the instance field setters
+  public void setBooleanField (int objref, String fname, boolean val) {
+    heap.getModifiable(objref).setBooleanField(fname, val);
+  }
+
+  public boolean getBooleanField (int objref, String fname) {
+    return heap.get(objref).getBooleanField(fname);
+  }
+
+  public boolean getBooleanArrayElement (int objref, int index) {
+    return heap.get(objref).getBooleanElement(index);
+  }
+
+  public void setBooleanArrayElement (int objref, int index, boolean value) {
+    heap.getModifiable(objref).setBooleanElement(index, value);
+  }
+
+
+  public void setByteField (int objref, String fname, byte val) {
+    heap.getModifiable(objref).setByteField(fname, val);
+  }
+
+  public byte getByteField (int objref, String fname) {
+    return heap.get(objref).getByteField(fname);
+  }
+
+  public void setCharField (int objref, String fname, char val) {
+    heap.getModifiable(objref).setCharField(fname, val);
+  }
+
+  public char getCharField (int objref, String fname) {
+    return heap.get(objref).getCharField(fname);
+  }
+
+  public void setDoubleField (int objref, String fname, double val) {
+    heap.getModifiable(objref).setDoubleField(fname, val);
+  }
+
+  public double getDoubleField (int objref, String fname) {
+    return heap.get(objref).getDoubleField(fname);
+  }
+
+  public void setFloatField (int objref, String fname, float val) {
+    heap.getModifiable(objref).setFloatField(fname, val);
+  }
+
+  public float getFloatField (int objref, String fname) {
+    return heap.get(objref).getFloatField(fname);
+  }
+
+
+  public void setByteArrayElement (int objref, int index, byte value) {
+    heap.getModifiable(objref).setByteElement(index, value);
+  }
+
+  public byte getByteArrayElement (int objref, int index) {
+    return heap.get(objref).getByteElement(index);
+  }
+
+  public void setCharArrayElement (int objref, int index, char value) {
+    heap.getModifiable(objref).setCharElement(index, value);
+  }
+
+  public void setIntArrayElement (int objref, int index, int value) {
+    heap.getModifiable(objref).setIntElement(index, value);
+  }
+
+  public void setShortArrayElement (int objref, int index, short value) {
+    heap.getModifiable(objref).setShortElement(index, value);
+  }
+
+  public void setFloatArrayElement (int objref, int index, float value) {
+    heap.getModifiable(objref).setFloatElement(index, value);
+  }
+
+  public float getFloatArrayElement (int objref, int index) {
+    return heap.get(objref).getFloatElement(index);
+  }
+
+  public double getDoubleArrayElement (int objref, int index) {
+    return heap.get(objref).getDoubleElement(index);
+  }
+  public void setDoubleArrayElement (int objref, int index, double value) {
+    heap.getModifiable(objref).setDoubleElement(index, value);
+  }
+
+  public short getShortArrayElement (int objref, int index) {
+    return heap.get(objref).getShortElement(index);
+  }
+
+  public int getIntArrayElement (int objref, int index) {
+    return heap.get(objref).getIntElement(index);
+  }
+
+  public char getCharArrayElement (int objref, int index) {
+    return heap.get(objref).getCharElement(index);
+  }
+
+  public void setIntField (int objref, String fname, int val) {
+    ElementInfo ei = heap.getModifiable(objref);
+    ei.setIntField(fname, val);
+  }
+
+  // these two are the workhorses
+  public void setDeclaredIntField (int objref, String refType, String fname, int val) {
+    ElementInfo ei = heap.getModifiable(objref);
+    ei.setDeclaredIntField(fname, refType, val);
+  }
+
+  public int getIntField (int objref, String fname) {
+    ElementInfo ei = heap.get(objref);
+    return ei.getIntField(fname);
+  }
+
+  public int getDeclaredIntField (int objref, String refType, String fname) {
+    ElementInfo ei = heap.get(objref);
+    return ei.getDeclaredIntField(fname, refType);
+  }
+
+  // these two are the workhorses
+  public void setDeclaredReferenceField (int objref, String refType, String fname, int val) {
+    ElementInfo ei = heap.getModifiable(objref);
+    ei.setDeclaredReferenceField(fname, refType, val);
+  }
+
+  public void setReferenceField (int objref, String fname, int ref) {
+     ElementInfo ei = heap.getModifiable(objref);
+     ei.setReferenceField(fname, ref);
+  }
+
+  public int getReferenceField (int objref, String fname) {
+    ElementInfo ei = heap.get(objref);
+    return ei.getReferenceField(fname);
+  }
+
+  // we need this in case of a masked field
+  public int getReferenceField (int objref, FieldInfo fi) {
+    ElementInfo ei = heap.get(objref);
+    return ei.getReferenceField(fi);
+  }
+
+  public String getStringField (int objref, String fname){
+    int ref = getReferenceField(objref, fname);
+    return getStringObject(ref);
+  }
+
+  // the box object accessors (should probably test for the appropriate class)
+  public boolean getBooleanValue (int objref) {
+    return getBooleanField(objref, "value");
+  }
+
+  public byte getByteValue (int objref) {
+    return getByteField(objref, "value");
+  }
+
+  public char getCharValue (int objref) {
+    return getCharField(objref, "value");
+  }
+
+  public short getShortValue (int objref) {
+    return getShortField(objref, "value");
+  }
+
+  public int getIntValue (int objref) {
+    return getIntField(objref, "value");
+  }
+
+  public long getLongValue (int objref) {
+    return getLongField(objref, "value");
+  }
+
+  public float getFloatValue (int objref) {
+    return getFloatField(objref, "value");
+  }
+
+  public double getDoubleValue (int objref) {
+    return getDoubleField(objref, "value");
+  }
+
+
+  public void setLongArrayElement (int objref, int index, long value) {
+    heap.getModifiable(objref).setLongElement(index, value);
+  }
+
+  public long getLongArrayElement (int objref, int index) {
+    return heap.get(objref).getLongElement(index);
+  }
+
+  public void setLongField (int objref, String fname, long val) {
+    ElementInfo ei = heap.getModifiable(objref);
+    ei.setLongField(fname, val);
+  }
+
+//  public void setLongField (int objref, String refType, String fname, long val) {
+//    ElementInfo ei = heap.get(objref);
+//    ei.setLongField(fname, refType, val);
+//  }
+
+  public long getLongField (int objref, String fname) {
+    ElementInfo ei = heap.get(objref);
+    return ei.getLongField(fname);
+  }
+
+//  public long getLongField (int objref, String refType, String fname) {
+//    ElementInfo ei = heap.get(objref);
+//    return ei.getLongField(fname, refType);
+//  }
+
+  public void setReferenceArrayElement (int objref, int index, int eRef) {
+    heap.getModifiable(objref).setReferenceElement(index, eRef);
+  }
+
+  public int getReferenceArrayElement (int objref, int index) {
+    return heap.get(objref).getReferenceElement(index);
+  }
+
+  public void setShortField (int objref, String fname, short val) {
+    setIntField(objref, fname, /*(int)*/ val);
+  }
+
+  public short getShortField (int objref, String fname) {
+    return (short) getIntField(objref, fname);
+  }
+
+  /**
+   * NOTE - this doesn't support element type checks or overlapping in-array copy 
+   */
+  public void arrayCopy (int srcRef, int srcPos, int dstRef, int dstPos, int len){
+    ElementInfo eiSrc = heap.get(srcRef);
+    ElementInfo eiDst = heap.get(dstRef);
+    
+    eiDst.arrayCopy(eiSrc, srcPos, dstPos, len);
+  }
+  
+  public String getTypeName (int objref) {
+    return heap.get(objref).getType();
+  }
+
+  public boolean isInstanceOf (int objref, String clsName) {
+    ClassInfo ci = getClassInfo(objref);
+    return ci.isInstanceOf(clsName);
+  }
+  
+  public boolean isInstanceOf (int objref, ClassInfo cls) {
+    ClassInfo ci = getClassInfo(objref);
+    return ci.isInstanceOf(cls);
+  }
+
+  //--- the static field accessors
+  // NOTE - it is the callers responsibility to ensure the class is
+  // properly initialized, since calling <clinit> requires a roundtrip
+  // (i.e. cannot be done synchronously from one of the following methods)
+  
+  // <2do> this uses the current system CL, we should probably use an explicit CL argument
+  
+  public void setStaticBooleanField (String clsName, String fname,
+                                     boolean value) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setBooleanField(fname, value);
+  }
+  public void setStaticBooleanField (int clsObjRef, String fname, boolean val) {
+    ElementInfo cei = getStaticElementInfo(clsObjRef);
+    cei.setBooleanField(fname, val);
+  }
+  
+  public boolean getStaticBooleanField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getBooleanField(fname);
+  }
+
+  public void setStaticByteField (String clsName, String fname, byte value) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setByteField(fname, value);  }
+
+  public byte getStaticByteField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getByteField(fname);
+  }
+
+  public void setStaticCharField (String clsName, String fname, char value) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setCharField(fname, value);  }
+
+  public char getStaticCharField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getCharField(fname);
+  }
+
+  public void setStaticDoubleField (String clsName, String fname, double val) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setDoubleField(fname, val);
+  }
+
+  public double getStaticDoubleField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getDoubleField(fname);
+  }
+  
+  public double getStaticDoubleField (int clsObjRef, String fname) {
+    ElementInfo cei = getStaticElementInfo(clsObjRef);
+    return cei.getDoubleField(fname);
+  }
+
+  public double getStaticDoubleField (ClassInfo ci, String fname) {
+    ElementInfo ei = ci.getStaticElementInfo();
+    return ei.getDoubleField(fname);
+  }
+  
+  public void setStaticFloatField (String clsName, String fname, float val) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setFloatField(fname, val);
+  }
+
+  public float getStaticFloatField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getFloatField(fname);
+  }
+
+  public void setStaticIntField (String clsName, String fname, int val) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setIntField(fname, val);
+  }
+
+  public void setStaticIntField (int clsObjRef, String fname, int val) {
+    ElementInfo cei = getStaticElementInfo(clsObjRef);
+    cei.setIntField(fname, val);
+  }
+
+  public int getStaticIntField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getIntField(fname);
+  }
+  
+  public int getStaticIntField (int clsObjRef, String fname) {
+    ElementInfo cei = getStaticElementInfo(clsObjRef);
+    return cei.getIntField(fname);
+  }
+
+  public int getStaticIntField (ClassInfo ci, String fname) {
+    ElementInfo ei = ci.getStaticElementInfo();
+    return ei.getIntField(fname);
+  }
+
+  public void setStaticLongField (String clsName, String fname, long value) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    ci.getStaticElementInfo().setLongField(fname, value);
+  }
+
+  public void setStaticLongField (int clsObjRef, String fname, long val) {
+    ElementInfo cei = getModifiableStaticElementInfo(clsObjRef);
+    cei.setLongField(fname, val);
+  }
+
+  public long getStaticLongField (int clsRef, String fname) {
+    ClassInfo ci = getReferredClassInfo(clsRef);
+    return getStaticLongField(ci, fname);
+  }
+
+  public long getStaticLongField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return getStaticLongField(ci, fname);
+  }
+
+  public long getStaticLongField (ClassInfo ci, String fname){
+    ElementInfo ei = ci.getStaticElementInfo();
+    return ei.getLongField(fname);
+  }
+
+  public void setStaticReferenceField (String clsName, String fname, int objref) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+
+    // <2do> - we should REALLY check for type compatibility here
+    ci.getModifiableStaticElementInfo().setReferenceField(fname, objref);
+  }
+
+  public void setStaticReferenceField (int clsObjRef, String fname, int objref) {
+    ElementInfo cei = getModifiableStaticElementInfo(clsObjRef);
+
+    // <2do> - we should REALLY check for type compatibility here
+    cei.setReferenceField(fname, objref);
+  }
+
+  public int getStaticReferenceField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getReferenceField(fname);
+  }
+
+  public int getStaticReferenceField (int clsObjRef, String fname) {
+    ElementInfo cei = getStaticElementInfo(clsObjRef);
+    return cei.getReferenceField(fname);
+  }
+
+  public int getStaticReferenceField (ClassInfo ci, String fname){
+    return ci.getStaticElementInfo().getReferenceField(fname);
+  }
+
+  public short getStaticShortField (String clsName, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    return ci.getStaticElementInfo().getShortField(fname);
+  }
+
+  public char[] getStringChars (int objRef){
+    if (objRef != MJIEnv.NULL) {
+      ElementInfo ei = getElementInfo(objRef);
+      return ei.getStringChars();
+      
+    } else {
+      return null;
+    }
+    
+  }
+  
+  /**
+   * turn JPF String object into a VM String object
+   * (this is a method available for non gov..jvm NativePeer classes)
+   */
+  public String getStringObject (int objRef) {
+    if (objRef != MJIEnv.NULL) {
+      ElementInfo ei = getElementInfo(objRef);
+      return ei.asString();
+      
+    } else {
+      return null;
+    }
+  }
+
+  public String[] getStringArrayObject (int aRef){
+    String[] sa = null;
+     
+    if (aRef == NULL) return sa;
+
+    ClassInfo aci = getClassInfo(aRef);
+    if (aci.isArray()){
+      ClassInfo eci = aci.getComponentClassInfo();
+      if (eci.getName().equals("java.lang.String")){
+        int len = getArrayLength(aRef);
+        sa = new String[len];
+
+        for (int i=0; i<len; i++){
+          int sRef = getReferenceArrayElement(aRef,i);
+          sa[i] = getStringObject(sRef);
+        }
+
+        return sa;
+        
+      } else {
+        throw new IllegalArgumentException("not a String[] array: " + aci.getName());
+      }
+    } else {
+      throw new IllegalArgumentException("not an array reference: " + aci.getName());
+    }
+  }
+  
+  public Date getDateObject (int objref) {
+    if (objref != MJIEnv.NULL) {
+      ElementInfo ei = getElementInfo(objref);
+      if (ei.getClassInfo().getName().equals("java.util.Date")) {
+        // <2do> this is not complete yet
+        long fastTime = ei.getLongField("fastTime");
+        Date d = new Date(fastTime);
+        return d;
+      } else {
+        throw new JPFException("not a Date object reference: " + ei);
+      }
+    } else {
+      return null;
+    }
+    
+  }
+
+  public Object[] getArgumentArray (int argRef) {
+    Object[] args = null;
+    if (argRef == NULL) return args;
+
+    int nArgs = getArrayLength(argRef);
+    args = new Object[nArgs];
+
+    for (int i=0; i<nArgs; i++){
+      int aref = getReferenceArrayElement(argRef,i);
+      ClassInfo ci = getClassInfo(aref);
+      String clsName = ci.getName();
+      if (clsName.equals("java.lang.Boolean")){
+        args[i] = Boolean.valueOf(getBooleanField(aref,"value"));
+      } else if (clsName.equals("java.lang.Integer")){
+        args[i] = Integer.valueOf(getIntField(aref,"value"));
+      } else if (clsName.equals("java.lang.Double")){
+        args[i] = Double.valueOf(getDoubleField(aref,"value"));
+      } else if (clsName.equals("java.lang.String")){
+        args[i] = getStringObject(aref);
+      }
+    }
+
+    return args;
+  }
+
+  public Boolean getBooleanObject (int objref){
+    return Boolean.valueOf(getBooleanField(objref, "value"));
+  }
+
+  public Byte getByteObject (int objref){
+    return new Byte(getByteField(objref, "value"));
+  }
+
+  public Character getCharObject (int objref){
+    return new Character(getCharField(objref, "value"));
+  }
+
+  public Short getShortObject (int objref){
+    return new Short(getShortField(objref, "value"));
+  }
+
+  public Integer getIntegerObject (int objref){
+    return new Integer(getIntField(objref, "value"));
+  }
+
+  public Long getLongObject (int objref){
+    return new Long(getLongField(objref, "value"));
+  }
+
+  public Float getFloatObject (int objref){
+    return new Float(getFloatField(objref, "value"));
+  }
+
+  public Double getDoubleObject (int objref){
+    return new Double(getDoubleField(objref, "value"));
+  }
+
+  // danger - the returned arrays could be used to modify contents of stored objects
+
+  public byte[] getByteArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    byte[] a = ei.asByteArray();
+
+    return a;
+  }
+
+  public char[] getCharArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    char[] a = ei.asCharArray();
+
+    return a;
+  }
+
+  public short[] getShortArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    short[] a = ei.asShortArray();
+
+    return a;
+  }
+
+  public int[] getIntArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    int[] a = ei.asIntArray();
+
+    return a;
+  }
+
+  public long[] getLongArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    long[] a = ei.asLongArray();
+
+    return a;
+  }
+
+  public float[] getFloatArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    float[] a = ei.asFloatArray();
+
+    return a;
+  }
+
+  public double[] getDoubleArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    double[] a = ei.asDoubleArray();
+
+    return a;
+  }
+
+  public boolean[] getBooleanArrayObject (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    boolean[] a = ei.asBooleanArray();
+
+    return a;
+  }
+  
+  public int[] getReferenceArrayObject (int objref){
+    ElementInfo ei = getElementInfo(objref);
+    int[] a = ei.asReferenceArray();
+
+    return a;    
+  }
+
+  public boolean canLock (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+
+    return ei.canLock(ti);
+  }
+
+  public int newBooleanArray (int size) {
+    return heap.newArray("Z", size, ti).getObjectRef();
+  }
+
+  public int newByteArray (int size) {
+    return heap.newArray("B", size, ti).getObjectRef();
+  }
+
+  public int newByteArray (byte[] buf){
+    ElementInfo eiArray = heap.newArray("B", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setByteElement( i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newCharArray (int size) {
+    return heap.newArray("C", size, ti).getObjectRef();
+  }
+
+  public int newCharArray (char[] buf){
+    ElementInfo eiArray = heap.newArray("C", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setCharElement( i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newShortArray (int size) {
+    return heap.newArray("S", size, ti).getObjectRef();
+  }
+  
+  public int newShortArray (short[] buf){
+    ElementInfo eiArray = heap.newArray("S", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setShortElement(i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newDoubleArray (int size) {
+    return heap.newArray("D", size, ti).getObjectRef();
+  }
+
+  public int newDoubleArray (double[] buf){
+    ElementInfo eiArray =  heap.newArray("D", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setDoubleElement(i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newFloatArray (int size) {
+    return heap.newArray("F", size, ti).getObjectRef();
+  }
+  
+  public int newFloatArray (float[] buf){
+    ElementInfo eiArray =  heap.newArray("F", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setFloatElement( i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newIntArray (int size) {
+    return heap.newArray("I", size, ti).getObjectRef();
+  }
+
+  public int newIntArray (int[] buf){
+    ElementInfo eiArray = heap.newArray("I", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setIntElement( i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newLongArray (int size) {
+    return heap.newArray("J", size, ti).getObjectRef();
+  }
+
+  public int newLongArray (long[] buf){
+    ElementInfo eiArray = heap.newArray("J", buf.length, ti);
+    for (int i=0; i<buf.length; i++){
+      eiArray.setLongElement( i, buf[i]);
+    }
+    return eiArray.getObjectRef();
+  }
+
+  public int newObjectArray (String elementClsName, int size) {
+    if (!elementClsName.endsWith(";")) {
+      elementClsName = Types.getTypeSignature(elementClsName, false);
+    }
+
+    return heap.newArray(elementClsName, size, ti).getObjectRef();
+  }
+
+  public ElementInfo newElementInfo (ClassInfo ci) throws ClinitRequired {
+    if (ci.initializeClass(ti)){
+      throw new ClinitRequired(ci);
+    }
+
+    return heap.newObject(ci, ti);
+  }
+  
+  /**
+   * check if the ClassInfo is properly initialized
+   * if yes, create a new instance of it but don't call any ctor
+   * if no, throw a ClinitRequired exception
+   */
+  public int newObject (ClassInfo ci)  throws ClinitRequired {
+    ElementInfo ei = newElementInfo(ci);
+    return ei.getObjectRef();
+  }
+
+  /**
+   * this creates a new object without checking if the ClassInfo needs
+   * initialization. This is useful in a context that already
+   * is aware and handles re-execution
+   */
+  public int newObjectOfUncheckedClass (ClassInfo ci){
+    ElementInfo ei = heap.newObject(ci, ti);
+    return ei.getObjectRef();    
+  }
+  
+  public ElementInfo newElementInfo (String clsName)  throws ClinitRequired, UnknownJPFClass {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+    if (ci != null){
+      return newElementInfo(ci);
+    } else {
+      throw new UnknownJPFClass(clsName);
+    }
+  }
+  
+  public int newObject (String clsName)   throws ClinitRequired, UnknownJPFClass {
+    ElementInfo ei = newElementInfo(clsName);
+    return (ei != null) ? ei.getObjectRef() : NULL;
+  }
+  
+  public int newString (String s) {
+    if (s == null){
+      return NULL;
+    } else {
+      return heap.newString(s, ti).getObjectRef();
+    }
+  }
+
+  public int newStringArray (String[] a){
+    int aref = newObjectArray("Ljava/lang/String;", a.length);
+
+    for (int i=0; i<a.length; i++){
+      setReferenceArrayElement(aref, i, newString(a[i]));
+    }
+
+    return aref;
+  }
+
+  public int newString (int arrayRef) {
+    String t = getArrayType(arrayRef);
+    String s = null;
+
+    if ("C".equals(t)) {          // character array
+      char[] ca = getCharArrayObject(arrayRef);
+      s = new String(ca);
+    } else if ("B".equals(t)) {   // byte array
+      byte[] ba = getByteArrayObject(arrayRef);
+      s = new String(ba);
+    }
+
+    if (s == null) {
+      return NULL;
+    }
+
+    return newString(s);
+  }
+  
+  public String format (int fmtRef, int argRef){
+    String format = getStringObject(fmtRef);
+    int len = getArrayLength(argRef);
+    Object[] arg = new Object[len];
+
+    for (int i=0; i<len; i++){
+      int ref = getReferenceArrayElement(argRef,i);
+      if (ref != NULL) {
+        String clsName = getClassName(ref);
+        if (clsName.equals("java.lang.String")) {
+          arg[i] = getStringObject(ref);
+        } else if (clsName.equals("java.lang.Boolean")){
+          arg[i] = getBooleanObject(ref);
+        } else if (clsName.equals("java.lang.Byte")) {
+          arg[i] = getByteObject(ref);
+        } else if (clsName.equals("java.lang.Char")) {
+          arg[i] = getCharObject(ref);
+        } else if (clsName.equals("java.lang.Short")) {
+          arg[i] = getShortObject(ref);
+        } else if (clsName.equals("java.lang.Integer")) {
+          arg[i] = getIntegerObject(ref);
+        } else if (clsName.equals("java.lang.Long")) {
+          arg[i] = getLongObject(ref);
+        } else if (clsName.equals("java.lang.Float")) {
+          arg[i] = getFloatObject(ref);
+        } else if (clsName.equals("java.lang.Double")) {
+          arg[i] = getDoubleObject(ref);
+        } else {
+          // need a toString() here
+          arg[i] = "??";
+        }
+      }
+    }
+
+    return String.format(format,arg);
+  }
+
+  public String format (Locale l,int fmtRef, int argRef){
+           String format = getStringObject(fmtRef);
+           int len = getArrayLength(argRef);
+           Object[] arg = new Object[len];
+
+           for (int i=0; i<len; i++){
+             int ref = getReferenceArrayElement(argRef,i);
+             if (ref != NULL) {
+               String clsName = getClassName(ref);
+               if (clsName.equals("java.lang.String")) {
+                 arg[i] = getStringObject(ref);
+               } else if (clsName.equals("java.lang.Byte")) {
+                 arg[i] = getByteObject(ref);
+               } else if (clsName.equals("java.lang.Char")) {
+                 arg[i] = getCharObject(ref);
+               } else if (clsName.equals("java.lang.Short")) {
+                 arg[i] = getShortObject(ref);
+               } else if (clsName.equals("java.lang.Integer")) {
+                 arg[i] = getIntegerObject(ref);
+               } else if (clsName.equals("java.lang.Long")) {
+                 arg[i] = getLongObject(ref);
+               } else if (clsName.equals("java.lang.Float")) {
+                 arg[i] = getFloatObject(ref);
+               } else if (clsName.equals("java.lang.Double")) {
+                 arg[i] = getDoubleObject(ref);
+               } else {
+                 // need a toString() here
+                 arg[i] = "??";
+               }
+             }
+           }
+
+           return String.format(l,format,arg);
+         }
+
+
+  public int newBoolean (boolean b){
+    return getStaticReferenceField("java.lang.Boolean", b ? "TRUE" : "FALSE");
+  }
+
+  public int newInteger (int n){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Integer"), ti);
+    ei.setIntField("value",n);
+    return ei.getObjectRef();
+  }
+
+  public int newLong (long l){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Long"), ti);
+    ei.setLongField("value",l);
+    return ei.getObjectRef();
+  }
+
+  public int newDouble (double d){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Double"), ti);
+    ei.setDoubleField("value",d);
+    return ei.getObjectRef();
+  }
+
+  public int newFloat (float f){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Float"), ti);
+    ei.setFloatField("value",f);
+    return ei.getObjectRef();
+  }
+
+  public int newByte (byte b){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Byte"), ti);
+    ei.setByteField("value",b);
+    return ei.getObjectRef();
+  }
+
+  public int newShort (short s){
+    ElementInfo ei = heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Short"), ti);
+    ei.setShortField("value",s);
+    return ei.getObjectRef();
+  }
+
+  public int newCharacter (char c){
+    ElementInfo ei =  heap.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Character"), ti);
+    ei.setCharField("value",c);
+    return ei.getObjectRef();
+  }
+
+
+  public boolean notify (int objref) {
+    // objref can't be NULL since the corresponding INVOKE would have failed
+    ElementInfo ei = getModifiableElementInfo(objref);
+    return notify(ei);
+  }
+
+  public boolean notify (ElementInfo ei) {
+    if (!ei.isLockedBy(ti)){
+      throwException("java.lang.IllegalMonitorStateException",
+                                 "un-synchronized notify");
+      return false;
+    }
+
+    return ei.notifies(getSystemState(), ti); 
+  }
+  
+  public boolean notifyAll (int objref) {
+    // objref can't be NULL since the corresponding INVOKE would have failed
+    ElementInfo ei = getElementInfo(objref);
+    return notifyAll(ei);
+  }
+
+  public boolean notifyAll (ElementInfo ei) {
+    if (!ei.isLockedBy(ti)){
+      throwException("java.lang.IllegalMonitorStateException",
+                                 "un-synchronized notifyAll");
+      return false;
+    }
+
+    return ei.notifiesAll();    
+  }
+  
+  public void registerPinDown(int objref){
+    heap.registerPinDown(objref);
+  }
+  public void registerPinDown (ElementInfo ei){
+    registerPinDown(ei.getObjectRef());
+  }
+  
+  public void releasePinDown(int objref){
+    heap.releasePinDown(objref);
+  }
+  public void releasePinDown (ElementInfo ei){
+    releasePinDown(ei.getObjectRef());
+  }
+  
+  /**
+   *  use this whenever a peer performs an operation on a class that might not be initialized yet
+   *  Do a repeatInvocation() in this case 
+   */
+  public boolean requiresClinitExecution(ClassInfo ci) {
+    return ci.initializeClass(ti);
+  }
+  
+  /**
+   * repeat execution of the instruction that caused a native method call
+   * NOTE - this does NOT mean it's the NEXT executed insn, since the native method
+   * might have pushed direct call frames on the stack before asking us to repeat it.
+   */
+  public void repeatInvocation () {
+    repeat = true;
+  }
+
+  public boolean isInvocationRepeated() {
+    return repeat;
+  }
+
+
+  public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg){
+    return vm.getSystemState().setNextChoiceGenerator(cg);
+  }
+
+  public void setMandatoryNextChoiceGenerator(ChoiceGenerator<?> cg, String failMsg){
+    vm.getSystemState().setMandatoryNextChoiceGenerator(cg, failMsg);
+  }
+
+  public ChoiceGenerator<?> getChoiceGenerator () {
+    return vm.getSystemState().getChoiceGenerator();
+  }
+
+  // note this only makes sense if we actually do return something
+  public void setReturnAttribute (Object attr) {
+    returnAttr = attr;
+  }
+
+  /**
+   * return attr list of all arguments. Use ObjectList to retrieve values
+   * from this list
+   * 
+   * NOTE - this can only be called from a native method context, since
+   * otherwise the top frame is the callee
+   */
+  public Object[] getArgAttributes () {
+    StackFrame caller = getCallerStackFrame();
+    return caller.getArgumentAttrs(mi);
+  }
+
+  public Object getReturnAttribute() {
+    return returnAttr;
+  }
+
+  // if any of the next methods is called from the bottom
+  // half of a CG method, you might want to check if another thread
+  // or a listener has already set an exception you don't want to override
+  // (this is for instance used in Thread.stop())
+
+  public void throwException (int xRef){
+    assert isInstanceOf(xRef, "java.lang.Throwable");
+    exceptionRef = xRef;
+  }
+
+  public void throwException (String clsName) {
+    ClassInfo ciX = ClassInfo.getInitializedClassInfo(clsName, ti);
+    assert ciX.isInstanceOf("java.lang.Throwable");
+    exceptionRef = ti.createException(ciX, null, NULL);
+  }
+
+  public void throwException (String clsName, String details) {
+    ClassInfo ciX = ClassInfo.getInitializedClassInfo(clsName, ti);
+    assert ciX.isInstanceOf("java.lang.Throwable");
+    exceptionRef = ti.createException(ciX, details, NULL);
+  }
+
+  public void throwAssertion (String details) {
+    throwException("java.lang.AssertionError", details);
+  }
+
+  public void throwInterrupt(){
+    throwException("java.lang.InterruptedException");
+  }
+
+  public void stopThread(){
+    stopThreadWithException(MJIEnv.NULL);
+  }
+
+  public void stopThreadWithException (int xRef){
+    // this will call throwException(xRef) with the proper Throwable
+    ti.setStopped(xRef);
+  }
+
+  void setCallEnvironment (MethodInfo mi) {
+    this.mi = mi;
+
+    if (mi != null){
+      ciMth = mi.getClassInfo();
+    } else {
+      //ciMth = null;
+      //mi = null;
+    }
+
+    repeat = false;
+    returnAttr = null;
+
+    // we should NOT reset exceptionRef here because it might have been set
+    // at the beginning of the transition. It gets reset upon return from the
+    // native method
+    //exceptionRef = NULL;
+  }
+
+  void clearCallEnvironment () {
+    setCallEnvironment(null);
+  }
+
+  ElementInfo getStaticElementInfo (int clsObjRef) {
+    ClassInfo ci = getReferredClassInfo( clsObjRef);
+    if (ci != null) {
+      return ci.getStaticElementInfo();
+    }
+    
+    return null;
+  }
+  
+  ElementInfo getModifiableStaticElementInfo (int clsObjRef) {
+    ClassInfo ci = getReferredClassInfo( clsObjRef);
+    if (ci != null) {
+      return ci.getModifiableStaticElementInfo();
+    }
+    
+    return null;
+  }
+  
+
+  ClassInfo getClassInfo () {
+    return ciMth;
+  }
+
+  public ClassInfo getReferredClassInfo (int clsObjRef) {
+    ElementInfo ei = getElementInfo(clsObjRef);
+    if (ei.getClassInfo().getName().equals("java.lang.Class")) {
+      int ciId = ei.getIntField( ClassInfo.ID_FIELD);
+      int clref = ei.getReferenceField("classLoader");
+      
+      ElementInfo eiCl = getElementInfo(clref);
+      int cliId = eiCl.getIntField(ClassLoaderInfo.ID_FIELD);
+      
+      ClassLoaderInfo cli = getVM().getClassLoader(cliId);
+      ClassInfo referredCi = cli.getClassInfo(ciId);
+      
+      return referredCi;
+      
+    } else {
+      throw new JPFException("not a java.lang.Class object: " + ei);
+    }
+  }
+
+  public ClassInfo getClassInfo (int objref) {
+    ElementInfo ei = getElementInfo(objref);
+    if (ei != null){
+      return ei.getClassInfo();
+    } else {
+      return null;
+    }
+  }
+
+  public String getClassName (int objref) {
+    return getClassInfo(objref).getName();
+  }
+
+  public Heap getHeap () {
+    return vm.getHeap();
+  }
+
+  public ElementInfo getElementInfo (int objref) {
+    return heap.get(objref);
+  }
+
+  public ElementInfo getModifiableElementInfo (int objref) {
+    return heap.getModifiable(objref);
+  }
+
+  
+  public int getStateId () {
+    return VM.getVM().getStateId();
+  }
+
+  void clearException(){
+    exceptionRef = MJIEnv.NULL;
+  }
+
+  public int peekException () {
+    return exceptionRef;
+  }
+
+  public int popException () {
+    int ret = exceptionRef;
+    exceptionRef = NULL;
+    return ret;
+  }
+
+  public boolean hasException(){
+    return (exceptionRef != NULL);
+  }
+
+  public boolean hasPendingInterrupt(){
+    return (exceptionRef != NULL && isInstanceOf(exceptionRef, "java.lang.InterruptedException"));
+  }
+
+  //-- time is managed by the VM
+  public long currentTimeMillis(){
+    return vm.currentTimeMillis();
+  }
+  
+  public long nanoTime(){
+    return vm.nanoTime();
+  }
+  
+  //--- those are not public since they refer to JPF internals
+  public KernelState getKernelState () {
+    return VM.getVM().getKernelState();
+  }
+
+  public MethodInfo getMethodInfo () {
+    return mi;
+  }
+
+  public Instruction getInstruction () {
+    return ti.getPC();
+  }
+
+  /**
+   * It returns the ClassLoaderInfo corresponding to the given classloader object
+   * reference
+   */
+  public ClassLoaderInfo getClassLoaderInfo(int clObjRef) {
+    if(clObjRef == MJIEnv.NULL) {
+      return null;
+    }
+
+    int cliId = heap.get(clObjRef).getIntField(ClassLoaderInfo.ID_FIELD);
+    return getVM().getClassLoader(cliId);
+  }
+
+  // <2do> that's not correct - it should return the current SystemClassLoader, NOT the startup SystemClassLoader
+  // (we can instantiate them explicitly)
+  public ClassLoaderInfo getSystemClassLoaderInfo() {
+    return ti.getSystemClassLoaderInfo();
+  }
+  
+  public SystemState getSystemState () {
+    return ti.getVM().getSystemState();
+  }
+  
+  public ApplicationContext getApplicationContext (){
+    return ti.getApplicationContext();
+  }
+
+  public ThreadInfo getThreadInfo () {
+    return ti;
+  }
+
+  /**
+   * NOTE - callers have to be prepared this might return null in case
+   * the thread got already terminated
+   */
+  public ThreadInfo getThreadInfoForId (int id){
+    return vm.getThreadList().getThreadInfoForId(id);
+  }
+
+  public ThreadInfo getLiveThreadInfoForId (int id){
+    ThreadInfo ti = vm.getThreadList().getThreadInfoForId(id);
+    if (ti != null && ti.isAlive()){
+      return ti;
+    }
+    
+    return null;
+  }
+  
+  /**
+   * NOTE - callers have to be prepared this might return null in case
+   * the thread got already terminated
+   */
+  public ThreadInfo getThreadInfoForObjRef (int id){
+    return vm.getThreadList().getThreadInfoForObjRef(id);
+  }
+  
+  public ThreadInfo getLiveThreadInfoForObjRef (int id){
+    ThreadInfo ti = vm.getThreadList().getThreadInfoForObjRef(id);
+    if (ti != null && ti.isAlive()){
+      return ti;
+    }
+    
+    return null;
+  }
+
+  
+  
+  public ThreadInfo[] getLiveThreads(){
+    return getVM().getLiveThreads();
+  }
+  
+  // <2do> - naming? not very intuitive
+  void lockNotified (int objref) {
+    ElementInfo ei = getModifiableElementInfo(objref);
+    ei.lockNotified(ti);
+  }
+
+  void initAnnotationProxyField (int proxyRef, FieldInfo fi, Object v) throws ClinitRequired {
+    String fname = fi.getName();
+    String ftype = fi.getType();
+
+    if (v instanceof String){
+      setReferenceField(proxyRef, fname, newString((String)v));
+    } else if (v instanceof Boolean){
+      setBooleanField(proxyRef, fname, ((Boolean)v).booleanValue());
+    } else if (v instanceof Integer){
+      setIntField(proxyRef, fname, ((Integer)v).intValue());
+    } else if (v instanceof Long){
+      setLongField(proxyRef, fname, ((Long)v).longValue());
+    } else if (v instanceof Float){
+      setFloatField(proxyRef, fname, ((Float)v).floatValue());
+    } else if (v instanceof Short){
+      setShortField(proxyRef, fname, ((Short)v).shortValue());
+    } else if (v instanceof Character){
+      setCharField(proxyRef, fname, ((Character)v).charValue());
+    } else if (v instanceof Byte){
+      setByteField(proxyRef, fname, ((Byte)v).byteValue());
+    } else if (v instanceof Double){
+      setDoubleField(proxyRef, fname, ((Double)v).doubleValue());
+
+    } else if (v instanceof AnnotationInfo.EnumValue){ // an enum constant
+      AnnotationInfo.EnumValue ev = (AnnotationInfo.EnumValue)v;
+      String eCls = ev.getEnumClassName();
+      String eConst = ev.getEnumConstName();
+
+      ClassInfo eci = ClassLoaderInfo.getCurrentResolvedClassInfo(eCls);
+      if (!eci.isInitialized()){
+        throw new ClinitRequired(eci);
+      }
+
+      StaticElementInfo sei = eci.getStaticElementInfo();
+      int eref = sei.getReferenceField(eConst);
+      setReferenceField(proxyRef, fname, eref);
+
+    } else if (v instanceof AnnotationInfo.ClassValue){ // a class
+      String clsName = v.toString();
+      ClassInfo cci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+      // <2do> should throw ClassNotFoundError here if cci is null
+
+      if (!cci.isInitialized()){
+        throw new ClinitRequired(cci);
+      }
+
+      int cref = cci.getClassObjectRef();
+      setReferenceField(proxyRef, fname, cref);
+
+    } else if (v.getClass().isArray()){ // ..or arrays thereof
+      Object[] a = (Object[])v;
+      int aref = NULL;
+
+      if (ftype.equals("java.lang.String[]")){
+        aref = newObjectArray("Ljava/lang/String;", a.length);
+        for (int i=0; i<a.length; i++){
+          setReferenceArrayElement(aref,i,newString(a[i].toString()));
+        }
+      } else if (ftype.equals("int[]")){
+        aref = newIntArray(a.length);
+        for (int i=0; i<a.length; i++){
+          setIntArrayElement(aref,i,((Number)a[i]).intValue());
+        }
+      } else if (ftype.equals("boolean[]")){
+        aref = newBooleanArray(a.length);
+        for (int i=0; i<a.length; i++){
+          setBooleanArrayElement(aref,i,((Boolean)a[i]).booleanValue());
+        }
+      } else if (ftype.equals("long[]")){
+        aref = newLongArray(a.length);
+        for (int i=0; i<a.length; i++){
+          setLongArrayElement(aref,i,((Number)a[i]).longValue());
+        }
+      } else if (ftype.equals("double[]")){
+        aref = newDoubleArray(a.length);
+        for (int i=0; i<a.length; i++){
+          setDoubleArrayElement(aref,i,((Number)a[i]).doubleValue());
+        }
+      } else if (ftype.equals("java.lang.Class[]")){
+        aref = newObjectArray("java.lang.Class", a.length);
+        for (int i=0; i<a.length; i++){
+          String clsName = ((AnnotationInfo.ClassValue)a[i]).getName();
+          ClassInfo cci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+          if (!cci.isInitialized()){
+            throw new ClinitRequired(cci);
+          }
+          int cref = cci.getClassObjectRef();
+          setReferenceArrayElement(aref,i,cref);
+        }
+      }
+
+      if (aref != NULL){
+        setReferenceField(proxyRef, fname, aref);
+      } else {
+        throwException("AnnotationElement type not supported: " + ftype);
+      }
+
+    } else {
+      throwException("AnnotationElement type not supported: " + ftype);
+    }
+  }
+
+  int newAnnotationProxy (ClassInfo aciProxy, AnnotationInfo ai) throws ClinitRequired {
+
+    int proxyRef = newObject(aciProxy);
+
+    // init fields of the new object from the AnnotationInfo
+    for (AnnotationInfo.Entry e : ai.getEntries()){
+      Object v = e.getValue();
+      String fname = e.getKey();
+      FieldInfo fi = aciProxy.getInstanceField(fname);
+
+      initAnnotationProxyField(proxyRef, fi, v);
+    }
+
+    return proxyRef;
+  }
+
+  int newAnnotationProxies (AnnotationInfo[] ai) throws ClinitRequired {
+
+    if ((ai != null) && (ai.length > 0)){
+      int aref = newObjectArray("Ljava/lang/annotation/Annotation;", ai.length);
+      for (int i=0; i<ai.length; i++){
+        ClassInfo aci = ClassLoaderInfo.getCurrentResolvedClassInfo(ai[i].getName());
+        ClassInfo aciProxy = aci.getAnnotationProxy();
+
+        int ar = newAnnotationProxy(aciProxy, ai[i]);
+        setReferenceArrayElement(aref, i, ar);
+      }
+      return aref;
+
+    } else {
+      // on demand init (not too many programs use annotation reflection)
+      int aref = getStaticReferenceField("java.lang.Class", "emptyAnnotations");
+      if (aref == NULL) {
+        aref = newObjectArray("Ljava/lang/annotation/Annotation;", 0);
+        setStaticReferenceField("java.lang.Class", "emptyAnnotations", aref);
+      }
+      return aref;
+    }
+  }
+
+  public void handleClinitRequest (ClassInfo ci) {
+    ThreadInfo ti = getThreadInfo();
+
+    // NOTE: we have to repeat no matter what, since this is called from
+    // a handler context (if we only had to create a class object w/o
+    // calling clinit, we can't just go on)
+    ci.initializeClass(ti);
+    repeatInvocation();
+  }
+
+  public StackFrame getCallerStackFrame() {
+    // since native methods are now executed within their own stack frames
+    // we provide a little helper to get the caller
+    return ti.getLastNonSyntheticStackFrame();
+  }
+
+  public StackFrame getModifiableCallerStackFrame() {
+    // since native methods are now executed within their own stack frames
+    // we provide a little helper to get the caller
+    return ti.getModifiableLastNonSyntheticStackFrame();
+  }
+
+  
+  public int valueOfBoolean(boolean b) {
+    return BoxObjectCacheManager.valueOfBoolean(ti, b);
+  }
+
+  public int valueOfByte(byte b) {
+    return BoxObjectCacheManager.valueOfByte(ti, b);
+  }
+
+  public int valueOfCharacter(char c) {
+    return BoxObjectCacheManager.valueOfCharacter(ti, c);
+  }
+
+  public int valueOfShort(short s) {
+    return BoxObjectCacheManager.valueOfShort(ti, s);
+  }
+
+  public int valueOfInteger(int i) {
+    return BoxObjectCacheManager.valueOfInteger(ti, i);
+  }
+
+  public int valueOfLong(long l) {
+    return BoxObjectCacheManager.valueOfLong(ti, l);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/Memento.java b/src/main/gov/nasa/jpf/vm/Memento.java
new file mode 100644 (file)
index 0000000..b7bb75d
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * generic interface for objects that are used to restore previous states from
+ * within a context that holds the references to the objects to restore (e.g. a
+ * container), i.e. the caller knows where to restore the objects in question.
+ * The caller can provide a cached object the memento can update. However, its
+ * up to the memento if it uses this (optional) argument object to restore
+ * in-situ, the only guarantee it makes is that it returns a restored object
+ */
+public interface Memento<T> {
+
+  /**
+   * note that there is no guarantee the restored object will be the same that
+   * is (optionally) passed in.
+   * 
+   * Implementations are free to restore in-situ or create a new object if a
+   * non-null reference is provided. Callers are responsible for identity
+   * integrity if they do provide in-situ objects
+   * 
+   * The caller does not guarantee the provided in-situ object was the one the
+   * Memento was created from
+   */
+  T restore(T inSitu);
+}
diff --git a/src/main/gov/nasa/jpf/vm/MementoFactory.java b/src/main/gov/nasa/jpf/vm/MementoFactory.java
new file mode 100644 (file)
index 0000000..d8731d4
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface to create Memento objects for state storage/restore
+ *
+ * this follows some sort of a visitor pattern
+ */
+public interface MementoFactory {
+
+  Memento<KernelState> getMemento(KernelState ks);
+
+
+  Memento<ThreadList> getMemento(ThreadList tlist);
+
+  Memento<ThreadInfo> getMemento(ThreadInfo ti);
+
+  Memento<ClassLoaderList> getMemento(ClassLoaderList cllist);
+
+  Memento<ClassLoaderInfo> getMemento(ClassLoaderInfo cl);
+
+  Memento<ClassPath> getMemento (ClassPath cp);
+
+  Memento<Heap> getMemento(Heap heap);
+
+  Memento<Statics> getMemento(Statics sa);
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/MementoRestorer.java b/src/main/gov/nasa/jpf/vm/MementoRestorer.java
new file mode 100644 (file)
index 0000000..595cd26
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * state storer/restorer that works solely on a snapshot basis
+ */
+public abstract class MementoRestorer extends AbstractRestorer<Memento<KernelState>> implements MementoFactory {
+
+
+  @Override
+  protected Memento<KernelState> computeRestorableData() {
+    return ks.getMemento(this);
+  }
+
+  @Override
+  protected void doRestore(Memento<KernelState> data) {
+
+    // it's identity preserving, so we don't have to worry about updating external fields
+    ks = data.restore(ks);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/MethodInfo.java b/src/main/gov/nasa/jpf/vm/MethodInfo.java
new file mode 100644 (file)
index 0000000..a4880c7
--- /dev/null
@@ -0,0 +1,1378 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.LocationSpec;
+import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+
+/**
+ * information associated with a method. Each method in JPF
+ * is represented by a MethodInfo object
+ */
+public class MethodInfo extends InfoObject implements GenericSignatureHolder  {
+
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.MethodInfo");
+  
+  static final int INIT_MTH_SIZE = 4096;
+  protected static final ArrayList<MethodInfo> mthTable = new ArrayList<MethodInfo>(INIT_MTH_SIZE);
+  
+  // special globalIds
+  static final int DIRECT_CALL = -1;
+
+  static final LocalVarInfo[] EMPTY = new LocalVarInfo[0];
+  
+  static final int[] EMPTY_INT = new int[0];
+  
+  /**
+   * Used to warn about local variable information.
+   */
+  protected static boolean warnedLocalInfo = false;
+  
+  //--- various JPF method attributes
+  static final int  EXEC_ATOMIC = 0x10000; // method executed atomically
+  static final int  EXEC_HIDDEN = 0x20000; // method hidden from path
+  static final int  FIREWALL    = 0x40000; // firewall any unhandled exceptionHandlers
+                                           // (turn into UnhandledException throws)
+  static final int  IS_CLINIT   = 0x80000;
+  static final int  IS_INIT     = 0x100000;
+  
+  static final int  IS_REFLECTION = 0x200000; // this is a reflection direct call
+  static final int  IS_DIRECT_CALL = 0x400000;
+  
+  /** a unique int assigned to this method */
+  protected int globalId = -1;
+
+  /**
+   * this is a lazy evaluated mangled name consisting of the name and
+   * arg type signature
+   */
+  protected String uniqueName;
+
+  /** Name of the method */
+  protected String name;
+
+  /** Signature of the method */
+  protected String signature;
+
+  /** Generic signature of the method */
+  protected String genericSignature;
+
+  /** Class the method belongs to */
+  protected ClassInfo ci;
+
+  /** Instructions associated with the method */
+  protected Instruction[] code;
+
+  /** JPFConfigException handlers */
+  protected ExceptionHandler[] exceptionHandlers;
+
+  /** classnames of checked exception thrown by the method */
+  protected String[] thrownExceptionClassNames;
+
+  /** Table used for line numbers 
+   * this assigns a line number to every instruction index, instead of 
+   * using an array of ranges. Assuming we have 2-3 insns per line on average,
+   * this should still require less memory than a reference array with associated
+   * range objects, and allows faster access of instruction line numbers, which
+   * we might need for location specs
+   */
+  protected int[] lineNumbers;
+  
+  /** Local variable information */
+  protected LocalVarInfo localVars[] = null;
+
+  /** Maximum number of local variables */
+  protected int maxLocals;
+
+  /** Maximum number of elements on the stack */
+  protected int maxStack;
+
+  /** null if we don't have any */
+  AnnotationInfo[][] parameterAnnotations;
+
+  //--- a batch of attributes
+  
+  /** the standard Java modifier attributes */
+  protected int modifiers;
+   
+  /** a batch of execution related JPF attributes */
+  protected int attributes;
+      
+
+  //--- all the stuff we need for native methods
+  // <2do> pcm - turn this into a derived class
+
+  /**  the number of stack slots for the arguments (incl. 'this'), lazy eval */
+  protected int argSize = -1;
+
+  /** number of arguments (excl. 'this'), lazy eval */
+  protected int nArgs = -1;
+
+  /** what return type do we have (again, lazy evaluated) */
+  protected byte returnType = -1;
+
+  /** number of stack slots for return value */
+  protected int retSize = -1;
+
+  /** used for native method parameter conversion (lazy evaluated) */
+  protected byte[] argTypes = null;
+  
+  static boolean init (Config config) {
+    mthTable.clear();    
+    return true;
+  }
+
+  public static MethodInfo getMethodInfo (int globalId){
+    if (globalId >=0 && globalId <mthTable.size()){
+      return mthTable.get(globalId);
+    } else {
+      return null;
+    }
+  }
+  
+  public static MethodInfo create (String name, String signature, int modifiers){
+    return new MethodInfo( name, signature, modifiers);
+  }
+  
+  public static MethodInfo create (ClassInfo ci, String name, String signature, int modifiers){
+    return new MethodInfo( ci, name, signature, modifiers);
+  }
+  
+  static MethodInfo create (ClassInfo ci, String name, String signature, int modifiers, int maxLocals, int maxStack){
+    return new MethodInfo( ci, name, signature, modifiers, maxLocals, maxStack);
+  }
+
+  /**
+   * for direct call construction
+   * Note: this is only a partial initialization, the code still has to be created/installed by the caller
+   */
+  public MethodInfo (MethodInfo callee, int nLocals, int nOperands) {
+    globalId = DIRECT_CALL;
+    // we don't want direct call methods in the mthTable (would be a memory leak) so don't register
+    
+    ci = callee.ci;
+    name = "[" + callee.name + ']'; // it doesn't allocate anything, so we don't have to be unique
+    signature = "()V";
+    genericSignature = "";
+    maxLocals = nLocals;
+    maxStack = nOperands;  // <2do> cache for optimization
+    localVars = EMPTY;
+    lineNumbers = null;
+    exceptionHandlers = null;
+    thrownExceptionClassNames = null;
+    uniqueName = name;
+    
+    // we need to preserve the ClassInfo so that class resolution for static method calls works
+    ci = callee.ci;
+    
+    attributes |= IS_DIRECT_CALL;
+    modifiers = Modifier.STATIC;   // always treated as static
+    
+    // code still has to be installed by caller
+  }
+  
+  /**
+   * This is used to create synthetic methods of function object types
+   */
+  public MethodInfo(String name, String signature, int modifiers, int nLocals, int nOperands) {
+    this( name, signature, modifiers);
+    maxLocals = nLocals;
+    maxStack = nOperands;
+    localVars = EMPTY;
+  }
+  
+  /**
+   * for NativeMethodInfo creation 
+   */
+  public MethodInfo (MethodInfo mi) {
+    globalId = mi.globalId;
+    uniqueName = mi.uniqueName;
+    name = mi.name;
+    signature = mi.signature;
+    genericSignature = mi.genericSignature;
+    ci = mi.ci;
+    modifiers = mi.modifiers;
+    attributes = mi.attributes;
+    thrownExceptionClassNames = mi.thrownExceptionClassNames;
+    parameterAnnotations = mi.parameterAnnotations;
+
+    annotations = mi.annotations;
+    
+    localVars = null; // there are no StackFrame localVarInfos, this is native
+    // code still has to be installed by caller
+  }
+  
+  // <2do> this is going away
+  public MethodInfo (ClassInfo ci, String name, String signature, int modifiers, int maxLocals, int maxStack){
+    this.ci = ci;
+    this.name = name;
+    this.signature = signature;
+    this.uniqueName = getUniqueName(name, signature);
+    this.genericSignature = "";
+    this.maxLocals = maxLocals;
+    this.maxStack = maxStack;
+    this.modifiers = modifiers;
+
+    this.lineNumbers = null;
+    this.exceptionHandlers = null;
+    this.thrownExceptionClassNames = null;
+
+    // set attributes we can deduce from the name and the ClassInfo
+    if (ci != null){
+      if (name.equals("<init>")) {
+        attributes |= IS_INIT;
+      } else if (name.equals("<clinit>")) {
+        this.modifiers |= Modifier.SYNCHRONIZED;
+        attributes |= IS_CLINIT | FIREWALL;
+      }
+      if (ci.isInterface()) { // all interface methods are public
+        this.modifiers |= Modifier.PUBLIC;
+      }
+    }
+
+    this.globalId = mthTable.size();
+    mthTable.add(this);
+  }
+
+  
+  public MethodInfo (String name, String signature, int modifiers){
+    this.name = name;
+    this.signature = signature;
+    this.modifiers = modifiers;
+    this.uniqueName = getUniqueName(name, signature);
+    this.genericSignature = "";
+
+    if (name.equals("<init>")) {
+      attributes |= IS_INIT;
+    } else if (name.equals("<clinit>")) {
+      // for some reason clinits don't have the synchronized modifier, but they are synchronized
+      // we keep it consistent so that we don't have to implement special lock acquisition/release for clinits
+      this.modifiers |= Modifier.SYNCHRONIZED;
+      attributes |= IS_CLINIT | FIREWALL;
+    }
+    
+    this.globalId = mthTable.size();
+    mthTable.add(this);    
+  }
+
+  public MethodInfo (ClassInfo ci, String name, String signature, int modifiers){
+    this(name, signature, modifiers);
+    
+    this.ci = ci;
+  }
+  
+  //--- setters used during construction
+  
+  public void linkToClass (ClassInfo ci){
+    this.ci = ci;
+    
+    if (ci.isInterface()) { // all interface methods are public
+      this.modifiers |= Modifier.PUBLIC;
+    }
+  }
+  
+  public void setMaxLocals(int maxLocals){
+    this.maxLocals = maxLocals;
+  }
+
+  public void setMaxStack(int maxStack){
+    this.maxStack = maxStack;
+  }
+  
+  public void setCode (Instruction[] code){
+    for (int i=0; i<code.length; i++){
+      code[i].setMethodInfo(this);
+    }
+    this.code = code;
+  }
+  
+  
+  public boolean hasParameterAnnotations() {
+    return (parameterAnnotations != null);
+  }
+
+  // since some listeners might call this on every method invocation, we should do a little optimization
+  static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_0 = new AnnotationInfo[0][];
+  static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_1 = { new AnnotationInfo[0] };
+  static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_2 = { new AnnotationInfo[0], new AnnotationInfo[0] };
+  static AnnotationInfo[][] NO_PARAMETER_ANNOTATIONS_3 = { new AnnotationInfo[0], new AnnotationInfo[0], new AnnotationInfo[0] };  
+  
+  public AnnotationInfo[][] getParameterAnnotations() {
+    if (parameterAnnotations == null){ // keep this similar to getAnnotations()
+      int n = getNumberOfArguments();
+      switch (n){
+      case 0: return NO_PARAMETER_ANNOTATIONS_0;
+      case 1: return NO_PARAMETER_ANNOTATIONS_1;
+      case 2: return NO_PARAMETER_ANNOTATIONS_2;
+      case 3: return NO_PARAMETER_ANNOTATIONS_3;
+      default:
+        AnnotationInfo[][] pai = new AnnotationInfo[n][];
+        for (int i=0; i<n; i++){
+          pai[i] = new AnnotationInfo[0];
+        }
+        return pai;
+      }
+      
+    } else {
+      return parameterAnnotations;
+    }
+  }
+
+  /**
+   * return annotations for parameterIndex
+   */
+  public AnnotationInfo[] getParameterAnnotations(int parameterIndex){
+    if (parameterAnnotations == null){
+      return null;
+    } else {
+      if (parameterIndex >= getNumberOfArguments()){
+        return null;
+      } else {
+        return parameterAnnotations[parameterIndex];
+      }
+    }
+  }
+
+
+  
+  public static int getNumberOfLoadedMethods () {
+    return mthTable.size();
+  }
+
+  void setAtomic (boolean isAtomic) {
+    if (isAtomic) {
+      attributes |= EXEC_ATOMIC;
+    } else {
+      attributes &= ~EXEC_ATOMIC;
+    }
+  }
+  public boolean isAtomic () {
+    return ((attributes & EXEC_ATOMIC) != 0);
+  }
+  
+  void setHidden (boolean isHidden) {
+    if (isHidden) {
+      attributes |= EXEC_HIDDEN;
+    } else {
+      attributes &= ~EXEC_HIDDEN;
+    }
+  }
+  public boolean isHidden () {
+    return ((attributes & EXEC_HIDDEN) != 0);    
+  }
+  
+  /**
+   * turn unhandled exceptionHandlers at the JPF execution level
+   * into UnhandledException throws at the host VM level
+   * this is useful to implement firewalls for direct calls
+   * which should not let exceptionHandlers permeate into bytecode/
+   * application code
+   */
+  public void setFirewall (boolean isFirewalled) {
+    if (isFirewalled) {
+      attributes |= FIREWALL;
+    } else {
+      attributes &= ~FIREWALL;
+    }
+  }
+  public boolean isFirewall () {
+    return ((attributes & FIREWALL) != 0);    
+  }
+  
+  
+  
+  @Override
+  public Object clone() {
+    try {
+      return super.clone();
+    } catch (CloneNotSupportedException cnx) {
+      return null;
+    }
+  }
+  
+  public int getGlobalId() {
+    return globalId;
+  }
+
+  public DirectCallStackFrame createRunStartStackFrame (ThreadInfo ti){
+    return ci.createRunStartStackFrame( ti, this);
+  }
+
+  public DirectCallStackFrame createDirectCallStackFrame (ThreadInfo ti, int nLocals){
+    return ci.createDirectCallStackFrame(ti, this, nLocals);
+  }
+
+  public boolean isSyncRelevant () {
+    return (name.charAt(0) != '<');
+  }
+  
+  public boolean isInitOrClinit (){
+    return ((attributes & (IS_CLINIT | IS_INIT)) != 0);
+  }
+  
+  public boolean isClinit () {
+    return ((attributes & IS_CLINIT) != 0);
+  }
+
+  public boolean isClinit (ClassInfo ci) {
+    return (((attributes & IS_CLINIT) != 0) && (this.ci == ci));
+  }
+
+  public boolean isInit() {
+    return ((attributes & IS_INIT) != 0);
+  }
+  
+  public boolean isDirectCallStub(){
+    return ((attributes & IS_DIRECT_CALL) != 0);    
+  }
+  
+  /**
+   * yet another name - this time with a non-mangled, but abbreviated signature
+   * and without return type (e.g. like "main(String[])"
+   */
+  public String getLongName () {
+    StringBuilder sb = new StringBuilder();
+    sb.append(name);
+    
+    sb.append('(');
+    String[] argTypeNames = getArgumentTypeNames();
+    for (int i=0; i<argTypeNames.length; i++) {
+      String a = argTypeNames[i];
+      int idx = a.lastIndexOf('.');
+      if (idx > 0) {
+        a = a.substring(idx+1);
+      }
+      if (i>0) {
+        sb.append(',');
+      }
+      sb.append(a);
+    }
+    sb.append(')');
+    
+    return sb.toString();
+  }
+  
+  /**
+   * return the minimal name that has to be unique for overloading
+   * used as a lookup key
+   * NOTE: with the silent introduction of covariant return types
+   * in Java 5.0, we have to use the full signature to be unique
+   */
+  public static String getUniqueName (String mname, String signature) {
+    return (mname + signature);
+  }
+
+  public String getStackTraceSource() {
+    return getSourceFileName();
+  }
+
+  public byte[] getArgumentTypes () {
+    if (argTypes == null) {
+      argTypes = Types.getArgumentTypes(signature);
+      nArgs = argTypes.length;
+    }
+
+    return argTypes;
+  }
+
+  public String[] getArgumentTypeNames () {
+    return Types.getArgumentTypeNames(signature);
+  }
+  
+  public int getArgumentsSize () {
+    if (argSize < 0) {
+      argSize = Types.getArgumentsSize(signature);
+
+      if (!isStatic()) {
+        argSize++;
+      }
+    }
+
+    return argSize;
+  }
+  
+  /**
+   * return only the LocalVarInfos for arguments, in order of definition
+   * or null if there are no localVarInfos.
+   * throw a JPFException if there are more immediately in scope vars than args
+   * 
+   * NOTE - it is perfectly legal for a method to have arguments but no LocalVarInfos,
+   * which are code attributes, clients have to check for a non-null return value
+   * even if the method has arguments.
+   * Note also that abstract / interface methods don't have code and hence no
+   * LocalVarInfos
+   */
+  public LocalVarInfo[] getArgumentLocalVars(){
+    if (localVars == null){ // shortcut in case we don't have args or localVars;
+      return null;
+    }
+    
+    int nArgs = getNumberOfStackArguments(); // we want 'this'
+    if (nArgs == 0){
+      return new LocalVarInfo[0]; // rare enough so that we don't use a static
+    }
+
+    LocalVarInfo[] argLvis = new LocalVarInfo[nArgs];
+    int n = 0; // how many args we've got so far
+    
+    for (LocalVarInfo lvi : localVars){
+      // arguments are the only ones that are immediately in scope
+      if (lvi.getStartPC() == 0){
+        if (n == nArgs){ // ARGH - more in-scope vars than args
+          throw new JPFException("inconsistent localVar table for method " + getFullName());
+        }
+        
+        // order with respect to slot index - since this might get called
+        // frequently, we don't use java.util.Arrays.sort() but sort in
+        // on-the-fly. Note that we can have several localVar entries for the
+        // same name, but only one can be immediately in scope
+        int slotIdx = lvi.getSlotIndex();
+
+        int i;
+        for (i = 0; i < n; i++) {
+          if (slotIdx < argLvis[i].getSlotIndex()) {
+            for (int j=n; j>i; j--){
+              argLvis[j] = argLvis[j-1];
+            }
+            argLvis[i] = lvi;
+            n++;
+            break;
+          }
+        }
+        if (i == n) { // append
+          argLvis[n++] = lvi;
+        }
+      }
+    }
+    
+    return argLvis;
+  }
+  
+  public String getReturnType () {
+    return Types.getReturnTypeSignature(signature);
+  }
+
+  public String getReturnTypeName () {
+    return Types.getReturnTypeName(signature);
+  }
+  
+  public String getSourceFileName () {
+    if (ci != null) {
+      return ci.getSourceFileName();
+    } else {
+      return "[VM]";
+    }
+  }
+
+  public String getClassName () {
+    if (ci != null) {
+      return ci.getName();
+    } else {
+      return "[VM]";
+    }
+  }
+  
+  /**
+   * Returns the class the method belongs to.
+   */
+  public ClassInfo getClassInfo () {
+    return ci;
+  }
+
+  /**
+   * @deprecated - use getFullName
+   */
+  @Deprecated
+public String getCompleteName () {
+    return getFullName();
+  }
+
+  /**
+   * return classname.name (but w/o signature)
+   */
+  public String getBaseName() {
+    return getClassName() + '.' + name;
+  }
+    
+  public boolean isCtor () {
+    return (name.equals("<init>"));
+  }
+  
+  public boolean isInternalMethod () {
+    // <2do> pcm - should turn this into an attribute for efficiency reasons
+    return (name.equals("<clinit>") || uniqueName.equals("finalize()V"));
+  }
+  
+  public boolean isThreadEntry (ThreadInfo ti) {
+    return (uniqueName.equals("run()V") && (ti.countStackFrames() == 1));
+  }
+  
+  /**
+   * Returns the full classname (if any) + name + signature.
+   */
+  public String getFullName () {
+    if (ci != null) {
+      return ci.getName() + '.' + getUniqueName();
+    } else {
+      return getUniqueName();
+    }
+  }
+
+  /**
+   * returns stack trace name: classname (if any) + name
+   */
+  public String getStackTraceName(){
+    if (ci != null) {
+      return ci.getName() + '.' + name;
+    } else {
+      return name;
+    }
+  }
+  
+  /**
+   * return number of instructions
+   */
+  public int getNumberOfInstructions() {
+    if (code == null){
+      return 0;
+    }
+    
+    return code.length;
+  }
+  
+  /**
+   * Returns a specific instruction.
+   */
+  public Instruction getInstruction (int i) {
+    if (code == null) {
+      return null;
+    }
+
+    if ((i < 0) || (i >= code.length)) {
+      return null;
+    }
+
+    return code[i];
+  }
+
+  /**
+   * Returns the instruction at a certain position.
+   */
+  public Instruction getInstructionAt (int position) {
+    if (code == null) {
+      return null;
+    }
+
+    for (int i = 0, l = code.length; i < l; i++) {
+      if ((code[i] != null) && (code[i].getPosition() == position)) {
+        return code[i];
+      }
+    }
+
+    throw new JPFException("instruction not found");
+  }
+
+  /**
+   * Returns the instructions of the method.
+   */
+  public Instruction[] getInstructions () {
+    return code;
+  }
+  
+  public boolean includesLine (int line){
+    int len = code.length;
+    return (code[0].getLineNumber() <= line) && (code[len].getLineNumber() >= line);
+  }
+
+  public Instruction[] getInstructionsForLine (int line){
+    return getInstructionsForLineInterval(line,line);
+  }
+
+  public Instruction[] getInstructionsForLineInterval (int l1, int l2){
+    Instruction[] c = code;
+       
+    // instruction line numbers don't have to be monotonic (they can decrease for loops)
+    // hence we cannot easily check for overlapping ranges
+    
+    if (c != null){
+       ArrayList<Instruction> matchingInsns = null;
+       
+       for (int i = 0; i < c.length; i++) {
+        Instruction insn = c[i];
+        int line = insn.getLineNumber();
+        if (line == l1 || line == l2 || (line > l1 && line < l2)) {
+          if (matchingInsns == null) {
+            matchingInsns = new ArrayList<Instruction>();
+          }
+          matchingInsns.add(insn);
+        }
+      }
+      
+      if (matchingInsns == null) {
+        return null;
+      } else {
+        return matchingInsns.toArray(new Instruction[matchingInsns.size()]);
+      }
+            
+    } else {
+      return null;
+    }
+  }
+
+  public Instruction[] getMatchingInstructions (LocationSpec lspec){
+    return getInstructionsForLineInterval(lspec.getFromLine(), lspec.getToLine());
+  }
+
+
+  /**
+   * Returns the line number for a given position.
+   */
+  public int getLineNumber (Instruction pc) {
+    if (lineNumbers == null) {
+      if (pc == null)
+        return -1;
+      else
+        return pc.getPosition();
+    }
+
+    if (pc != null) {
+      int idx = pc.getInstructionIndex();
+      if (idx < 0) idx = 0;
+      return lineNumbers[idx];
+    } else {
+      return -1;
+    }
+  }
+
+  /**
+   * Returns a table to translate positions into line numbers.
+   */
+  public int[] getLineNumbers () {
+    return lineNumbers;
+  }
+
+  public boolean containsLineNumber (int n){
+    if (lineNumbers != null){
+      return (lineNumbers[0] <= n) && (lineNumbers[lineNumbers.length-1] <= n);
+    }
+    
+    return false;
+  }
+  
+  public boolean intersectsLineNumbers( int first, int last){
+    if (lineNumbers != null){
+      if ((last < lineNumbers[0]) || (first > lineNumbers[lineNumbers.length-1])){
+        return false;
+      }
+      return true;
+    }
+    
+    return false;
+  }
+  
+  public ExceptionHandler getHandlerFor (ClassInfo ciException, Instruction insn){
+    if (exceptionHandlers != null){
+      int position = insn.getPosition();
+      for (int i=0; i<exceptionHandlers.length; i++){
+        ExceptionHandler handler = exceptionHandlers[i];
+        if ((position >= handler.getBegin()) && (position < handler.getEnd())) {
+          // checks if this type of exception is caught here (null means 'any')
+          String handledType = handler.getName();
+          if ((handledType == null)   // a catch-all handler
+                  || ciException.isInstanceOf(handledType)) {
+            return handler;
+          }
+        }          
+      }      
+    }
+    
+    return null;
+  }
+  
+  public boolean isMJI () {
+    return false;
+  }
+
+  public int getMaxLocals () {
+    return maxLocals;
+  }
+
+  public int getMaxStack () {
+    return maxStack;
+  }
+
+  public ExceptionHandler[] getExceptions () {
+    return exceptionHandlers;
+  }
+
+  public String[] getThrownExceptionClassNames () {
+    return thrownExceptionClassNames;
+  }
+
+
+  public LocalVarInfo getLocalVar(String name, int pc){
+    LocalVarInfo[] vars = localVars;
+    if (vars != null){
+      for (int i = 0; i < vars.length; i++) {
+        LocalVarInfo lv = vars[i];
+        if (lv.matches(name, pc)) {
+          return lv;
+        }
+      }
+    }
+
+    return null;
+
+  }
+
+  public LocalVarInfo getLocalVar (int slotIdx, int pc){
+    LocalVarInfo[] vars = localVars;
+
+    if (vars != null){
+      for (int i = 0; i < vars.length; i++) {
+        LocalVarInfo lv = vars[i];
+        if (lv.matches(slotIdx, pc)) {
+          return lv;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public LocalVarInfo[] getLocalVars() {
+    return localVars; 
+  }
+
+
+  /**
+   * note that this might contain duplicates for variables with multiple
+   * scope entries
+   */
+  public String[] getLocalVariableNames() {
+    String[] names = new String[localVars.length];
+
+    for (int i=0; i<localVars.length; i++){
+      names[i] = localVars[i].getName();
+    }
+
+    return names;
+  }
+
+
+  public MethodInfo getOverriddenMethodInfo(){
+    MethodInfo smi = null;
+    
+    if (ci != null) {
+      ClassInfo sci = ci.getSuperClass();
+      if (sci != null){
+        smi = sci.getMethod(getUniqueName(), true);
+      }
+    }
+    
+    return smi;
+  }
+  
+  /**
+   * Returns the name of the method.
+   */
+  public String getName () {
+    return name;
+  }
+
+  public String getJNIName () {
+    return Types.getJNIMangledMethodName(null, name, signature);
+  }
+  
+  public int getModifiers () {
+    return modifiers;
+  }
+  
+  /**
+   * Returns true if the method is native
+   */
+  public boolean isNative () {
+    return ((modifiers & Modifier.NATIVE) != 0);
+  }
+
+  public boolean isAbstract () {
+    return ((modifiers & Modifier.ABSTRACT) != 0);
+  }
+  
+  // overridden by NativeMethodInfo
+  public boolean isUnresolvedNativeMethod(){
+    return ((modifiers & Modifier.NATIVE) != 0);
+  }
+
+  // overridden by NativeMethodInfo
+  public boolean isJPFExecutable (){
+    return !hasAttr(NoJPFExec.class);
+  }
+
+  public int getNumberOfArguments () {
+    if (nArgs < 0) {
+      nArgs = Types.getNumberOfArguments(signature);
+    }
+
+    return nArgs;
+  }
+
+  /**
+   * Returns the size of the arguments.
+   * This returns the number of parameters passed on the stack, incl. 'this'
+   */
+  public int getNumberOfStackArguments () {
+    int n = getNumberOfArguments();
+
+    return isStatic() ? n : n + 1;
+  }
+
+  public int getNumberOfCallerStackSlots () {
+    return Types.getNumberOfStackSlots(signature, isStatic()); // includes return type
+  }
+
+  public Instruction getFirstInsn(){
+    if (code != null){
+      return code[0];
+    }
+    return null;    
+  }
+  
+  public Instruction getLastInsn() {
+    if (code != null){
+      return code[code.length-1];
+    }
+    return null;
+  }
+
+  /**
+   * do we return Object references?
+   */
+  public boolean isReferenceReturnType () {
+    int r = getReturnTypeCode();
+
+    return ((r == Types.T_REFERENCE) || (r == Types.T_ARRAY));
+  }
+
+  public byte getReturnTypeCode () {
+    if (returnType < 0) {
+      returnType = Types.getReturnBuiltinType(signature);
+    }
+
+    return returnType;
+  }
+
+  /**
+   * what is the slot size of the return value
+   */
+  public int getReturnSize() {
+    if (retSize == -1){
+      switch (getReturnTypeCode()) {
+        case Types.T_VOID:
+          retSize = 0;
+          break;
+
+        case Types.T_LONG:
+        case Types.T_DOUBLE:
+          retSize = 2;
+          break;
+
+        default:
+          retSize = 1;
+          break;
+      }
+    }
+
+    return retSize;
+  }
+
+  public Class<? extends ChoiceGenerator<?>> getReturnChoiceGeneratorType (){
+    switch (getReturnTypeCode()){
+      case Types.T_BOOLEAN:
+        return BooleanChoiceGenerator.class;
+
+      case Types.T_BYTE:
+      case Types.T_CHAR:
+      case Types.T_SHORT:
+      case Types.T_INT:
+        return IntChoiceGenerator.class;
+
+      case Types.T_LONG:
+        return LongChoiceGenerator.class;
+
+      case Types.T_FLOAT:
+        return FloatChoiceGenerator.class;
+
+      case Types.T_DOUBLE:
+        return DoubleChoiceGenerator.class;
+
+      case Types.T_ARRAY:
+      case Types.T_REFERENCE:
+      case Types.T_VOID:
+        return ReferenceChoiceGenerator.class;
+    }
+
+    return null;
+  }
+
+  /**
+   * Returns the signature of the method.
+   */
+  public String getSignature () {
+    return signature;
+  }
+
+  @Override
+  public String getGenericSignature() {
+    return genericSignature;
+  }
+
+  @Override
+  public void setGenericSignature(String sig){
+    genericSignature = sig;
+  }
+
+  /**
+   * Returns true if the method is static.
+   */
+  public boolean isStatic () {
+    return ((modifiers & Modifier.STATIC) != 0);
+  }
+
+  /**
+   * is this a public method
+   */
+  public boolean isPublic() {
+    return ((modifiers & Modifier.PUBLIC) != 0);
+  }
+  
+  public boolean isPrivate() {
+    return ((modifiers & Modifier.PRIVATE) != 0);
+  }
+  
+  public boolean isProtected() {
+    return ((modifiers & Modifier.PROTECTED) != 0);
+  }
+
+  /**
+   * Returns true if the method is synchronized.
+   */
+  public boolean isSynchronized () {
+    return ((modifiers & Modifier.SYNCHRONIZED) != 0);
+  }
+
+  // <2do> these modifiers are still java.lang.reflect internal and not
+  // supported by public Modifier methods, but since we want to keep this 
+  // similar to the Method reflection and we get the modifiers from the
+  // classfile we implement this with explicit values
+  
+  public boolean isSynthetic(){
+    return ((modifiers & 0x00001000) != 0);    
+  } 
+  public boolean isVarargs(){
+    return ((modifiers & 0x00000080) != 0);        
+  }
+  
+  /*
+   * is this from a classfile or was it created by JPF (and hence should not
+   * be visible in stacktraces etc)
+   */
+  public boolean isJPFInternal(){
+    // note this has a different meaning than Method.isSynthetic(), which
+    // is defined in VM spec 4.7.8. What we mean here is that this MethodInfo
+    // is not associated with any class (such as direct call MethodInfos), but
+    // there might be more in the future
+    return (ci == null);
+  }
+  
+  public String getUniqueName () {
+    return uniqueName;
+  }
+  
+  public boolean hasCode(){
+    return (code != null);
+  }
+  
+  public boolean hasEmptyBody (){
+    // only instruction is a return
+    return (code.length == 1 && (code[0] instanceof ReturnInstruction));
+  }
+
+
+  //--- parameter annotations
+  //<2do> these are going away
+  protected void startParameterAnnotations(int annotationCount){
+    parameterAnnotations = new AnnotationInfo[annotationCount][];
+  }
+  protected void setParameterAnnotations(int index, AnnotationInfo[] ai){
+    parameterAnnotations[index] = ai;
+  }
+  protected void finishParameterAnnotations(){
+    // nothing
+  }
+
+  public void setParameterAnnotations (AnnotationInfo[][] parameterAnnotations){
+    this.parameterAnnotations = parameterAnnotations;
+  }
+  
+  //--- thrown exceptions
+  //<2do> these are going away
+  protected void startTrownExceptions (int exceptionCount){
+    thrownExceptionClassNames = new String[exceptionCount];
+  }
+  protected void setException (int index, String exceptionType){
+    thrownExceptionClassNames[index] = Types.getClassNameFromTypeName(exceptionType);
+  }
+  protected void finishThrownExceptions(){
+    // nothing
+  }
+
+  public void setThrownExceptions (String[] exceptions){
+    thrownExceptionClassNames = exceptions;
+  }
+  
+
+  //--- exception handler table initialization
+  //<2do> these are going away
+  protected void startExceptionHandlerTable (int handlerCount){
+    exceptionHandlers = new ExceptionHandler[handlerCount];
+  }
+  protected void setExceptionHandler (int index, int startPc, int endPc, int handlerPc, String catchType){
+    exceptionHandlers[index] = new ExceptionHandler(catchType, startPc, endPc, handlerPc);
+  }
+  protected void finishExceptionHandlerTable(){
+    // nothing
+  }
+
+  public void setExceptionHandlers (ExceptionHandler[] handlers){
+    exceptionHandlers = handlers;
+  }
+  
+  //--- local var table initialization
+  // <2do> these are going away
+  protected void startLocalVarTable (int localVarCount){
+    localVars = new LocalVarInfo[localVarCount];
+  }
+  protected void setLocalVar(int index, String varName, String descriptor, int scopeStartPc, int scopeEndPc, int slotIndex){
+    localVars[index] = new LocalVarInfo(varName, descriptor, "", scopeStartPc, scopeEndPc, slotIndex);
+  }
+  protected void finishLocalVarTable(){
+    // nothing to do
+  }
+
+  public void setLocalVarTable (LocalVarInfo[] locals){
+    localVars = locals;
+  }
+  
+  public void setLocalVarAnnotations (){
+    if (localVars != null){
+      for (VariableAnnotationInfo ai : getTargetTypeAnnotations(VariableAnnotationInfo.class)){
+        for (int i = 0; i < ai.getNumberOfScopeEntries(); i++) {
+          for (LocalVarInfo lv : localVars) {
+            if (lv.getStartPC() == ai.getStartPC(i) && lv.getSlotIndex() == ai.getSlotIndex(i)) {
+              lv.addTypeAnnotation(ai);
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  public boolean hasTypeAnnotatedLocalVars (){
+    if (localVars != null){
+      for (LocalVarInfo lv : localVars){
+        if (lv.hasTypeAnnotations()){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  public List<LocalVarInfo> getTypeAnnotatedLocalVars (){
+    List<LocalVarInfo> list = null;
+    
+    if (localVars != null){
+      for (LocalVarInfo lv : localVars){
+        if (lv.hasTypeAnnotations()){
+          if (list == null){
+            list = new ArrayList<LocalVarInfo>();
+          }
+          list.add(lv);
+        }
+      }
+    }
+    
+    if (list == null){
+      list = Collections.emptyList();
+    }
+    
+    return list;
+  }
+  
+  public List<LocalVarInfo> getTypeAnnotatedLocalVars (String annotationClsName){
+    List<LocalVarInfo> list = null;
+    
+    if (localVars != null){
+      for (LocalVarInfo lv : localVars){
+        AbstractTypeAnnotationInfo tai = lv.getTypeAnnotation(annotationClsName);
+        if (tai != null){
+          if (list == null){
+            list = new ArrayList<LocalVarInfo>();
+          }
+          list.add(lv);
+        }
+      }
+    }
+    
+    if (list == null){
+      list = Collections.emptyList();
+    }
+    
+    return list;
+  }
+  
+  
+  //--- line number table initialization
+  // <2do> these are going away
+  protected void startLineNumberTable(int lineNumberCount){
+    int len = code.length;
+    int[] ln = new int[len];
+
+    lineNumbers = ln;
+  }
+  protected void setLineNumber(int index, int lineNumber, int startPc){
+    int len = code.length;
+    int[] ln = lineNumbers;
+
+    for (int i=0; i<len; i++){
+      Instruction insn = code[i];
+      int pc = insn.getPosition();
+
+      if (pc == startPc){ // this is the first insn with this line number
+        ln[i] = lineNumber;
+        return;
+      }
+    }
+  }
+  protected void finishLineNumberTable (){
+    int len = code.length;
+    int[] ln = lineNumbers;
+    int lastLine = ln[0];
+
+    for (int i=1; i<len; i++){
+      if (ln[i] == 0){
+        ln[i] = lastLine;
+      } else {
+        lastLine = ln[i];
+      }
+    }
+  }
+
+  /**
+   * note - this depends on that we already have a code array
+   * and that the lines/startPcs are sorted (monotonic increasing)
+   */
+  public void setLineNumbers (int[] lines, int[] startPcs){
+    int j=0;
+    int lastLine = -1;
+    
+    int len = code.length;
+    int[] ln = new int[len];
+
+    for (int i=0; i<len; i++){
+      Instruction insn = code[i];
+      int pc = insn.getPosition();
+      
+      if ((j < startPcs.length) && pc == startPcs[j]){
+        lastLine = lines[j];
+        j++;
+      }
+      
+      ln[i] = lastLine;
+    }
+    
+    lineNumbers = ln;
+  }
+
+  /**
+   * this version takes an already expanded line number array which has to be of
+   * the same size as the code array
+   */
+  public void setLineNumbers (int[] lines){
+    if (lines.length != code.length){
+      throw new JPFException("inconsitent code/line number size");
+    }
+    lineNumbers = lines;
+  }
+  
+  @Override
+  public String toString() {
+    return "MethodInfo[" + getFullName() + ']';
+  }
+  
+  // for debugging purposes
+  public void dump(){
+    System.out.println("--- " + this);
+    for (int i = 0; i < code.length; i++) {
+      System.out.printf("%2d [%d]: %s\n", i, code[i].getPosition(), code[i].toString());
+    }
+  }
+
+  /**
+   * Creates a method for a given class, by cloning this MethodInfo
+   * and all the instructions belong to the method
+   */
+  public MethodInfo getInstanceFor(ClassInfo ci) {
+    MethodInfo clone;
+
+    try {
+      clone = (MethodInfo)super.clone();
+      clone.ci = ci;
+
+      clone.globalId = mthTable.size();
+      mthTable.add(this);
+
+      if(code == null) {
+        clone.code = null;
+      } else {
+        clone.code = new Instruction[code.length];
+
+        for(int i=0; i<code.length; i++) {
+          clone.code[i] = code[i].typeSafeClone(clone);
+        }
+      }
+
+    } catch (CloneNotSupportedException cnsx){
+      cnsx.printStackTrace();
+      return null;
+    }
+
+    return clone;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/MethodLocator.java b/src/main/gov/nasa/jpf/vm/MethodLocator.java
new file mode 100644 (file)
index 0000000..56bcaf4
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * an interface to lookup methods
+ */
+public interface MethodLocator {
+  boolean match (MethodInfo mi);
+}
diff --git a/src/main/gov/nasa/jpf/vm/Monitor.java b/src/main/gov/nasa/jpf/vm/Monitor.java
new file mode 100644 (file)
index 0000000..0571ed7
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+
+/**
+ * Represents the variable, hash-collapsed pooled data associated with an object
+ * that is not related to the object values (->Fields), but to the use of the
+ * object for synchronization purposes (locks and signals).
+ * 
+ */
+public class Monitor implements Cloneable {
+  
+  static ThreadInfo[] emptySet = new ThreadInfo[0];
+  
+  /** the thread owning the lock */
+  private ThreadInfo lockingThread;
+
+  /** the nesting level for recursive lock acquisition */
+  private int lockCount;
+  
+  /** 
+   * the list of threads that try to acquire the lock (can be in blocked, waiting,
+   * interrupted or running state).
+   */
+  ThreadInfo[] lockedThreads;
+
+  /**
+   * Creates a new empty monitor.
+   */
+  public Monitor () {
+    lockingThread = null;
+    lockCount = 0;    
+    lockedThreads = emptySet;
+  }
+
+  private Monitor (ThreadInfo locking, int count, ThreadInfo[] locked) {
+    lockingThread = locking;
+    lockCount = count;
+    lockedThreads = locked.clone();
+    Arrays.sort(lockedThreads);
+  }
+  
+  public void printFields (PrintWriter pw) {
+    int i;
+
+    pw.print(this);
+    pw.print(" [");
+    if (lockingThread != null) {
+      pw.print( "locked by: ");
+      pw.print( lockingThread.getName());
+    } else {
+      pw.print( "unlocked");
+    }
+    
+    pw.print(", lockCount: ");
+    pw.print( lockCount);
+    
+    pw.print(", locked: {");
+    for (i=0; i<lockedThreads.length; i++) {
+      if (i > 0) pw.print(',');
+      pw.print(lockedThreads[i].getName());
+      pw.print(':');
+      pw.print(lockedThreads[i].getStateName());
+    }
+    pw.println("}]");
+  }
+  
+  // for debugging purposes
+  public void dump() {
+    PrintWriter pw = new PrintWriter(System.out);
+    printFields(pw);
+    pw.flush();
+  }
+  
+  Monitor cloneWithLocked (ThreadInfo ti) {
+    return new Monitor(lockingThread, lockCount, add(lockedThreads, ti));
+  }
+
+  Monitor cloneWithoutLocked (ThreadInfo ti) {
+    return new Monitor(lockingThread, lockCount, remove(lockedThreads, ti));
+  }
+
+  @Override
+  public Monitor clone () {
+    try {
+      // no need to clone the empty set (which should be the majority of cases)
+      Monitor m = (Monitor) super.clone();
+      if (lockedThreads != emptySet) {
+        m.lockedThreads = lockedThreads.clone();
+      }
+      return m;
+      
+    } catch (CloneNotSupportedException cnsx) {
+      throw new InternalError("should not happen");
+    }
+  }
+  
+  
+  /**
+   * Compares to another object.
+   */
+  @Override
+  public boolean equals (Object o) {
+    if (o == null) {
+      return false;
+    }
+
+    if (!(o instanceof Monitor)) {
+      return false;
+    }
+
+    Monitor m = (Monitor) o;
+
+    if (lockingThread != m.getLockingThread()) {
+      return false;
+    }
+
+    if (lockCount != m.getLockCount()) {
+      return false;
+    }
+
+    ThreadInfo[] list = m.lockedThreads;
+    if (lockedThreads.length != list.length) {
+      return false;
+    }
+
+    for (int i = 0; i < lockedThreads.length; i++) {
+      if (lockedThreads[i] != list[i]) {
+        return false;
+      }
+    }
+
+    return true;
+  }
+  
+
+  public void hash (HashData hd) {
+    if (lockingThread != null) {
+      hd.add(lockingThread.getId());
+    }
+    
+    hd.add(lockCount);
+    
+    for (int i = 0; i < lockedThreads.length; i++) {
+      hd.add(lockedThreads[i].getId());
+    }    
+  }
+
+  
+  @Override
+  public int hashCode () {
+    HashData hd = new HashData();
+    hash(hd);
+    return hd.getValue();
+  }
+  
+
+  /**
+   * Returns the number of nested locks acquired.
+   */
+  public int getLockCount () {
+    return lockCount;
+  }
+
+
+  /**
+   * Returns the identifier of the thread holding the lock.
+   */
+  public ThreadInfo getLockingThread () {
+    return lockingThread;
+  }
+
+
+  /**
+   * Returns the list of locked threads
+   */ 
+  public ThreadInfo[] getLockedThreads() {
+    return lockedThreads;
+  }
+  
+
+  public boolean hasLockedThreads () {
+    return (lockedThreads.length > 0);
+  }
+  
+  public boolean hasWaitingThreads () {
+    for (int i=0; i<lockedThreads.length; i++) {
+      if (lockedThreads[i].isWaiting()) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public int getNumberOfWaitingThreads() {
+    int n=0;
+
+    for (ThreadInfo ti : lockedThreads){
+      if (ti.isWaiting()){
+        n++;
+      }
+    }
+
+    return n;
+  }
+
+
+  public ThreadInfo[] getWaitingThreads() {
+    int n = getNumberOfWaitingThreads();
+
+    if (n > 0){
+      ThreadInfo[] list = new ThreadInfo[n];
+      int i=0;
+      for (int j=0; j<lockedThreads.length && i<n; j++){
+        ThreadInfo ti = lockedThreads[j];
+        if (ti.isWaiting()){
+          list[i++] = ti;
+        }
+      }
+
+      return list;
+
+    } else {
+      return emptySet;
+    }
+  }
+
+  public int getNumberOfBlockedThreads() {
+    int n=0;
+
+    for (ThreadInfo ti : lockedThreads){
+      if (ti.isBlocked()){
+        n++;
+      }
+    }
+
+    return n;
+  }
+
+
+  public ThreadInfo[] getBlockedThreads() {
+    int n = getNumberOfBlockedThreads();
+
+    if (n > 0){
+      ThreadInfo[] list = new ThreadInfo[n];
+      int i=0;
+      for (int j=0; j<lockedThreads.length && i<n; j++){
+        ThreadInfo ti = lockedThreads[j];
+        if (ti.isBlocked()){
+          list[i++] = ti;
+        }
+      }
+
+      return list;
+
+    } else {
+      return emptySet;
+    }
+  }
+
+
+  public int getNumberOfBlockedOrWaitingThreads() {
+    int n=0;
+
+    for (ThreadInfo ti : lockedThreads){
+      if (ti.isBlocked() || ti.isWaiting()){
+        n++;
+      }
+    }
+
+    return n;
+  }
+
+
+  public ThreadInfo[] getBlockedOrWaitingThreads() {
+    int n = getNumberOfBlockedThreads();
+
+    if (n > 0){
+      ThreadInfo[] list = new ThreadInfo[n];
+      int i=0;
+      for (int j=0; j<lockedThreads.length && i<n; j++){
+        ThreadInfo ti = lockedThreads[j];
+        if (ti.isBlocked() || ti.isWaiting()){
+          list[i++] = ti;
+        }
+      }
+
+      return list;
+
+    } else {
+      return emptySet;
+    }
+  }
+
+  
+  /**
+   * Returns true if it is possible to lock the monitor.
+   */
+  public boolean canLock (ThreadInfo th) {
+    if (lockingThread == null) {
+      return true;
+    }
+
+    return (lockingThread == th);
+  }
+
+
+  void setLockingThread (ThreadInfo ti) {
+    lockingThread = ti;
+  }
+  
+
+  void incLockCount () {
+    lockCount++;
+  }
+  
+  
+  void decLockCount () {
+    assert lockCount > 0 : "negative lockCount";
+    lockCount--;
+  }
+  
+  
+  void setLockCount (int lc) {
+    assert lc >= 0 : "attempt to set negative lockCount";
+    lockCount = lc;
+  }
+  
+  public int objectHashCode () {
+    return super.hashCode();
+  }
+
+  void resetLockedThreads () {
+    lockedThreads = emptySet;
+  }
+
+  public boolean isLocking(ThreadInfo ti){
+    if (lockedThreads != null){
+      for (ThreadInfo lti : lockedThreads){
+        if (lti == ti){
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+
+  
+  static boolean containsLocked(ThreadInfo[] list, ThreadInfo ti){
+    int len = list.length;
+    for (int i=0; i<len; i++){
+      if (list[i] == ti){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  static ThreadInfo[] add (ThreadInfo[] list, ThreadInfo ti) {
+    int len = list.length;
+    
+    //--- first, check if its already there
+    if (containsLocked(list, ti)){
+      // this is required because interrupted parks/joins can try to
+      // re-park/join from their respective handlers (they don't hold locks) 
+      return list;
+    }
+        
+    ThreadInfo[] newList = new ThreadInfo[len+1];
+
+    int pos = 0;
+    for (; pos < len && ti.compareTo(list[pos]) > 0; pos++) {
+      newList[pos] = list[pos];
+    }
+    
+    newList[pos] = ti;
+    for (; pos < len; pos++) {
+      newList[pos+1] = list[pos];
+    }
+
+    return newList;
+  }
+  
+  void addLocked (ThreadInfo ti) {
+    lockedThreads = add(lockedThreads, ti);
+  }
+  
+  static ThreadInfo[] remove (ThreadInfo[] list, ThreadInfo ti) {
+    int len = list.length;
+
+    if (len == 0) { // nothing to remove from
+      return list;
+      
+    } else if (len == 1) {  // one element list optimization
+      if (list[0] == ti) {
+        return emptySet;
+      } else {
+        return list;
+      }
+    } else {
+      
+      //--- first, check if its already there
+      if (!containsLocked(list, ti)) {
+        // no known case yet, but we keep it symmetric
+        // <2do> maybe worth a warning
+        return list;
+      }
+      
+      for (int i=0; i<len; i++) {
+        if (list[i] == ti) {
+          int newLen = len-1;
+          ThreadInfo[] newList = new ThreadInfo[newLen];
+          if (i > 0) {
+            System.arraycopy(list, 0, newList, 0, i);
+          }
+          if (i < newLen) {
+            System.arraycopy(list, i+1, newList, i, newLen-i);
+          }
+          return newList;
+        }
+      }
+      // else, not in list:
+      return list;
+    }
+  }
+  
+  void removeLocked (ThreadInfo ti) {
+    lockedThreads = remove(lockedThreads, ti);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/MultiProcessVM.java b/src/main/gov/nasa/jpf/vm/MultiProcessVM.java
new file mode 100644 (file)
index 0000000..644ca18
--- /dev/null
@@ -0,0 +1,466 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.Predicate;
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+
+import gov.nasa.jpf.vm.choice.ThreadChoiceFromSet;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A VM implementation that simulates running multiple applications within the same
+ * JPF process (centralized model checking of distributed applications).
+ * This is achieved by executing each application in a separate thread group,
+ * using separate SystemClassLoader instances to ensure proper separation of types / static fields.
+ * 
+ * 
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * To use this jpf.properties includes,
+ *              vm.class = gov.nasa.jpf.vm.MultiProcessVM 
+ */
+public class MultiProcessVM extends VM {
+
+  static final int MAX_APP = 32;
+
+  ApplicationContext[] appCtxs;
+  
+  MultiProcessPredicate runnablePredicate;
+  MultiProcessPredicate appTimedoutRunnablePredicate;
+  MultiProcessPredicate appDaemonRunnablePredicate;
+  MultiProcessPredicate appPredicate;
+  protected Predicate<ThreadInfo> systemInUsePredicate;
+  
+  public MultiProcessVM (JPF jpf, Config conf) {
+    super(jpf, conf);
+    
+    appCtxs = createApplicationContexts();
+    
+    initializePredicates();
+  }
+  
+  void initializePredicates() {
+    runnablePredicate = new MultiProcessPredicate() {
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        return (t.isRunnable() && this.appCtx == t.appCtx);
+      }
+    };
+    
+    appTimedoutRunnablePredicate = new MultiProcessPredicate() {
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        return (this.appCtx == t.appCtx && t.isTimeoutRunnable());
+      }
+    }; 
+    
+    appDaemonRunnablePredicate = new MultiProcessPredicate() {
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        return (this.appCtx == t.appCtx && t.isRunnable() && t.isDaemon());
+      }
+    };
+    
+    appPredicate = new MultiProcessPredicate() {
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        return (this.appCtx == t.appCtx);
+      }
+    };
+    
+    
+    // this predicates collects those finalizers which are either runnable or
+    // have some queued objects to process.
+    systemInUsePredicate = new Predicate<ThreadInfo> () {
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        boolean isTrue = false;
+        if(t.isSystemThread()) {
+          if(t.isRunnable()) {
+            isTrue = true;
+          } else {
+            FinalizerThreadInfo finalizer = (FinalizerThreadInfo) t;
+            isTrue = !finalizer.isIdle();
+          }
+        }
+        return isTrue;
+      }
+    };
+  }
+
+  /**
+   * <2do> this should also handle command line specs such as "jpf ... tgt1 tgt1_arg ... -- tgt2 tgt2_arg ... 
+   */
+  ApplicationContext[] createApplicationContexts(){
+    String[] targets;
+
+    int replicate = config.getInt("target.replicate", 0);
+    if(replicate>0) {
+      String target = config.getProperty("target");
+      targets = new String[replicate];
+      for(int i=0; i<replicate; i++) {
+        targets[i] = target;
+      }
+    } else {
+      targets = config.getStringEnumeration("target", MAX_APP);
+    }
+
+    if (targets == null){
+      throw new JPFConfigException("no applications specified, check 'target.N' settings");
+    }
+    
+    ArrayList<ApplicationContext> list = new ArrayList<ApplicationContext>(targets.length);
+    for (int i=0; i<targets.length; i++){
+      if (targets[i] != null){ // there might be holes in the array
+        String clsName = targets[i];
+        if (!isValidClassName(clsName)) {
+          throw new JPFConfigException("main class not a valid class name: " + clsName);
+        }
+        
+        String argsKey;
+        String entryKey;
+        String hostKey;
+        if(replicate>0) {
+          argsKey = "target.args";
+          entryKey = "target.entry";
+          hostKey = "target.host";
+        } else {
+          argsKey = "target.args." + i;
+          entryKey = "target.entry." + i;
+          hostKey = "target.host." + i;
+        }
+        
+        String[] args = config.getCompactStringArray(argsKey);
+        if (args == null){
+          args = EMPTY_ARGS;
+        }
+        
+        String mainEntry = config.getString(entryKey, "main([Ljava/lang/String;)V");
+        
+        String host = config.getString(hostKey, "localhost");
+        
+        SystemClassLoaderInfo sysCli = createSystemClassLoaderInfo(list.size());
+    
+        ApplicationContext appCtx = new ApplicationContext( i, clsName, mainEntry, args, host, sysCli);
+        list.add( appCtx);
+      }
+    }
+    
+    return list.toArray(new ApplicationContext[list.size()]);
+  }
+
+  @Override
+  public boolean initialize(){
+    try {
+      ThreadInfo tiFirst = null;
+      
+      for (int i=0; i<appCtxs.length; i++){
+        ApplicationContext appCtx = appCtxs[i];
+    
+        // this has to happen before we load the startup classes during initializeMainThread
+        scheduler.initialize(this, appCtx);
+    
+        ThreadInfo tiMain = initializeMainThread(appCtx, i);
+        initializeFinalizerThread(appCtx, appCtxs.length+i);
+        
+        if (tiMain == null) {
+          return false; // bail out
+        }
+        if (tiFirst == null){
+          tiFirst = tiMain;
+        }
+      }
+
+      initSystemState(tiFirst);
+      initialized = true;
+      notifyVMInitialized();
+      
+      return true;
+      
+    } catch (JPFConfigException cfe){
+      log.severe(cfe.getMessage());
+      return false;
+    } catch (ClassInfoException cie){
+      log.severe(cie.getMessage());
+      return false;
+    }
+    // all other exceptions are JPF errors that should cause stack traces
+  }
+
+  @Override
+  public int getNumberOfApplications(){
+    return appCtxs.length;
+  }
+    
+  @Override
+  public ApplicationContext getApplicationContext(int objRef) {
+    VM vm = VM.getVM();
+
+    ClassInfo ci = vm.getElementInfo(objRef).getClassInfo();
+    while(!ci.isObjectClassInfo()) {
+      ci = ci.getSuperClass();
+    }
+
+    ClassLoaderInfo sysLoader = ci.getClassLoaderInfo();
+    ApplicationContext[] appContext = vm.getApplicationContexts();
+    
+    for(int i=0; i<appContext.length; i++) {
+      if(appContext[i].getSystemClassLoader() == sysLoader) {
+        return appContext[i];
+      }
+    }
+    return null;
+  }
+  
+  @Override
+  public ApplicationContext[] getApplicationContexts(){
+    return appCtxs;
+  }
+
+  @Override
+  public ApplicationContext getCurrentApplicationContext(){
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    if (ti != null){
+      return ti.getApplicationContext();
+    } else {
+      // return the last defined one
+      return appCtxs[appCtxs.length-1];
+    }
+  }
+
+  
+  @Override
+  public String getSUTName() {
+    StringBuilder sb = new StringBuilder();
+    
+    for (int i=0; i<appCtxs.length; i++){
+      if (i>0){
+        sb.append("+");
+      }
+      sb.append(appCtxs[i].mainClassName);
+    }
+    
+    return sb.toString();
+  }
+
+  @Override
+  public String getSUTDescription(){
+    StringBuilder sb = new StringBuilder();
+    
+    for (int i=0; i<appCtxs.length; i++){
+      if (i>0){
+        sb.append('+'); // "||" would be more fitting, but would screw up filenames
+      }
+
+      ApplicationContext appCtx = appCtxs[i];
+      sb.append(appCtx.mainClassName);
+      sb.append('.');
+      sb.append(Misc.upToFirst(appCtx.mainEntry, '('));
+
+      sb.append('(');
+      String[] args = appCtx.args;
+      for (int j = 0; j < args.length; j++) {
+        if (j > 0) {
+          sb.append(',');
+        }
+        sb.append('"');
+        sb.append(args[j]);
+        sb.append('"');
+      }
+      sb.append(')');
+    }
+    
+    return sb.toString();
+  }
+
+  
+  @Override
+  public boolean isSingleProcess() {
+    return false;
+  }
+
+  @Override
+  public boolean isEndState () {
+    boolean hasNonTerminatedDaemon = getThreadList().hasAnyMatching(getUserLiveNonDaemonPredicate());
+    boolean hasRunnable = getThreadList().hasAnyMatching(getUserTimedoutRunnablePredicate());
+    boolean isEndState = !(hasNonTerminatedDaemon && hasRunnable);
+    
+    if(processFinalizers) {
+      if(isEndState) {
+        int n = getThreadList().getMatchingCount(systemInUsePredicate);
+        if(n>0) {
+          return false;
+        }
+      }
+    }
+    
+    return isEndState;
+  }
+
+  @Override
+  // Note - for now we just check for global deadlocks not the local ones which occur within a
+  // scope of a single progress
+  public boolean isDeadlocked () { 
+    boolean hasNonDaemons = false;
+    boolean hasBlockedThreads = false;
+
+    if (ss.isBlockedInAtomicSection()) {
+      return true; // blocked in atomic section
+    }
+
+    ThreadInfo[] threads = getThreadList().getThreads();
+    int len = threads.length;
+
+    boolean hasUserThreads = false;
+    for (int i=0; i<len; i++){
+      ThreadInfo ti = threads[i];
+      
+      if (ti.isAlive()) {
+        hasNonDaemons |= !ti.isDaemon();
+
+        // shortcut - if there is at least one runnable, we are not deadlocked
+        if (ti.isTimeoutRunnable()) { // willBeRunnable() ?
+          return false;
+        }
+        
+        if(!ti.isSystemThread()) {
+          hasUserThreads = true;
+        }
+
+        // means it is not NEW or TERMINATED, i.e. live & blocked
+        hasBlockedThreads = true;
+      }
+    }
+
+    boolean isDeadlock = hasNonDaemons && hasBlockedThreads;
+    
+    if(processFinalizers && isDeadlock && !hasUserThreads) {
+      // all threads are blocked, system threads. If at least one of them 
+      // is in-use, then this is a deadlocked state.
+      return (getThreadList().getMatchingCount(systemInUsePredicate)>0);
+    }
+    
+    return isDeadlock;
+  }
+  
+  @Override
+  public void terminateProcess (ThreadInfo ti) {
+    SystemState ss = getSystemState();
+    ThreadInfo[] appThreads = getThreadList().getAllMatching(getAppPredicate(ti));
+    ThreadInfo finalizerTi = null;
+
+    for (int i = 0; i < appThreads.length; i++) {
+      ThreadInfo t = appThreads[i];
+      
+      // if finalizers have to be processed, FinalizerThread is not killed at this 
+      // point. We need to keep it around in case fianlizable objects are GCed after 
+      // System.exit() returns.
+      if(processFinalizers && t.isSystemThread()) {
+        finalizerTi = t;
+      } else {
+        // keep the stack frames around, so that we can inspect the snapshot
+        t.setTerminated();
+      }
+    }
+    
+    ThreadList tl = getThreadList();
+    
+    ChoiceGenerator<ThreadInfo> cg;
+    if (tl.hasAnyMatching(getAlivePredicate())) {
+      ThreadInfo[] runnables = getThreadList().getAllMatching(getTimedoutRunnablePredicate());
+      cg = new ThreadChoiceFromSet( "PROCESS_TERMINATE", runnables, true);
+      GlobalSchedulingPoint.setGlobal(cg);
+      
+    } else {
+      cg = new BreakGenerator("exit", ti, true);
+    }
+    
+    ss.setMandatoryNextChoiceGenerator(cg, "exit without break CG");
+    
+    // if there is a finalizer thread, we have to run the last GC, to queue finalizable objects, if any
+    if(finalizerTi != null) {
+      assert finalizerTi.isAlive();
+      activateGC();
+    }
+  }
+  
+  @Override
+  public Map<Integer,IntTable<String>> getInitialInternStringsMap() {
+    Map<Integer,IntTable<String>> interns = new HashMap<Integer,IntTable<String>>();
+     
+    for(ApplicationContext appCtx:getApplicationContexts()) {
+      interns.put(appCtx.getId(), appCtx.getInternStrings());
+    }
+    
+    return interns;
+  }
+  
+  //---------- Predicates used to query threads from ThreadList ----------//
+  
+  abstract class MultiProcessPredicate implements Predicate<ThreadInfo> {
+    ApplicationContext appCtx;
+
+    public void setAppCtx (ApplicationContext appCtx) { 
+      this.appCtx = appCtx; 
+    }
+  }
+  
+  @Override
+  public Predicate<ThreadInfo> getRunnablePredicate() {
+    runnablePredicate.setAppCtx(getCurrentApplicationContext());
+    return runnablePredicate;
+  }
+  
+  @Override
+  public Predicate<ThreadInfo> getAppTimedoutRunnablePredicate() {
+    appTimedoutRunnablePredicate.setAppCtx(getCurrentApplicationContext());
+    return appTimedoutRunnablePredicate;
+  }
+  
+  @Override
+  public Predicate<ThreadInfo> getDaemonRunnablePredicate() {
+    appDaemonRunnablePredicate.setAppCtx(getCurrentApplicationContext());
+    return appDaemonRunnablePredicate;
+  }
+  
+  /**
+   * Returns a predicate used to obtain all the threads that belong to the same application as ti
+   */
+  Predicate<ThreadInfo> getAppPredicate (final ThreadInfo ti){
+    appPredicate.setAppCtx(ti.getApplicationContext());
+    return appPredicate;
+  }
+  
+  // ---------- Methods for handling finalizers ---------- //
+
+  @Override
+  void updateFinalizerQueues () {
+    for(ApplicationContext appCtx: appCtxs) {
+      appCtx.getFinalizerThread().processNewFinalizables();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/NamedFields.java b/src/main/gov/nasa/jpf/vm/NamedFields.java
new file mode 100644 (file)
index 0000000..bc3718a
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+/**
+ * value container for non-array classes
+ */
+public class NamedFields extends Fields {
+
+  /** this is where we store the instance data. Since field types are
+   * heterogenous, we have to map everything into int
+   */
+  protected int[] values;
+
+  public NamedFields (int dataSize) {
+    values = new int[dataSize];
+  }
+
+  @Override
+  public int[] asFieldSlots() {
+    return values;
+  }
+
+  /**
+   * give an approximation of the heap size in bytes - we assume fields are word
+   * aligned, hence the number of values*4 should be good. Note that this is
+   * overridden by ArrayFields (arrays would be packed)
+   */
+  @Override
+  public int getHeapSize () {
+    return values.length*4;
+  }
+
+  // our low level getters and setters
+  @Override
+  public int getIntValue (int index) {
+    return values[index];
+  }
+
+  public boolean isEqual(Fields o, int off, int len, int otherOff) {
+    if (o instanceof NamedFields) {
+      NamedFields other = (NamedFields) o;
+      int iEnd = off + len;
+      int jEnd = otherOff + len;
+      int[] v = other.values;
+
+      if ((iEnd > values.length) || (jEnd > v.length)) {
+        return false;
+      }
+
+      for (int i = off, j = otherOff; i < iEnd; i++, j++) {
+        if (values[i] != v[j]) {
+          return false;
+        }
+      }
+
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  // same as above, just here to make intentions clear
+  @Override
+  public int getReferenceValue (int index) {
+    return values[index];
+  }
+
+  @Override
+  public long getLongValue (int index) {
+    return Types.intsToLong(values[index + 1], values[index]);
+  }
+
+  @Override
+  public boolean getBooleanValue (int index) {
+    return Types.intToBoolean(values[index]);
+  }
+
+  @Override
+  public byte getByteValue (int index) {
+    return (byte) values[index];
+  }
+
+  @Override
+  public char getCharValue (int index) {
+    return (char) values[index];
+  }
+
+  @Override
+  public short getShortValue (int index) {
+    return (short) values[index];
+  }
+
+  // <2do> get rid of it!! this is only for internal use, to increase efficiency
+  public int[] getValues() {
+    return values;
+  }
+
+  //--- the field modifier methods (both instance and static)
+
+  @Override
+  public void setReferenceValue (int index, int newValue) {
+    values[index] = newValue;
+  }
+
+  @Override
+  public void setBooleanValue (int index, boolean newValue) {
+    values[index] = newValue ? 1 : 0;
+  }
+
+  @Override
+  public void setByteValue (int index, byte newValue) {
+    values[index] = newValue;
+  }
+
+  @Override
+  public void setCharValue (int index, char newValue) {
+    values[index] = newValue;
+  }
+
+  @Override
+  public void setShortValue (int index, short newValue) {
+    values[index] = newValue;
+  }
+
+  @Override
+  public void setFloatValue (int index, float newValue) {
+    values[index] = Types.floatToInt(newValue);
+  }
+
+  @Override
+  public void setIntValue (int index, int newValue) {
+    values[index] = newValue;
+  }
+
+  @Override
+  public void setLongValue (int index, long newValue) {
+               values[index++] = Types.hiLong(newValue);
+    values[index] = Types.loLong(newValue);
+  }
+
+  @Override
+  public void setDoubleValue (int index, double newValue) {
+    values[index++] = Types.hiDouble(newValue);
+    values[index] = Types.loDouble(newValue);
+  }
+
+
+  @Override
+  public float getFloatValue (int index) {
+    return Types.intToFloat(values[index]);
+  }
+
+  @Override
+  public double getDoubleValue (int index) {
+    return Types.intsToDouble( values[index+1], values[index]);
+  }
+
+  /**
+   * Creates a clone.
+   */
+  @Override
+  public NamedFields clone () {
+    NamedFields f = (NamedFields) cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  /**
+   * Checks for equality.
+   */
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof NamedFields) {
+      NamedFields other = (NamedFields) o;
+
+      //--- check values
+      int[] v1 = values;
+      int[] v2 = other.values;
+      int l = v1.length;
+      if (l != v2.length) {
+        return false;
+      }
+      for (int i = 0; i < l; i++) {
+        if (v1[i] != v2[i]) {
+          return false;
+        }
+      }
+      
+      return super.compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  // serialization interface
+  @Override
+  public void appendTo(IntVector v) {
+    v.append(values);
+  }
+
+
+  /**
+   * Adds some data to the computation of an hashcode.
+   */
+  @Override
+  public void hash (HashData hd) {
+    int[] v = values;
+    for (int i=0, l=v.length; i < l; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+  /**
+   * Size of the fields.
+   */
+  public int size () {
+    return values.length;
+  }
+
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder("NamedFields[");
+
+    sb.append("values=");
+    sb.append('[');
+
+    for (int i = 0; i < values.length; i++) {
+      if (i != 0) {
+        sb.append(',');
+      }
+
+      sb.append(values[i]);
+    }
+
+    sb.append(']');
+    sb.append(',');
+
+    sb.append(']');
+
+    return sb.toString();
+  }
+
+  // <2do> replace with copyTo() !!
+  public int[] getRawValues() {
+    return values;
+  }
+
+  public void copyFrom(Fields other) {
+    System.arraycopy(((NamedFields)other).values, 0, this.values, 0, values.length);
+    super.copyAttrs(other);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/NativeMethodInfo.java b/src/main/gov/nasa/jpf/vm/NativeMethodInfo.java
new file mode 100644 (file)
index 0000000..24ee41f
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFNativePeerException;
+import gov.nasa.jpf.util.JPFLogger;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+/**
+ * a MethodInfo for a native peer executed method
+ */
+public class NativeMethodInfo extends MethodInfo {
+
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.NativePeer");
+
+  static final int  MAX_NARGS = 6;
+  static Object[][]  argCache;
+
+  static {
+    argCache = new Object[MAX_NARGS][];
+
+    for (int i = 0; i < MAX_NARGS; i++) {
+      argCache[i] = new Object[i];
+    }
+  }
+
+  protected Method mth; // the native method to enter in lieu
+  protected NativePeer peer;
+
+  public NativeMethodInfo (MethodInfo mi, Method mth, NativePeer peer){
+    super(mi);  // <2do> do we want any operands or locals?
+
+    this.peer = peer;
+    this.mth = mth;
+
+    ci.setNativeCallCode(this);
+  }
+
+  public void replace( MethodInfo mi){
+    mthTable.set(mi.globalId, this);
+    mi.ci.putDeclaredMethod(this);
+  }
+  
+  @Override
+  public boolean isUnresolvedNativeMethod() {
+    // we are already a NativeMethodInfo
+    return false;
+  }
+
+  @Override
+  public boolean isMJI () {
+    return true;
+  }
+
+  @Override
+  public boolean hasEmptyBody (){
+    // how would we know
+    return false;
+  }
+
+  @Override
+  public boolean isJPFExecutable(){
+    return true; // that's our only purpose in life
+  }
+
+  public NativePeer getNativePeer() {
+    return peer;
+  }
+
+  public Method getMethod() {
+    return mth;
+  }
+
+  @Override
+  public String getStackTraceSource() {
+    if (peer != null){
+      return peer.getPeerClassName();
+    } else {
+      return "no peer";
+    }
+  }
+
+  @Override
+  public int getLineNumber (Instruction pc) {
+    return -1; // we have no line numbers
+  }
+
+  public Instruction executeNative (ThreadInfo ti) {
+    Object   ret = null;
+    Object[] args = null;
+    MJIEnv   env = ti.getMJIEnv();
+        
+    NativeStackFrame nativeFrame = (NativeStackFrame)ti.getTopFrame();
+
+    env.setCallEnvironment(this);
+
+    if (isUnsatisfiedLinkError(env)) {
+      return ti.createAndThrowException("java.lang.UnsatisfiedLinkError",
+                                        "cannot find native " + ci.getName() + '.' + getName());
+    }
+
+    try {
+      args = nativeFrame.getArguments();
+
+      // this is the reflection call into the native peer
+      ret = mth.invoke(peer, args);
+
+      if (env.hasException()) {
+        // even though we should prefer throwing normal exceptionHandlers,
+        // sometimes it might be better/required to explicitly throw
+        // something that's not wrapped into a InvocationTargetException
+        // (e.g. InterruptedException), which is why there still is a
+        // MJIEnv.throwException()
+        return ti.throwException( env.popException());
+      }
+
+      StackFrame top = ti.getTopFrame();
+//      if (top == nativeFrame){ // no roundtrips, straight return
+      if (top.originatesFrom(nativeFrame)){ // could have changed attributes
+        NativeStackFrame ntop = (NativeStackFrame)top;
+
+        if (env.isInvocationRepeated()){
+          // don't advance
+          return ntop.getPC();
+
+        } else {
+          // we don't have to do a ti.topClone() because the last insn left
+          // is NATIVERETURN. Even if a listener creates a CG on it, it won't
+          // modify its StackFrame, which is then popped anyways
+
+          ntop.setReturnValue(ret);
+          ntop.setReturnAttr(env.getReturnAttribute());
+
+          return ntop.getPC().getNext(); // that should be the NATIVERETURN
+        }
+
+      } else {
+        // direct calls from within the native method, i.e. nativeFrame is not
+        // on top anymore, but its current instruction (invoke) will be reexecuted
+        // because DirectCallStackFrames don't advance the pc of the new top top upon return
+        return top.getPC();
+      }
+
+    } catch (IllegalArgumentException iax) {
+      logger.warning(iax.toString());
+      return ti.createAndThrowException("java.lang.IllegalArgumentException",
+                                        "calling " + ci.getName() + '.' + getName());
+    } catch (IllegalAccessException ilax) {
+      logger.warning(ilax.toString());
+      return ti.createAndThrowException("java.lang.IllegalAccessException",
+                                        "calling " + ci.getName() + '.' + getName());
+    } catch (InvocationTargetException itx) {
+
+      // if loading a class throws an exception
+      if(itx.getTargetException() instanceof ClassInfoException) {
+        ClassInfoException cie = (ClassInfoException) itx.getTargetException();
+        return ti.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
+      }
+
+      if (itx.getTargetException() instanceof UncaughtException) {  // Native methods could 
+        throw (UncaughtException) itx.getTargetException();
+      } 
+       
+      // this will catch all exceptionHandlers thrown by the native method execution
+      // we don't try to hand them back to the application
+      throw new JPFNativePeerException("exception in native method "
+          + ci.getName() + '.' + getName(), itx.getTargetException());
+    }
+  }
+
+  protected boolean isUnsatisfiedLinkError(MJIEnv env){
+    return(mth == null);
+  }
+
+  /**
+   * Get and convert the native method parameters off the ThreadInfo stack.
+   * Use the MethodInfo parameter type info for this (not the reflect.Method
+   * type array), or otherwise we won't have any type check
+   */
+  protected Object[] getArguments (ThreadInfo ti) {
+    // these are just local refs to speed up
+    int      nArgs = getNumberOfArguments();
+    byte[]   argTypes = getArgumentTypes();
+
+    //Object[] a = getArgArray(nArgs + 2);
+    Object[] a = new Object[nArgs+2];
+
+    int      stackOffset;
+    int      i, j, k;
+    int      ival;
+    long     lval;
+    StackFrame caller = ti.getTopFrame();
+
+
+    for (i = 0, stackOffset = 0, j = nArgs + 1, k = nArgs - 1;
+         i < nArgs;
+         i++, j--, k--) {
+      switch (argTypes[k]) {
+      case Types.T_BOOLEAN:
+        ival = caller.peek(stackOffset);
+        a[j] = Boolean.valueOf(Types.intToBoolean(ival));
+
+        break;
+
+      case Types.T_BYTE:
+        ival = caller.peek(stackOffset);
+        a[j] = Byte.valueOf((byte) ival);
+
+        break;
+
+      case Types.T_CHAR:
+        ival = caller.peek(stackOffset);
+        a[j] = Character.valueOf((char) ival);
+
+        break;
+
+      case Types.T_SHORT:
+        ival = caller.peek(stackOffset);
+        a[j] = new Short((short) ival);
+
+        break;
+
+      case Types.T_INT:
+        ival = caller.peek(stackOffset);
+        a[j] = new Integer(ival);
+
+        break;
+
+      case Types.T_LONG:
+        lval = caller.peekLong(stackOffset);
+        stackOffset++; // 2 stack words
+        a[j] = new Long(lval);
+
+        break;
+
+      case Types.T_FLOAT:
+        ival = caller.peek(stackOffset);
+        a[j] = new Float(Types.intToFloat(ival));
+
+        break;
+
+      case Types.T_DOUBLE:
+        lval = caller.peekLong(stackOffset);
+        stackOffset++; // 2 stack words
+        a[j] = new Double(Types.longToDouble(lval));
+
+        break;
+
+      default:
+        // NOTE - we have to store T_REFERENCE as an Integer, because
+        // it shows up in our native method as an 'int'
+        ival = caller.peek(stackOffset);
+        a[j] = new Integer(ival);
+      }
+
+      stackOffset++;
+    }
+
+    //--- set  our standard MJI header arguments
+    if (isStatic()) {
+      a[1] = new Integer(ci.getClassObjectRef());
+    } else {
+      a[1] = new Integer(ti.getCalleeThis(this));
+    }
+
+    a[0] = ti.getMJIEnv();
+
+    return a;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/NativePeer.java b/src/main/gov/nasa/jpf/vm/NativePeer.java
new file mode 100644 (file)
index 0000000..e9087e7
--- /dev/null
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+
+
+/**
+ * native peer classes are part of MJI and contain the code that is
+ * executed by the host VM (i.e. outside the state-tracked JPF VM). Each
+ * class executed by JPF that has native mehods must have a native peer class
+ * (which is looked up and associated at class loadtime)
+ */
+public class NativePeer implements Cloneable {
+
+  static final String MODEL_PACKAGE = "<model>";
+  static final String DEFAULT_PACKAGE = "<default>";
+
+  static JPFLogger logger = JPF.getLogger("class");
+
+  static ClassLoader loader;
+  static HashMap<ClassInfo, NativePeer> peers;
+  static Config config;
+  static boolean noOrphanMethods;
+
+  static String[] peerPackages;
+
+  ClassInfo ci;
+  Class<?> peerClass;
+  HashMap<String, Method> methods;
+
+
+  public static boolean init (Config conf) {
+    loader = conf.getClassLoader();
+    peers = new HashMap<ClassInfo, NativePeer>();
+
+    peerPackages = getPeerPackages(conf);
+
+    config = conf;
+    noOrphanMethods = conf.getBoolean("vm.no_orphan_methods", false);
+
+    return true;
+  }
+
+  static String[] getPeerPackages (Config conf) {
+    String[] defPeerPackages = { MODEL_PACKAGE, "gov.nasa.jpf.vm", DEFAULT_PACKAGE };
+    String[] packages = conf.getStringArray("peer_packages", defPeerPackages);
+
+    // internalize
+    for (int i=0; i<packages.length; i++) {
+      if (packages[i].equals(MODEL_PACKAGE)) {
+        packages[i] = MODEL_PACKAGE;
+      } else if (packages[i].equals(DEFAULT_PACKAGE)) {
+        packages[i] = DEFAULT_PACKAGE;
+      }
+    }
+
+    return packages;
+  }
+
+  static Class<?> locatePeerCls (String clsName) {
+    String cn = "JPF_" + clsName.replace('.', '_');
+
+    for (int i=0; i<peerPackages.length; i++) {
+      String pcn;
+      String pkg = peerPackages[i];
+
+      if (pkg == MODEL_PACKAGE) {
+        int j = clsName.lastIndexOf('.');
+        pcn = clsName.substring(0, j+1) + cn;
+      } else if (pkg == DEFAULT_PACKAGE) {
+        pcn = cn;
+      } else {
+        pcn = pkg + '.' + cn;
+      }
+     
+      try {
+        Class<?> peerCls = loader.loadClass(pcn);
+        
+        if ((peerCls.getModifiers() & Modifier.PUBLIC) == 0) {
+          logger.warning("non-public peer class: ", pcn);
+          continue; // pointless to use this one, it would just create IllegalAccessExceptions
+        }
+        
+        logger.info("loaded peer class: ", pcn);
+        
+        return peerCls;
+      } catch (ClassNotFoundException cnfx) {
+        // try next one
+      }
+    }
+
+    return null; // nothing found
+  }
+
+  /**
+   * this becomes the factory method to load either a plain (slow)
+   * reflection-based peer (a NativePeer object), or some speed optimized
+   * derived class object.
+   * Watch out - this gets called before the ClassInfo is fully initialized
+   * (we shouldn't rely on more than just its name here)
+   */
+  static NativePeer getNativePeer (ClassInfo ci) {
+    String     clsName = ci.getName();
+    NativePeer peer = peers.get(ci);
+    Class<?>      peerCls = null;
+
+    if (peer == null) {
+      peerCls = locatePeerCls(clsName);
+
+      if (peerCls != null) {
+        initializePeerClass( peerCls);
+                
+        if (logger.isLoggable(Level.INFO)) {
+          logger.info("load peer: ", peerCls.getName());
+        }
+
+        peer = getInstance(peerCls, NativePeer.class);
+        peer.initialize(peerCls, ci, true);
+
+        peers.put(ci, peer);
+      }
+    }
+
+    return peer;
+  }
+
+  public static <T> T getInstance(Class<?> cls, Class<T> type) throws JPFException {
+    Class<?>[] argTypes = Config.CONFIG_ARGTYPES;
+    Object[] args = config.CONFIG_ARGS;
+
+    return getInstance(cls, type, argTypes, args);
+  }
+
+  public static <T> T getInstance(Class<?> cls, Class<T> type, Class<?>[] argTypes,
+                     Object[] args) throws JPFException {
+    Object o = null;
+    Constructor<?> ctor = null;
+
+    if (cls == null) {
+      return null;
+    }
+
+    while (o == null) {
+      try {
+        ctor = cls.getConstructor(argTypes);
+        o = ctor.newInstance(args);
+      } catch (NoSuchMethodException nmx) {
+         
+        if ((argTypes.length > 1) || ((argTypes.length == 1) && (argTypes[0] != Config.class))) {
+          // fallback 1: try a single Config param
+          argTypes = Config.CONFIG_ARGTYPES;
+          args = config.CONFIG_ARGS;
+        } else if (argTypes.length > 0) {
+          // fallback 2: try the default ctor
+          argTypes = Config.NO_ARGTYPES;
+          args = Config.NO_ARGS;
+
+        } else {
+          // Ok, there is no suitable ctor, bail out
+          throw new JPFException("no suitable ctor found for the peer class " + cls.getName());
+        }
+      } catch (IllegalAccessException iacc) {
+        throw new JPFException("ctor not accessible: "
+            + config.getMethodSignature(ctor));
+      } catch (IllegalArgumentException iarg) {
+        throw new JPFException("illegal constructor arguments: "
+            + config.getMethodSignature(ctor));
+      } catch (InvocationTargetException ix) {
+        Throwable tx = ix.getCause();
+        throw new JPFException("exception " + tx + " occured in " 
+            + config.getMethodSignature(ctor));
+      } catch (InstantiationException ivt) {
+        throw new JPFException("abstract class cannot be instantiated");
+      } catch (ExceptionInInitializerError eie) {
+        throw new JPFException("static initialization failed:\n>> "
+            + eie.getException(), eie.getException());
+      }
+    }
+
+    // check type
+    if (!cls.isInstance(o)) {
+      throw new JPFException("instance not of type: "
+          + cls.getName());
+    }
+
+    return type.cast(o); // safe according to above
+  }
+
+  static String getPeerDispatcherClassName (String clsName) {
+    return (clsName + '$');
+  }
+
+  public Class<?> getPeerClass() {
+    return peerClass;
+  }
+
+  public String getPeerClassName() {
+    return peerClass.getName();
+  }
+
+  protected void initialize (Class<?> peerClass, ClassInfo ci, boolean cacheMethods) {
+    if ((this.ci != null) || (this.peerClass != null)) {
+      throw new RuntimeException("cannot re-initialize NativePeer: " +
+                                 peerClass.getName());
+    }
+
+    this.ci = ci;
+    this.peerClass = peerClass;
+
+    loadMethods(cacheMethods);
+  }
+
+  protected static void initializePeerClass( Class<?> cls) {
+    try {
+      Method m = cls.getDeclaredMethod("init", Config.class );
+      try {
+        m.invoke(null, config);
+      } catch (IllegalArgumentException iax){
+        // can't happen - static method
+      } catch (IllegalAccessException iacx) {
+        throw new RuntimeException("peer initialization method not accessible: "
+                                   + cls.getName());
+      } catch (InvocationTargetException itx){
+        throw new RuntimeException("initialization of peer " +
+                                   cls.getName() + " failed: " + itx.getCause());
+
+      }
+    } catch (NoSuchMethodException nsmx){
+      // nothing to do
+    }
+  }
+
+  private static boolean isMJICandidate (Method mth) {
+
+    // the native peer should be annotated with @MJI
+    if(!mth.isAnnotationPresent(MJI.class)) {
+      return false;
+    }
+
+    // this native peer should be Public
+    if(!Modifier.isPublic(mth.getModifiers())) {
+      return false;
+    }
+
+    // native method always have a MJIEnv and int as the first parameters
+    Class<?>[] argTypes = mth.getParameterTypes();
+    if ((argTypes.length >= 2) && (argTypes[0] == MJIEnv.class) && (argTypes[1] == int.class) ) {
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+
+  private Method getMethod (MethodInfo mi) {
+    return getMethod(null, mi);
+  }
+
+  private Method getMethod (String prefix, MethodInfo mi) {
+    String name = mi.getUniqueName();
+
+    if (prefix != null) {
+      name = prefix + name;
+    }
+
+    return methods.get(name);
+  }
+
+  /**
+   * look at all @MJI annotated  methods in the peer and set their
+   * corresponding model class MethodInfo attributes
+   * <2do> pcm - this is too long, break it down
+   */
+  protected void loadMethods (boolean cacheMethods) {
+    // since we allow native peer class hierarchies, we have to look at all methods
+    //Method[] m = peerClass.getDeclaredMethods();
+    Method[] m = peerClass.getMethods();
+    
+    methods = new HashMap<String, Method>(m.length);
+
+    Map<String,MethodInfo> methodInfos = ci.getDeclaredMethods();
+    MethodInfo[] mis = null;
+
+    for (int i = 0; i < m.length; i++) {
+      Method  mth = m[i];
+
+      if (isMJICandidate(mth)) {
+        // Note that we can't mangle the name automatically, since we loose the
+        // object type info (all mapped to int). This has to be handled
+        // the same way like with overloaded JNI methods - you have to
+        // mangle them manually
+        String mn = mth.getName();
+
+        // JNI doesn't allow <clinit> or <init> to be native, but MJI does
+        // (you should know what you are doing before you use that, really)
+        if (mn.startsWith("$clinit")) {
+          mn = "<clinit>";
+        } else if (mn.startsWith("$init")) {
+          mn = "<init>" + mn.substring(5);
+        }
+
+        String mname = Types.getJNIMethodName(mn);
+        String sig = Types.getJNISignature(mn);
+
+        if (sig != null) {
+          mname += sig;
+        }
+
+        // now try to find a corresponding MethodInfo object and mark it
+        // as 'peer-ed'
+        // <2do> in case of <clinit>, it wouldn't be strictly required to
+        // have a MethodInfo upfront (we could create it). Might be handy
+        // for classes where we intercept just a few methods, but need
+        // to init before
+        MethodInfo mi = methodInfos.get(mname);
+
+        if ((mi == null) && (sig == null)) {
+          // nothing found, we have to do it the hard way - check if there is
+          // a single method with this name (still unsafe, but JNI behavior)
+          // Note there's no point in doing that if we do have a signature
+          if (mis == null) { // cache it for subsequent lookup
+            mis = new MethodInfo[methodInfos.size()];
+            methodInfos.values().toArray(mis);
+          }
+
+          mi = searchMethod(mname, mis);
+        }
+
+        if (mi != null) {
+          logger.info("load MJI method: ", mname);
+
+          NativeMethodInfo miNative = new NativeMethodInfo(mi, mth, this);
+          miNative.replace(mi);
+
+        } else {
+          checkOrphan(mth, mname);
+        }
+      }
+    }
+  }
+
+  protected void checkOrphan (Method mth, String mname){
+    if (!ignoreOrphan(mth)) {
+      // we have an orphan method, i.e. a peer method that does not map into any model method
+      // This is usually a signature typo or an out-of-sync peer, but could also be a
+      // MJI method in a peer superclass which is bound to a MethodInfo in a model superclass
+
+      Class<?> implCls = mth.getDeclaringClass();
+      if (implCls != peerClass) {
+        ClassInfo ciSuper = ci.getSuperClass();
+        if (ciSuper != null){
+          MethodInfo mi = ciSuper.getMethod(mname, true);
+          if (mi != null){
+            if (mi instanceof NativeMethodInfo){
+              NativeMethodInfo nmi = (NativeMethodInfo)mi;
+              if (nmi.getMethod().equals(mth)){
+                return;
+              }
+            }
+          }
+        }
+      }
+
+      String message = "orphan NativePeer method: " + ci.getName() + '.' + mname;
+
+      if (noOrphanMethods) {
+        throw new JPFException(message);
+      } else {
+        // issue a warning if we have a NativePeer native method w/o a corresponding
+        // method in the model class (this might happen due to compiler optimizations
+        // silently skipping empty methods)
+        logger.warning(message);
+      }
+    }
+  }
+  
+  protected boolean ignoreOrphan (Method m){
+    MJI annotation = m.getAnnotation(MJI.class);
+    return annotation.noOrphanWarning();
+  }
+  
+  private MethodInfo searchMethod (String mname, MethodInfo[] methods) {
+    int idx = -1;
+
+    for (int j = 0; j < methods.length; j++) {
+      if (methods[j].getName().equals(mname)) {
+        // if this is actually a overloaded method, and the first one
+        // isn't the right choice, we would get an IllegalArgumentException,
+        // hence we have to go on and make sure it's not overloaded
+
+        if (idx == -1) {
+          idx = j;
+        } else {
+          throw new JPFException("overloaded native method without signature: " + ci.getName() + '.' + mname);
+        }
+      }
+    }
+
+    if (idx >= 0) {
+      return methods[idx];
+    } else {
+      return null;
+    }
+  }
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/NativeStackFrame.java b/src/main/gov/nasa/jpf/vm/NativeStackFrame.java
new file mode 100644 (file)
index 0000000..cc93bd0
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.jvm.bytecode.NATIVERETURN;
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.Misc;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * a stack frame for MJI methods
+ * 
+ * This is a special Stackframe to execute NativeMethodInfos, which are just a wrapper around Java reflection
+ * calls. As required by the Java reflection API, they can store argument and return values as object references
+ *
+ * NOTE: operands and locals can be, but are not automatically used during
+ * native method execution.
+ */
+public abstract class NativeStackFrame extends StackFrame {
+
+
+  // we don't use the operand stack or locals for arguments and return value
+  // because (1) they don't have the right representation (host VM),
+  // (2) for performance reasons (no array alloc), and (3) because there is no
+  // choice point once we enter a native method, so there is no need to do
+  // copy-on-write on the ThreadInfo callstack. Native method execution is
+  // atomic (leave alone roundtrips of course)
+
+  // return value registers
+  protected Object ret;
+  protected Object retAttr;
+
+  // our argument registers
+  protected Object[] args;
+
+  public NativeStackFrame (NativeMethodInfo mi){
+    super( mi, 0, 0);
+  }
+  
+  public void setArgs (Object[] args){
+    this.args = args; 
+  }
+
+  @Override
+  public StackFrame clone () {
+    NativeStackFrame sf = (NativeStackFrame) super.clone();
+
+    if (args != null) {
+      sf.args = args.clone();
+    }
+
+    return sf;
+  }
+
+  @Override
+  public boolean isNative() {
+    return true;
+  }
+
+  @Override
+  public boolean isSynthetic() {
+    return true;
+  }
+
+  @Override
+  public boolean modifiesState() {
+    // native stackframes don't do anything with their operands or locals per se
+    // they are executed atomically, so there is no need to ever restore them
+    return false;
+  }
+
+  @Override
+  public boolean hasAnyRef() {
+    return false;
+  }
+
+  public void setReturnAttr (Object a){
+    retAttr = a;
+  }
+
+  public void setReturnValue(Object r){
+    ret = r;
+  }
+
+  public void clearReturnValue() {
+    ret = null;
+    retAttr = null;
+  }
+
+  public Object getReturnValue() {
+    return ret;
+  }
+
+  public Object getReturnAttr() {
+    return retAttr;
+  }
+
+  public Object[] getArguments() {
+    return args;
+  }
+
+  @Override
+  public void markThreadRoots (Heap heap, int tid) {
+    // what if some listener creates a CG post-EXECUTENATIVE or pre-NATIVERETURN?
+    // and the native method returned an object?
+    // on the other hand, we have to make sure we don't mark a return value from
+    // a previous transition
+
+    if (pc instanceof NATIVERETURN){
+      if (ret != null && ret instanceof Integer && mi.isReferenceReturnType()) {
+        int ref = ((Integer) ret).intValue();
+        heap.markThreadRoot(ref, tid);
+      }
+    }
+  }
+
+  @Override
+  protected void hash (HashData hd) {
+    super.hash(hd);
+
+    if (ret != null){
+      hd.add(ret);
+    }
+    if (retAttr != null){
+      hd.add(retAttr);
+    }
+
+    for (Object a : args){
+      hd.add(a);
+    }
+  }
+
+  @Override
+  public boolean equals (Object object) {
+    if (object == null || !(object instanceof NativeStackFrame)){
+      return false;
+    }
+
+    if (!super.equals(object)){
+      return false;
+    }
+
+    NativeStackFrame o = (NativeStackFrame)object;
+
+    if (ret != o.ret){
+      return false;
+    }
+    if (retAttr != o.retAttr){
+      return false;
+    }
+
+    if (args.length != o.args.length){
+      return false;
+    }
+
+    if (!Misc.compare(args.length, args, o.args)){
+      return false;
+    }
+
+    return true;
+  }
+
+  @Override
+  public String toString () {
+    StringWriter sw = new StringWriter(128);
+    PrintWriter pw = new PrintWriter(sw);
+
+    pw.print("NativeStackFrame@");
+    pw.print(Integer.toHexString(objectHashCode()));
+    pw.print("{ret=");
+    pw.print(ret);
+    if (retAttr != null){
+      pw.print('(');
+      pw.print(retAttr);
+      pw.print(')');
+    }
+    pw.print(',');
+    printContentsOn(pw);
+    pw.print('}');
+
+    return sw.toString();
+  }
+  
+  //--- NativeStackFrames aren't called directly and have special return value processing (in NATIVERETURN.execute())
+  @Override
+  public void setArgumentLocal (int idx, int value, Object attr){
+    throw new JPFException("NativeStackFrames don't support setting argument locals");
+  }
+  @Override
+  public void setLongArgumentLocal (int idx, long value, Object attr){
+    throw new JPFException("NativeStackFrames don't support setting argument locals");    
+  }
+  @Override
+  public void setReferenceArgumentLocal (int idx, int ref, Object attr){
+    throw new JPFException("NativeStackFrames don't support setting argument locals");
+  }
+  
+  //--- exception refs
+  @Override
+  public void setExceptionReference (int exRef){
+    throw new JPFException("NativeStackFrames don't support exception handlers");    
+  }
+
+  @Override
+  public int getExceptionReference (){
+    throw new JPFException("NativeStackFrames don't support exception handlers");    
+  }
+
+  @Override
+  public void setExceptionReferenceAttribute (Object attr){
+    throw new JPFException("NativeStackFrames don't support exception handlers");    
+  }
+  
+  @Override
+  public Object getExceptionReferenceAttribute (){
+    throw new JPFException("NativeStackFrames don't support exception handlers");    
+  }
+  
+  @Override
+  public int getResult(){
+    Object r = ret;
+    
+    if (r instanceof Number){
+      if (r instanceof Double){
+        throw new JPFException("result " + ret + " can't be converted into int");    
+      } else if (r instanceof Float){
+        return Float.floatToIntBits((Float)r);
+      } else {
+        return ((Number)r).intValue();
+      }
+    } else if (r instanceof Boolean){
+      return (r == Boolean.TRUE) ? 1 : 0;
+    } else {
+      throw new JPFException("result " + ret + " can't be converted into raw int value");
+    }
+  }
+  
+  @Override
+  public int getReferenceResult(){
+    if (ret instanceof Integer){
+      return (Integer)ret; // MJI requires references to be returned as 'int'
+    } else {
+      throw new JPFException("result " + ret + " can't be converted into JPF refrence value");      
+    }
+  }
+  
+  @Override
+  public long getLongResult(){
+    Object r = ret;
+    if (r instanceof Long){
+      return (Long)r;
+    } else if (r instanceof Double){
+      return Double.doubleToLongBits((Double)r);
+    } else {
+      throw new JPFException("result " + ret + " can't be converted into raw long value");      
+    }
+  }
+  
+  @Override
+  public Object getResultAttr(){
+    return retAttr;
+  }
+  @Override
+  public Object getLongResultAttr(){
+    return retAttr;    
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/NativeStateHolder.java b/src/main/gov/nasa/jpf/vm/NativeStateHolder.java
new file mode 100644 (file)
index 0000000..801a880
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface for native state components that are not visible to the serializer,
+ * i.e. are not stored in any form within static or instance fields or stackframes
+ */
+public interface NativeStateHolder {
+  
+  /**
+   * this is called by serializers to compute the state hash 
+   */
+  int getHash();
+}
diff --git a/src/main/gov/nasa/jpf/vm/NoJPFExec.java b/src/main/gov/nasa/jpf/vm/NoJPFExec.java
new file mode 100644 (file)
index 0000000..95489e1
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.SystemAttribute;
+
+/**
+ * InfoObject attr that flags a certain construct (field, method, class) is not supposed
+ * to be used under JPF. Useful for model classes that have or use features which have to be
+ * cut off / abstracted by means of native peers, and we want to catch violations as
+ * early as possible
+ */
+public class NoJPFExec implements SystemAttribute {
+
+  // we only need one
+  public static final NoJPFExec SINGLETON = new NoJPFExec();
+}
diff --git a/src/main/gov/nasa/jpf/vm/NoOutOfMemoryErrorProperty.java b/src/main/gov/nasa/jpf/vm/NoOutOfMemoryErrorProperty.java
new file mode 100644 (file)
index 0000000..7c66c85
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.GenericProperty;
+import gov.nasa.jpf.search.Search;
+
+/**
+ * A property class so that gov.nasa.jpf.JPF can add an error to the 
+ * gov.nasa.jpf.search.Search object when JPF catches an OutOfMemoryError.
+ */
+public class NoOutOfMemoryErrorProperty extends GenericProperty {
+
+  private boolean m_triggered = true;
+  
+  public NoOutOfMemoryErrorProperty() {
+  }
+  
+  @Override
+  public void reset() {
+    m_triggered = false;
+  }
+  
+  @Override
+  public boolean check(Search search, VM vm) {
+    return(m_triggered);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/NoUncaughtExceptionsProperty.java b/src/main/gov/nasa/jpf/vm/NoUncaughtExceptionsProperty.java
new file mode 100644 (file)
index 0000000..ce3e97b
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.GenericProperty;
+import gov.nasa.jpf.search.Search;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * property class to check for uncaught exceptions
+ */
+public class NoUncaughtExceptionsProperty extends GenericProperty {
+  
+  ExceptionInfo uncaughtXi;
+  
+  public NoUncaughtExceptionsProperty (Config config) {
+    uncaughtXi = null;
+  }
+  
+  void setExceptionInfo (ExceptionInfo xi){
+    uncaughtXi = xi;
+  }
+  
+  public ExceptionInfo getUncaughtExceptionInfo() {
+         return uncaughtXi;
+  }
+  
+  @Override
+  public String getExplanation () {
+    // that's pretty self explaining, isn't it?
+    return null;
+    //return "no uncaught exception";
+  }
+
+  @Override
+  public String getErrorMessage () {
+    if (uncaughtXi != null) {
+      StringWriter sw = new StringWriter();
+      uncaughtXi.printOn(new PrintWriter(sw));
+      return sw.toString();
+    }
+    
+    return null;
+  }
+  
+  @Override
+  public void reset() {
+    uncaughtXi = null;
+  }
+  
+  @Override
+  public boolean check (Search search, VM vm) {
+    uncaughtXi = vm.getPendingException();
+    return (uncaughtXi == null);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/NotDeadlockedProperty.java b/src/main/gov/nasa/jpf/vm/NotDeadlockedProperty.java
new file mode 100644 (file)
index 0000000..a70986c
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.GenericProperty;
+import gov.nasa.jpf.search.Search;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * property class to check for no-runnable-threads conditions
+ */
+public class NotDeadlockedProperty extends GenericProperty {
+  Search search;
+  ThreadInfo tiAtomic;
+  
+  public NotDeadlockedProperty (Config conf, Search search) {
+    this.search = search; 
+  }
+  
+  @Override
+  public String getErrorMessage () {
+    VM vm = search.getVM();
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+
+    if (tiAtomic != null){
+      pw.println("blocked in atomic section:");
+    } else {
+      pw.println("deadlock encountered:");
+    }
+    
+    ThreadInfo[] liveThreads = vm.getLiveThreads();
+    for (ThreadInfo ti : liveThreads) {
+      pw.print("  ");
+      if (ti == tiAtomic){
+        pw.print("ATOMIC ");
+      }
+      pw.println(ti.getStateDescription());
+    }
+    
+    return sw.toString();
+  }
+
+  @Override
+  public boolean check (Search search, VM vm) {
+    if (vm.isDeadlocked()){
+      ThreadInfo ti = vm.getCurrentThread();
+      if (ti.isAtomic()){
+        tiAtomic = ti;
+      }
+      return false;
+    } else {
+      return true;
+    }
+  }
+
+  @Override
+  public void reset() {
+    tiAtomic = null;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/OVHeap.java b/src/main/gov/nasa/jpf/vm/OVHeap.java
new file mode 100644 (file)
index 0000000..109a01f
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.ObjVector;
+
+import java.util.Iterator;
+
+/**
+ * a heap that implements search global object ids (SGOIDs) and uses
+ * a simple ObjVector to store ElementInfos. This is only efficient
+ * for small heaps with low fragmentation
+ * 
+ * SGOID computation uses HashedAllocationContext, which means there
+ * is a chance of collisions, in which case a different heap type
+ * has to be used (we don't try to resolve collisions here)
+ * 
+ * NOTE - a reference value of 0 represents NULL, but we rather waste one
+ * unused element than doing a -1 on all gets/sets
+ */
+public class OVHeap extends GenericSGOIDHeap {
+  
+  //--- state management
+  static class OVMemento extends GenericSGOIDHeapMemento {
+    ObjVector.Snapshot<ElementInfo> eiSnap;
+    
+    OVMemento(OVHeap heap) {
+      super(heap);
+      
+      heap.elementInfos.process(ElementInfo.storer);      
+      eiSnap = heap.elementInfos.getSnapshot();
+    }
+
+    @Override
+    public Heap restore(Heap inSitu) {
+      super.restore( inSitu);
+      
+      OVHeap heap = (OVHeap)inSitu;
+      heap.elementInfos.restore(eiSnap);      
+      heap.elementInfos.process(ElementInfo.restorer);
+      
+      return heap;
+    }
+  }
+  
+  //--- instance data
+  
+  ObjVector<ElementInfo> elementInfos;
+  
+  
+  //--- constructors
+  
+  public OVHeap (Config config, KernelState ks){
+    super(config, ks);
+    
+    elementInfos = new ObjVector<ElementInfo>();
+  }
+      
+  //--- the container interface
+
+  /**
+   * return number of non-null elements
+   */
+  @Override
+  public int size() {
+    return nLiveObjects;
+  }
+  
+  @Override
+  protected void set (int index, ElementInfo ei) {
+    elementInfos.set(index, ei);
+  }
+
+  /**
+   * we treat ref <= 0 as NULL reference instead of throwing an exception
+   */
+  @Override
+  public ElementInfo get (int ref) {
+    if (ref <= 0) {
+      return null;
+    } else {
+      return elementInfos.get(ref);
+    }
+  }
+
+  @Override
+  public ElementInfo getModifiable (int ref) {
+    if (ref <= 0) {
+      return null;
+    } else {
+      ElementInfo ei = elementInfos.get(ref);
+
+      if (ei != null && ei.isFrozen()) {
+        ei = ei.deepClone(); 
+        // freshly created ElementInfos are not frozen, so we don't have to defreeze
+        elementInfos.set(ref, ei);
+      }
+
+      return ei;
+    }
+  }
+    
+  @Override
+  protected void remove(int ref) {
+    elementInfos.remove(ref);
+  }
+
+  @Override
+  public Iterator<ElementInfo> iterator() {
+    return elementInfos.nonNullIterator();
+  }
+
+  @Override
+  public Iterable<ElementInfo> liveObjects() {
+    return elementInfos.elements();
+  }
+
+  @Override
+  public void resetVolatiles() {
+    // we don't have any
+  }
+
+  @Override
+  public void restoreVolatiles() {
+    // we don't have any
+  }
+
+  @Override
+  public Memento<Heap> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  @Override
+  public Memento<Heap> getMemento(){
+    return new OVMemento(this);
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/OVStatics.java b/src/main/gov/nasa/jpf/vm/OVStatics.java
new file mode 100644 (file)
index 0000000..dc72f20
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.Iterator;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.ObjVector;
+
+/**
+ * Statics implementation that uses a simple ObjVector as the underlying container.
+ * 
+ * The ids used to retrieve ElementInfos are dense and search global, computation is based 
+ * on the assumption that each ClassLoader can only define one class per binary class name
+ */
+public class OVStatics implements Statics {
+
+  static class OVMemento implements Memento<Statics> {
+    ObjVector.Snapshot<ElementInfo> eiSnap;
+    
+    OVMemento (OVStatics statics){
+      statics.elementInfos.process( ElementInfo.storer);
+      eiSnap = statics.elementInfos.getSnapshot();
+    }
+    
+    @Override
+    public Statics restore(Statics inSitu) {
+      OVStatics statics = (OVStatics) inSitu;
+      statics.elementInfos.restore(eiSnap);
+      statics.elementInfos.process( ElementInfo.restorer);
+      
+      return statics;
+    }
+  }
+  
+  protected ObjVector<ElementInfo> elementInfos;
+  
+  // search global class ids (for this ClassLoader only)
+  // NOTE this is per instance so that each one is as dense as possible, but since
+  // it is search global it does NOT have to be restored and we can copy the reference when cloning
+  protected int nextId;
+  protected IntTable<String> ids;
+  
+  
+  //--- construction
+  
+  public OVStatics (Config conf) {
+    elementInfos = new ObjVector<ElementInfo>();
+    
+    nextId = 0;
+    ids = new IntTable<String>();
+  }
+  
+  protected int computeId (ClassInfo ci) {
+    String clsName = ci.getName();
+    IntTable.Entry<String> e = ids.get(clsName);
+    if (e == null) {
+      int id = nextId++;
+      ids.put( clsName, id);
+      return id;
+      
+    } else {
+      return e.val;
+    }
+  }
+  
+  protected StaticElementInfo createStaticElementInfo (int id, ClassInfo ci, ThreadInfo ti, ElementInfo eiClsObj) {
+    Fields   f = ci.createStaticFields();
+    Monitor  m = new Monitor();
+
+    StaticElementInfo ei = new StaticElementInfo( id, ci, f, m, ti, eiClsObj);
+
+    ci.initializeStaticData(ei, ti);
+
+    return ei;
+  }
+  
+  @Override
+  public StaticElementInfo newClass (ClassInfo ci, ThreadInfo ti, ElementInfo eiClsObj) {
+    assert (eiClsObj != null);
+    
+    int id = computeId( ci);
+    
+    StaticElementInfo ei = createStaticElementInfo( id, ci, ti, eiClsObj);
+    elementInfos.set(id, ei);
+    
+    return ei;
+  }
+
+  @Override
+  public StaticElementInfo newStartupClass (ClassInfo ci, ThreadInfo ti) {
+    int id = computeId( ci);
+    
+    StaticElementInfo ei = createStaticElementInfo( id, ci, ti, null);
+    elementInfos.set(id, ei);
+    
+    return ei;
+  }
+
+  
+  //--- accessors
+  
+  @Override
+  public StaticElementInfo get(int id) {
+    // the cast sucks, but otherwise we run into the Processor covariance problem 
+    return (StaticElementInfo)elementInfos.get(id);
+  }
+
+  @Override
+  public StaticElementInfo getModifiable(int id) {
+    StaticElementInfo ei = (StaticElementInfo)elementInfos.get(id);
+    
+    if (ei.isFrozen()) {
+      ei = (StaticElementInfo)ei.deepClone();
+      // freshly created ElementInfos are not frozen, so we don't have to defreeze
+      elementInfos.set(id, ei);
+    }
+    
+    return ei;
+  }
+
+  //--- housekeeping
+  
+  @Override
+  public void cleanUpDanglingReferences (Heap heap) {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    int tid = ti.getId();
+    boolean isThreadTermination = ti.isTerminated();
+    
+    for (ElementInfo e : this) {
+      e.cleanUp( heap, isThreadTermination, tid);
+    }
+  }
+  
+  //--- state restoration
+  
+  @Override
+  public Memento<Statics> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  @Override
+  public Memento<Statics> getMemento() {
+    return new OVMemento(this);
+  }
+  
+  @SuppressWarnings("rawtypes")
+  @Override
+  public Iterator<ElementInfo> iterator(){
+    return ((ObjVector)elementInfos).nonNullIterator();
+  }
+  
+  @Override
+  public void markRoots(Heap heap) {
+    for (StaticElementInfo ei : liveStatics()){
+      ei.markStaticRoot(heap);
+    }
+  }
+
+  @SuppressWarnings("rawtypes")
+  @Override
+  public Iterable<StaticElementInfo> liveStatics() {
+    return (Iterable)elementInfos.elements();
+  }
+
+  @Override
+  public int size() {
+    return elementInfos.length();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ObjRef.java b/src/main/gov/nasa/jpf/vm/ObjRef.java
new file mode 100644 (file)
index 0000000..51376e1
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+/**
+ * helper class to store object references in a context where Integer is used for boxed 'int' values
+ */
+public class ObjRef {
+  public static final ObjRef NULL = new ObjRef(MJIEnv.NULL);
+  
+  int reference;
+
+  protected ObjRef (int ref) {
+    reference = ref;
+  }
+
+  public boolean isNull () {
+    return reference == MJIEnv.NULL;
+  }
+
+  public int getReference () {
+    return reference;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o.getClass() == ObjRef.class) {
+      ObjRef other = (ObjRef)o;
+      return reference == other.reference;
+    }
+    return false;
+  }
+
+  @Override
+  public int hashCode () {
+    return reference;
+  }
+
+  @Override
+  public String toString () {
+    return "ObjRef(" + reference + ')';
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/PSIMHeap.java b/src/main/gov/nasa/jpf/vm/PSIMHeap.java
new file mode 100644 (file)
index 0000000..d53e4ae
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.PSIntMap;
+import gov.nasa.jpf.util.Predicate;
+
+import java.util.Iterator;
+
+/**
+ * heap implementation that uses a PersistentStagingMsbIntMap as the underlying container
+ * 
+ * This is intended for large state spaces, to minimize store/restore costs. While those
+ * are negligible for PersistentMaps, the per-change overhead is not since the container
+ * has to duplicate the access path to the changed node on every modification. Efficiency
+ * of PSIMHeap therefore relies on accessing objects in a consecutive pattern, i.e.
+ * depends on clustered access.
+ * 
+ * <2do> this should not be a GenericSGOIDHeap derived class since this includes
+ * a number of non-persistent data structures (allocCounts, intern strings, pinDowns) that
+ * are not persistent, i.e. still requires a memento that creates/restores snapshots of these
+ * and hence looses a lot of the benefit we use a persistent map for
+ * 
+ * NOTE - a reference value of 0 represents null and therefore is not a valid SGOID
+ */
+public class PSIMHeap extends GenericSGOIDHeap {
+  
+  /**
+   * this sucks - we need a memento in order to store/restore allocCounts, internStrings and pinDownList
+   */
+  static class PSIMMemento extends GenericSGOIDHeapMemento {
+    PSIntMap<ElementInfo> eiSnap;
+    
+    PSIMMemento (PSIMHeap heap) {
+      super(heap);
+      
+      heap.elementInfos.process(ElementInfo.storer);
+      eiSnap = heap.elementInfos; // no need to transform anything, it's persistent
+    }
+
+    @Override
+    public Heap restore(Heap inSitu) {
+      super.restore( inSitu);
+      
+      PSIMHeap heap = (PSIMHeap) inSitu;
+      heap.elementInfos = eiSnap;
+      heap.elementInfos.process(ElementInfo.restorer);
+      
+      return heap;
+    }
+  }
+  
+  class SweepPredicate implements Predicate<ElementInfo>{
+    ThreadInfo ti;
+    int tid;
+    boolean isThreadTermination;
+    
+    protected void setContext() {
+      ti = vm.getCurrentThread();
+      tid = ti.getId();
+      isThreadTermination = ti.isTerminated();      
+    }
+    
+    @Override
+    public boolean isTrue (ElementInfo ei) {
+      
+      if (ei.isMarked()){ // live object, prepare for next transition & gc cycle
+        ei.setUnmarked();
+        ei.setAlive( liveBitValue);          
+        ei.cleanUp( PSIMHeap.this, isThreadTermination, tid);
+        return false;
+        
+      } else { // object is no longer reachable  
+        // no finalizer support yet
+        ei.processReleaseActions();
+        // <2do> still have to process finalizers here, which might make the object live again
+        vm.notifyObjectReleased( ti, ei);
+        return true;
+      } 
+    }
+  }
+  
+  SweepPredicate sweepPredicate;
+  PSIntMap<ElementInfo> elementInfos;
+  
+  
+  public PSIMHeap (Config config, KernelState ks) {
+    super(config,ks);
+    
+    elementInfos = new PSIntMap<ElementInfo>();    
+    sweepPredicate = new SweepPredicate();
+  }
+  
+  @Override
+  public int size() {
+    return elementInfos.size();
+  }
+
+  @Override
+  protected void set(int index, ElementInfo ei) {
+    elementInfos = elementInfos.set(index, ei);
+  }
+
+  @Override
+  public ElementInfo get(int ref) {
+    if (ref <= 0) {
+      return null;
+    } else {      
+      return elementInfos.get(ref);
+    }
+  }
+
+  @Override
+  public ElementInfo getModifiable(int ref) {
+    // <2do> this could probably use a specialized replaceValue() method
+    
+    if (ref <= 0) {
+      return null;
+    } else {
+      ElementInfo ei = elementInfos.get(ref);
+
+      if (ei != null && ei.isFrozen()) {
+        ei = ei.deepClone(); 
+        // freshly created ElementInfos are not frozen, so we don't have to defreeze
+        elementInfos = elementInfos.set(ref, ei);
+      }
+
+      return ei;
+    }
+  }
+
+  @Override
+  protected void remove(int ref) {
+    elementInfos = elementInfos.remove(ref);
+  }
+  
+  @Override
+  protected void sweep () {
+    sweepPredicate.setContext();
+    elementInfos = elementInfos.removeAllSatisfying( sweepPredicate);
+  }
+  
+  @Override
+  public Iterator<ElementInfo> iterator() {
+    return elementInfos.iterator();
+  }
+
+  @Override
+  public Iterable<ElementInfo> liveObjects() {
+    return elementInfos;
+  }
+
+  @Override
+  public void resetVolatiles() {
+    // we don't have any
+  }
+
+  @Override
+  public void restoreVolatiles() {
+    // we don't have any
+  }
+
+  @Override
+  public Memento<Heap> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  @Override
+  public Memento<Heap> getMemento() {
+    return new PSIMMemento(this);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/Path.java b/src/main/gov/nasa/jpf/vm/Path.java
new file mode 100644 (file)
index 0000000..2cf2321
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.Printable;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+
+/**
+ * Path represents the data structure in which a execution trace is recorded.
+ */
+public class Path implements Printable, Iterable<Transition>, Cloneable {
+  String             application;  
+  private LinkedList<Transition> stack;
+  
+  private Path() {} // for cloning
+  
+  public Path (String app) {
+    application = app;
+    stack = new LinkedList<Transition>();
+  }
+  
+  @Override
+  public Path clone() {
+    Path clone = new Path();
+    clone.application = application;
+    
+    // we need to deep copy the stack to preserve CG and ThreadInfo state
+    LinkedList<Transition> clonedStack = new LinkedList<Transition>();
+    for (Transition t : stack){
+      clonedStack.add( (Transition)t.clone());
+    }
+    clone.stack = clonedStack;
+    
+    return clone;
+  }
+  
+  public String getApplication () {
+    return application;
+  }
+
+  public Transition getLast () {
+    if (stack.isEmpty()) {
+      return null;
+    } else {
+      return stack.getLast();
+    }
+  }
+
+  public void add (Transition t) {
+    stack.add(t);
+  }
+
+  public Transition get (int pos) {
+    return stack.get(pos);
+  }
+
+  public boolean isEmpty() {
+    return (stack.size() == 0);
+  }
+  
+  public int size () {
+    return stack.size();
+  }
+
+  public boolean hasOutput () {
+    for (Transition t : stack) {
+      if (t.getOutput() != null) {
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public void printOutputOn (PrintWriter pw) {
+    for (Transition t : stack) {
+      String output = t.getOutput();
+      if (t != null) {
+        pw.print(output);
+      }
+    }
+  }
+  
+  @Override
+  public void printOn (PrintWriter pw) {
+/**** <2do> this is going away
+    int    length = size;
+    Transition entry;
+
+    for (int index = 0; index < length; index++) {
+      pw.print("Transition #");
+      pw.print(index);
+      
+      if ((entry = get(index)) != null) {
+        pw.print(' ');
+
+        entry.printOn(pw);
+      }
+    }
+***/
+  }
+
+  public void removeLast () {
+    stack.removeLast();
+  }
+  
+  @Override
+  public Iterator<Transition> iterator () {
+    return stack.iterator();
+  }
+  
+  public Iterator<Transition> descendingIterator() {
+    return stack.descendingIterator();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/PathSharednessPolicy.java b/src/main/gov/nasa/jpf/vm/PathSharednessPolicy.java
new file mode 100644 (file)
index 0000000..43a4844
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * a SharednessPolicy implementation that only computes and keeps sharedness
+ * along the same path, i.e. not search global.
+ * 
+ * This is the policy that should be used to create traces that can be replayed.
+ * 
+ * This policy uses PersistentTids, i.e. it does not modify existing ThreadInfoSet
+ * instances but replaces upon add/remove with new ones. This ensures that ThreadInfoSets
+ * are path local, but it also means that operations that add/remove ThreadInfos
+ * have to be aware of the associated ElementInfo cloning and don't keep/use references
+ * to old ElementInfos. The upside is that sharedness should be the same along any
+ * given path, regardless of which paths were executed before. The downside is that all
+ * callers of ThreadInfoSet updates have to be aware of that the ElementInfo might change.
+ * 
+ * Note that without additional transition breaks this can miss races due 
+ * to non-overlapping thread execution along all paths. Most real world systems
+ * have enough scheduling points (sync, field access within loops etc.) to avoid this,
+ * but short living threads that only have single field access interaction points can
+ * run into this effect: T1 creates O, starts T2, accesses O and
+ * terminates before T2 runs. When T2 runs, it only sees access to O from an already
+ * terminated thread and therefore treats this as a clean handover. Even if
+ * T2 would break at its O access, there is no CG that would bring T1 back
+ * into a state between creation and access of O, hence races caused by the O access
+ * of T1 are missed (see .test.mc.threads.MissedPathTest).
+ * Two additional scheduling points help to prevent this case: thread start CGs and
+ * object exposure CGs (/after/ assignment in reference field PUTs where the owning
+ * object is shared, but the referenced object isn't yet). Both are conservative by
+ * nature, i.e. might introduce superfluous states for the sake of not missing paths to 
+ * race points. Both can be controlled by configuration options ('cg.threads.break_start'
+ * and 'vm.por.break_on_exposure').
+ * 
+ * Note also that shared-ness is not the same along all paths, because our goal
+ * is just to find potential data races. As long as JPF explores /one/ path that
+ * leads into a race we are fine - we don't care how many paths don't detect a race.
+ */
+public class PathSharednessPolicy extends GenericSharednessPolicy {
+  
+  public PathSharednessPolicy (Config config){
+    super(config);
+  }
+  
+  @Override
+  public void initializeObjectSharedness (ThreadInfo allocThread, DynamicElementInfo ei) {
+    ei.setReferencingThreads( new PersistentTidSet(allocThread));
+  }
+
+  @Override
+  public void initializeClassSharedness (ThreadInfo allocThread, StaticElementInfo ei) {
+    ei.setReferencingThreads( new PersistentTidSet(allocThread));
+    ei.setExposed(); // static fields are per se exposed
+  }
+  
+  @Override
+  protected FieldLockInfo createFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    int[] lockRefs = ti.getLockedObjectReferences();
+    switch (lockRefs.length){
+      case 0:
+        return FieldLockInfo.getEmptyFieldLockInfo();
+      case 1:
+        return new PersistentSingleLockThresholdFli(ti, lockRefs[0], lockThreshold);
+      default: 
+        return new PersistentLockSetThresholdFli(ti, lockRefs, lockThreshold);
+    }
+  }
+  
+  @Override
+  protected boolean checkOtherRunnables (ThreadInfo ti){
+    // since we only consider properties along this path, we can
+    // ignore states that don't have other runnables
+    return ti.hasOtherRunnables();
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/PersistentLockSetThresholdFli.java b/src/main/gov/nasa/jpf/vm/PersistentLockSetThresholdFli.java
new file mode 100644 (file)
index 0000000..921252d
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * persistent version of a FieldLockInfo with multiple locks
+ */
+public class PersistentLockSetThresholdFli extends LockSetThresholdFli {
+
+  public PersistentLockSetThresholdFli(ThreadInfo ti, int[] currentLockRefs, int checkThreshold) {
+    super(ti, currentLockRefs, checkThreshold);
+  }
+
+  @Override
+  protected SingleLockThresholdFli singleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks) {
+    return new PersistentSingleLockThresholdFli(ti, lockRef, remainingChecks);
+  }
+
+  @Override
+  protected LockSetThresholdFli lockSetThresholdFli (ThreadInfo ti, int[] lockRefs, int remainingChecks){
+    return new PersistentLockSetThresholdFli( ti, lockRefs, remainingChecks);
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/PersistentSingleLockThresholdFli.java b/src/main/gov/nasa/jpf/vm/PersistentSingleLockThresholdFli.java
new file mode 100644 (file)
index 0000000..cbc3255
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * persistent SingleLockThresholdFli version
+ */
+public class PersistentSingleLockThresholdFli extends SingleLockThresholdFli {
+
+  public PersistentSingleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks){
+    super(ti, lockRef, remainingChecks);
+  }
+  
+  @Override
+  protected SingleLockThresholdFli singleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks) {
+    return new PersistentSingleLockThresholdFli(ti, lockRef, remainingChecks);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/PersistentTidSet.java b/src/main/gov/nasa/jpf/vm/PersistentTidSet.java
new file mode 100644 (file)
index 0000000..fe46611
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+/**
+ * set that stores threads via (search global) thread ids. Used to detect shared objects/classes,
+ * created by configured SharedObjectPolicy factory.
+ * This set is persistent, i.e. does not modify contents of existing set instances to avoid
+ * introducing search global state
+ */
+public class PersistentTidSet extends TidSet {
+  
+  public PersistentTidSet (ThreadInfo ti){
+    super(ti);
+  }  
+  
+  //--- non-destructive set update
+  
+  @Override
+  public ThreadInfoSet add (ThreadInfo ti) {
+    int id = ti.getId();
+    
+    if (!contains(id)){
+      PersistentTidSet newSet = (PersistentTidSet)clone();
+      newSet.add(id);
+      return newSet;
+      
+    } else {
+      return this;
+    }
+  }
+  
+  @Override
+  public ThreadInfoSet remove (ThreadInfo ti) {
+    int id = ti.getId();
+
+    if (contains(id)){
+      PersistentTidSet newSet = (PersistentTidSet)clone();
+      newSet.remove(id);
+      return newSet;
+      
+    } else {
+      return this;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/PreciseAllocationContext.java b/src/main/gov/nasa/jpf/vm/PreciseAllocationContext.java
new file mode 100644 (file)
index 0000000..9f72158
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.OATHash;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+/**
+ * class that captures execution context consisting of executing thread and 
+ * pc's of ti's current StackFrames
+ * 
+ * note that we pool (i.e. use static factory methods) in order to avoid
+ * creating a myriad of redundant objects
+ */
+public class PreciseAllocationContext implements AllocationContext {
+
+  // this is search global, i.e. does not have to be backtracked, but has to be
+  // re-initialized between JPF runs (everything that changes hashCode)
+  static private HashMap<PreciseAllocationContext,PreciseAllocationContext> ccCache = new HashMap<PreciseAllocationContext,PreciseAllocationContext>();
+  
+  protected ThreadInfo ti;
+  protected Instruction[] cc;
+  protected int hashCode; // computed once during construction (from LookupContext)
+  
+  // a mutable ExecutionContext that is only used internally to avoid creating superfluous new instances to
+  // find out if we already have seen a similar one
+  private static class LookupContext extends PreciseAllocationContext {
+    int stackDepth;
+    
+    LookupContext (){
+      cc = new Instruction[64];
+    }
+    
+    @Override
+       public int getStackDepth(){
+      return stackDepth;
+    }    
+  }
+  
+  private static LookupContext lookupContext = new LookupContext();
+  
+  static boolean init (Config config) {
+    ccCache = new HashMap<PreciseAllocationContext,PreciseAllocationContext>();
+    return true;
+  }
+  
+  public static synchronized PreciseAllocationContext getSUTExecutionContext (ClassInfo ci, ThreadInfo ti){
+    int stackDepth = ti.getStackDepth();
+    int h = 0;
+    
+    lookupContext.ti = ti;
+    lookupContext.stackDepth = stackDepth;
+    
+    h = OATHash.hashMixin(h, ti.getId());
+    
+    Instruction[] cc = lookupContext.cc;
+    if (cc.length < stackDepth){
+      cc = new Instruction[stackDepth];
+      lookupContext.cc = cc;
+    }
+
+    int i=0;
+    for (StackFrame f = ti.getTopFrame(); f != null; f = f.getPrevious()){
+      Instruction insn = f.getPC();
+      cc[i++] = insn;
+      h = OATHash.hashMixin(h, insn.hashCode());
+    }
+    h = OATHash.hashFinalize(h);
+    lookupContext.hashCode = h;
+    
+    PreciseAllocationContext ec = ccCache.get(lookupContext);
+    if (ec == null){
+      ec = new PreciseAllocationContext(ti, Arrays.copyOf(cc, stackDepth), h);
+      ccCache.put(ec, ec);
+    }
+    
+    return ec;
+  }
+  
+  protected PreciseAllocationContext(){
+    // for subclassing
+  }
+  
+  // we only construct this from a LookupContext, which already has all the data
+  private PreciseAllocationContext (ThreadInfo ti, Instruction[] cc, int hashCode){
+    this.ti = ti;
+    this.cc = cc;
+    this.hashCode = hashCode;
+  }
+  
+  @Override
+  public int hashCode(){
+    return hashCode;
+  }
+  
+  public int getStackDepth(){
+    return cc.length;
+  }
+    
+  @Override
+  public boolean equals (Object o){
+    if (o == this){ // identity shortcut
+      return true;
+      
+    } else {
+      if (o instanceof PreciseAllocationContext){
+        PreciseAllocationContext other = (PreciseAllocationContext)o;
+        if (hashCode == other.hashCode){ // we might get here because of bin masking
+          if (ti.getId() == other.ti.getId()) {
+            Instruction[] ccOther = other.cc;
+            if (cc.length == other.getStackDepth()) {
+              for (int i = 0; i < cc.length; i++) {
+                if (cc[i] != ccOther[i]) {
+                  return false;
+                }
+              }
+              return true;
+            }
+          }
+        }
+      }
+      
+      return false;
+    }
+  }
+  
+  // for automatic field init allocations
+  @Override
+  public AllocationContext extend (ClassInfo ci, int anchor) {
+    return new PreciseAllocationContext(ti, cc, OATHash.hash(hashCode, ci.hashCode()));
+  }
+  
+  /** mostly for debugging purposes */
+  @Override
+  public String toString(){
+    StringBuffer sb = new StringBuffer();
+    sb.append("(tid=");
+    sb.append(ti.getId());
+    sb.append(",stack=[");
+    for (int i=0; i<cc.length; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append(cc[i]);
+    }
+    sb.append("])");
+    return sb.toString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/PredicateMap.java b/src/main/gov/nasa/jpf/vm/PredicateMap.java
new file mode 100644 (file)
index 0000000..a3db74a
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+public abstract class PredicateMap {
+       public int ref = MJIEnv.NULL;
+       public void setRef(int objref) {
+               ref = objref;
+       }
+       public int getRef() {
+               return ref;
+       }
+       abstract public void evaluate();
+       
+       abstract public String getRep();
+}
diff --git a/src/main/gov/nasa/jpf/vm/PriorityRunnablesSyncPolicy.java b/src/main/gov/nasa/jpf/vm/PriorityRunnablesSyncPolicy.java
new file mode 100644 (file)
index 0000000..02522f9
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * a AllRunnablesSyncPolicy that only considers the runnables with the highest
+ * priority as choices
+ * 
+ * 2do - it doesn't make much sense to compare priorities across process
+ * boundaries (unless we model distributed apps running on the same machine)
+ */
+public class PriorityRunnablesSyncPolicy extends AllRunnablesSyncPolicy {
+
+  public PriorityRunnablesSyncPolicy(Config config) {
+    super(config);
+  }
+  
+  @Override
+  protected ThreadInfo[] getTimeoutRunnables (ApplicationContext appCtx){
+    ThreadInfo[] allRunnables = super.getTimeoutRunnables(appCtx);
+    
+    int maxPrio = Integer.MIN_VALUE;
+    int n=0;
+    for (int i=0; i<allRunnables.length; i++){
+      ThreadInfo ti = allRunnables[i];
+      if (ti != null){
+        int prio = ti.getPriority();
+        
+        if (prio > maxPrio){
+          maxPrio = prio;
+          for (int j=0; j<i; j++){
+            allRunnables[j]= null;
+          }
+          n = 1;
+          
+        } else if (prio < maxPrio){
+          allRunnables[i] = null;
+          
+        } else { // prio == maxPrio
+          n++;
+        }
+      }
+    }
+    
+    if (n < allRunnables.length){
+      ThreadInfo[] prioRunnables = new ThreadInfo[n];
+      for (int i=0, j=0; j<n; j++, i++){
+        if (allRunnables[i] != null){
+          prioRunnables[j++] = allRunnables[i];
+        }
+      }
+
+      return prioRunnables;
+      
+    } else { // all runnables had the same prio
+      return allRunnables;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ReferenceArrayFields.java b/src/main/gov/nasa/jpf/vm/ReferenceArrayFields.java
new file mode 100644 (file)
index 0000000..7700f96
--- /dev/null
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.PrintUtils;
+
+import java.io.PrintStream;
+
+/**
+ * element values for reference array objects
+ * (references are stored as int's)
+ */
+public class ReferenceArrayFields extends ArrayFields {
+
+  int[] values; // the references
+
+  public ReferenceArrayFields (int length) {
+    values = new int[length];
+
+    /** not required for MJIEnv.NULL = 0
+    for (int i=0; i<length; i++) {
+      values[i] = MJIEnv.NULL;
+    }
+    **/
+  }
+
+  @Override
+  public int[] asReferenceArray() {
+    return values;
+  }
+  
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    ReferenceArrayFields a = (ReferenceArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    PrintUtils.printReference(ps, values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public boolean isReferenceArray() {
+    return true;
+  }
+
+  @Override
+  public int getHeapSize() {
+    return values.length * 4;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.append(values);
+  }
+
+  @Override
+  public ReferenceArrayFields clone(){
+    ReferenceArrayFields f = (ReferenceArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+
+  @Override
+  public boolean equals(Object o) {
+    if (o instanceof ReferenceArrayFields) {
+      ReferenceArrayFields other = (ReferenceArrayFields)o;
+
+      int[] v = values;
+      int[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void setReferenceValue(int pos, int newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public int getReferenceValue(int pos) {
+    return values[pos];
+  }
+
+  @Override
+  public void hash (HashData hd) {
+    int[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/ReferenceChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/ReferenceChoiceGenerator.java
new file mode 100644 (file)
index 0000000..55fa881
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * that's a little bit counter-intuitive - references are just int values in JPF
+ */
+public interface ReferenceChoiceGenerator extends ChoiceGenerator<Integer> {
+}
diff --git a/src/main/gov/nasa/jpf/vm/ReferenceFieldInfo.java b/src/main/gov/nasa/jpf/vm/ReferenceFieldInfo.java
new file mode 100644 (file)
index 0000000..e499972
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+/**
+ * field info for object fields
+ */
+public class ReferenceFieldInfo extends SingleSlotFieldInfo {
+  int init;  //  = MJIEnv.NULL; // not required for MJIEnv.NULL = 0
+  
+  String sInit; // <2do> pcm - just a temporary quirk to init from string literals
+                // check if there are other non-object reference inits
+
+  public ReferenceFieldInfo (String name, String type, int modifiers) {
+    super(name, type, modifiers);
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    int i = f.getIntValue(storageOffset);
+    if (i == MJIEnv.NULL) {
+      return "null";
+    } else {
+      return (VM.getVM().getHeap().get(i)).toString();
+    }
+  }
+
+  @Override
+  public boolean isReference () {
+    return true;
+  }
+
+  @Override
+  public Class<? extends ChoiceGenerator<?>> getChoiceGeneratorType() {
+    return ReferenceChoiceGenerator.class;
+  }
+
+  @Override
+  public boolean isArrayField () {
+    return ci.isArray;
+  }
+
+  @Override
+  public void setConstantValue (Object constValue){
+    // <2do> pcm - check what other constants we might encounter, this is most
+    // probably not just used for Strings.
+    // Besides the type issue, there is an even bigger problem with identities.
+    // For instance, all String refs initialized via the same string literal
+    // inside a single classfile are in fact refering to the same object. This
+    // means we have to keep a registry (hashtab) with string-literal created
+    // String objects per ClassInfo, and use this when we assign or init
+    // String references.
+    // For the sake of progress, we ignore this for now, but have to come back
+    // to it because it violates the VM spec
+
+    if (constValue instanceof String){
+      cv = constValue;
+      sInit = (String)constValue;
+    } else {
+      throw new JPFException ("unsupported reference initialization: " + constValue);
+    }
+  }
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    int ref = init;
+    if (sInit != null) {
+      VM vm = ti.getVM();
+      Heap heap = vm.getHeap();
+      ref = heap.newString(sInit, ti).getObjectRef();
+    }
+    ei.getFields().setReferenceValue( storageOffset, ref);
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    if (i == MJIEnv.NULL) {
+      return null;
+    } else {
+      Heap heap = VM.getVM().getHeap();
+      return heap.get(i);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ReferenceProcessor.java b/src/main/gov/nasa/jpf/vm/ReferenceProcessor.java
new file mode 100644 (file)
index 0000000..18b041d
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a generic interface to process reference values
+ */
+public interface ReferenceProcessor {
+
+  void processReference (int ref);
+}
diff --git a/src/main/gov/nasa/jpf/vm/ReleaseAction.java b/src/main/gov/nasa/jpf/vm/ReleaseAction.java
new file mode 100644 (file)
index 0000000..0471f4a
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface for actions to be taken when gc'ing objects that are no longer 
+ * reachable. 
+ * 
+ * This is meant to be used for types that have native companion objects
+ * which need to be cleaned up. We can't use Object.finalize() for this because
+ * this can be overridden in user code, and actually can make objects live again
+ */
+public interface ReleaseAction {
+  
+  /**
+   * object is about to be terminally released (no more finalization etc.)
+   */
+  public void release (ElementInfo ei);
+}
diff --git a/src/main/gov/nasa/jpf/vm/Restorable.java b/src/main/gov/nasa/jpf/vm/Restorable.java
new file mode 100644 (file)
index 0000000..3fb7ee6
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * generic interface of objects that can provide state storage/restore capabilities
+ */
+public interface Restorable<T> {
+
+  Memento<T> getMemento(MementoFactory factory);
+}
diff --git a/src/main/gov/nasa/jpf/vm/RestorableVMState.java b/src/main/gov/nasa/jpf/vm/RestorableVMState.java
new file mode 100644 (file)
index 0000000..ed0c930
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+/**
+ * NOTE - making VMStates fully restorable is currently very
+ * expensive and should only be done on a selective basis
+ */
+public class RestorableVMState {
+  
+  /** the set of last executed insns */
+  Transition lastTransition;
+  
+  /* these are the icky parts - the history is kept as stacks inside the
+   * VM (for restoration reasons), hence we have to copy it if we want
+   * to restore a state. Since this is really expensive, it has to be done
+   * on demand, with varying degrees of information
+   */
+  Path path;
+  
+  Backtracker.RestorableState bkstate;
+  
+  VM vm;
+  
+  RestorableVMState (VM vm) {
+    this.vm = vm;
+
+    path = vm.getClonedPath();
+    bkstate = vm.getBacktracker().getRestorableState();
+    lastTransition = vm.lastTrailInfo;
+  }
+  
+  public Backtracker.RestorableState getBkState() {
+    return bkstate;
+  }
+  
+  public Transition getLastTransition () {
+    return lastTransition;
+  }
+  
+  public Path getPath () {
+    return path;
+  }
+  
+  public int getThread () {
+    return lastTransition.getThreadIndex();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/Scheduler.java b/src/main/gov/nasa/jpf/vm/Scheduler.java
new file mode 100644 (file)
index 0000000..83398b1
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a policy interface that is used for all system operations that might
+ * cause ThreadChoiceGenerators
+ */
+public interface Scheduler extends SyncPolicy, SharednessPolicy {
+  
+  /**
+   * per application initialization 
+   */
+  void initialize (VM vm, ApplicationContext appCtx);
+}
diff --git a/src/main/gov/nasa/jpf/vm/SerializingStateSet.java b/src/main/gov/nasa/jpf/vm/SerializingStateSet.java
new file mode 100644 (file)
index 0000000..48fcdd4
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+public abstract class SerializingStateSet implements StateSet {
+  protected StateSerializer serializer;
+  
+  @Override
+  public void attach(VM vm) {
+    serializer = vm.getSerializer();
+  }
+  
+  @Override
+  public int addCurrent () {
+    return add(serializer.getStoringData());
+  }
+  
+  protected abstract int add(int[] state);
+}
diff --git a/src/main/gov/nasa/jpf/vm/SharednessPolicy.java b/src/main/gov/nasa/jpf/vm/SharednessPolicy.java
new file mode 100644 (file)
index 0000000..9b88a66
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * SharednessPolicy is a configured policy that is used to detect data races for objects that are accessible from concurrent
+ * threads.
+ *
+ * Its major purpose is to detect operations that need CGs in order to expose races, and introduce such CGs in a way that
+ * minimizes state space overhead. This was previously implemented in various different places (ThreadInfo, FieldInstruction etc.)
+ * and controlled by a number of "vm.por.*" properties. Strictly speaking, the default implementation does not do classic partial
+ * order reduction, it merely tries to reduce states associated with shared objects based on information that was collected by
+ * previous execution. All configuration therefore is now done through "vm.shared.*" properties that are loaded in
+ * SharednessPolicy implementations.
+ *
+ * The interface of this class, which is used by field and array element accessors (GETx, PUTx, xASTORE, xALOAD, Field
+ * reflection), revolves around two concepts:
+ *
+ * (1) OBJECT SHAREDNESS - this is supposed to be precise, i.e. a property associated with ElementInfo instances that is set if
+ * such an instance is in fact used by two different threads. As such, it has to be updated from the respective accessors (e.g.
+ * GETx, PUTx). Detecting sharedness is the responsibility of the SharednessPolicy, storing it is done by means of ElementInfo
+ * flags (which could also be set explicitly by listeners or peers).
+ *
+ * Once an object is marked as shared, respective field and element access operations can lead to races and hence it has to be
+ * determined if a CG should be registered to re-execute such instructions. In order to minimize superfluous states, the default
+ * policies filter out a number of access specific conditions such as immutable objects, and access point state such as number of
+ * runnable threads and especially LOCK PROTECTION (which should normally happen for well designed concurrent programs). Detecting
+ * lock protection is field specific and is done by keeping track of (intersections of) lock sets held by the accessing thread.
+ * The corresponding FieldLockInfo objects are stored in the respective ElementInfo. The default FieldLockInfo is semi-conservative
+ * - even if there is a non-empty lock set it treats the access as unprotected for a certain number of times. If the set becomes
+ * empty the object/field is permanently marked as unprotected. If the threshold is reached with a non-empty set, the object/field
+ * is henceforth treated as protected (a warning is issued should the lock set subsequently become empty).  *
+ * If the access operation passes all filters, the SharednessPolicy registers a CG, i.e. the respective operation is re-executed
+ * after performing a scheduling choice. Semantic actions (pushing operand values, changing field values) is done in the bottom
+ * half of such operations. However, this can lead to cases in which sharedness is not detected due to non-overlapping lifetimes
+ * of accessing threads. Note this does not mean that there is only one live thread at a time, only that threads don't have a CG
+ * within their overlapping lifetime that would expose the race. This can lead to missed paths/errors.
+ *
+ * (2) OBJECT EXPOSURE - is a conservative measure to avoid such missed paths. It is used to introduce additional CGs when a
+ * reference to an unshared object gets stored in a shared object, i.e. it is conceptually related to the object whose reference
+ * gets stored (i.e. the field value), not to the object that holds the field. Exposure CGs are conservative since at this point
+ * JPF only knows that the exposed object /could/ become shared in the future, not that it will. There are various different
+ * degrees of conservatism that can be employed (transitive, only first object etc.), but exposure CGs can be a major contributor
+ * to state explosion and hence should be minimized. Exposure CGs should break /after/ the semantic action (e.g. field
+ * assignment), so that it becomes visible to other threads. This leads to a tricky problem that the bottom half of related
+ * accessors has to determine if the semantic action already took place (exposure CGs) or not (sharedness CG). The action is not
+ * allowed to be re-executed in the bottom half since this could change the SUT program behavior. In order to determine execution
+ * state, implementation mechanisms have to be aware of that there can be any number of transitions between the top and bottom
+ * half, i.e. cannot rely on the current CG in the bottom half execution. Conceptually, the execution state is a StackFrame
+ * attribute.
+ *
+ * Note that exposure CGs are not mandatory. Concrete SharednessPolicy implementations can either ignore them in bug finding mode,
+ * or can replace them by means of re-execution.
+ * 
+ * Concrete SharednessPolicy implementations fall within a spectrum that is marked by two extremes: SEARCH GLOBAL and PATH LOCAL
+ * behavior. Search global policies are mostly for bug finding and keep sharedness, lock protection and exposure information from
+ * previously executed paths. This has two implications: (a) the search policy / execution order matters (e.g. leading to
+ * different results when randomizing choice selection), (b) path replay based on CG traces can lead to different results (e.g.
+ * not showing errors found in a previous search).
+ *
+ * The opposite mode is path local behavior, i.e. no sharednes/protection/exposure information from previous execution paths is
+ * used. This should yield the same results for the same path, no matter of execution history. This mode requires the use of
+ * persistent data structures for sharedness, lock info and exposure, and hence can be significantly more expensive. However, it
+ * is required to guarantee path reply that preserves outcome.
+ *
+ * Although the two standard policy implementations (GlobalSharednessPolicy and PathSharednessPolicy) correspond to static
+ * incarnations of these extremes, it should be noted that the SharednessPolicy interface strives to accommodate mixed and dynamic
+ * modes, especially controlling re-execution with adaptive behavior. A primary use for this could be to avoid exposure CGs until
+ * additional information becomes available that indicates object sharedness (e.g. non-overlapping thread access), then backtrack
+ * to a common ancestor state and re-execute with added exposure CGs for the respective object/field.
+ *
+ * The motivation for this flexibility and the related implementation complexity/cost is that race detection based on field/array
+ * access is a major contributor to state explosion. In many cases, suitable optimizations make the difference between running
+ * into search constraints or finishing the search.
+ */
+public interface SharednessPolicy {
+  
+  /**
+   * per application / SystemClassLoaderInfo specific initialization of this policy 
+   */
+  void initializeSharednessPolicy (VM vm, ApplicationContext appCtx);
+  
+  /**
+   * initializeSharednessPolicy object specific sharedness data 
+   */
+  void initializeObjectSharedness (ThreadInfo allocThread, DynamicElementInfo ei);
+  
+  /**
+   * initializeSharednessPolicy class specific sharedness data 
+   */  
+  void initializeClassSharedness (ThreadInfo allocThread, StaticElementInfo ei);
+  
+  
+  boolean canHaveSharedObjectCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi);
+  ElementInfo updateObjectSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi);
+  boolean setsSharedObjectCG  (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi);
+  
+  boolean canHaveSharedClassCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi);
+  ElementInfo updateClassSharedness (ThreadInfo ti, ElementInfo ei, FieldInfo fi);
+  boolean setsSharedClassCG  (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi);
+  
+  boolean canHaveSharedArrayCG (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int idx);
+  ElementInfo updateArraySharedness (ThreadInfo ti, ElementInfo eiArray, int idx);
+  boolean setsSharedArrayCG  (ThreadInfo ti, Instruction insn, ElementInfo eiArray, int idx);
+  
+  boolean setsSharedObjectExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed);
+  boolean setsSharedClassExposureCG (ThreadInfo ti, Instruction insn, ElementInfo eiFieldOwner, FieldInfo fi, ElementInfo eiExposed);
+  
+  
+  /**
+   * give policy a chance to clean up referencing ThreadInfoSets upon
+   * thread termination
+   */
+  void cleanupThreadTermination(ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/vm/ShortArrayFields.java b/src/main/gov/nasa/jpf/vm/ShortArrayFields.java
new file mode 100644 (file)
index 0000000..f33fc62
--- /dev/null
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+
+import java.io.PrintStream;
+
+/**
+ * element values for short[] objects
+ */
+public class ShortArrayFields extends ArrayFields {
+
+  short[] values;
+
+  public ShortArrayFields (int length) {
+    values = new short[length];
+  }
+
+  @Override
+  public short[] asShortArray() {
+    return values;
+  }
+
+  @Override
+  public void copyElements (ArrayFields src, int srcPos, int dstPos, int len){
+    ShortArrayFields a = (ShortArrayFields) src;
+    System.arraycopy(a.values, srcPos, values, dstPos, len);
+  }
+  
+  @Override
+  protected void printValue(PrintStream ps, int idx){
+    ps.print(values[idx]);
+  }
+  
+  @Override
+  public Object getValues(){
+    return values;
+  }
+
+  @Override
+  public int arrayLength() {
+    return values.length;
+  }
+
+  @Override
+  public int getHeapSize() {  // in bytes
+    return values.length * 2;
+  }
+
+  @Override
+  public void appendTo (IntVector v) {
+    v.appendPacked(values);
+  }
+
+  @Override
+  public ShortArrayFields clone(){
+    ShortArrayFields f = (ShortArrayFields)cloneFields();
+    f.values = values.clone();
+    return f;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof ShortArrayFields) {
+      ShortArrayFields other = (ShortArrayFields)o;
+      short[] v = values;
+      short[] vOther = other.values;
+      if (v.length != vOther.length) {
+        return false;
+      }
+
+      for (int i=0; i<v.length; i++) {
+        if (v[i] != vOther[i]) {
+          return false;
+        }
+      }
+
+      return compareAttrs(other);
+
+    } else {
+      return false;
+    }
+  }
+
+
+  @Override
+  public short getShortValue(int pos) {
+    return values[pos];
+  }
+
+  @Override
+  public void setShortValue (int pos, short newValue) {
+    values[pos] = newValue;
+  }
+
+  @Override
+  public void hash(HashData hd) {
+    short[] v = values;
+    for (int i=0; i < v.length; i++) {
+      hd.add(v[i]);
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/ShortFieldInfo.java b/src/main/gov/nasa/jpf/vm/ShortFieldInfo.java
new file mode 100644 (file)
index 0000000..01459cb
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+
+/**
+ * fieldinfo for slots holding booleans
+ */
+public class ShortFieldInfo extends SingleSlotFieldInfo {
+
+  short init;
+
+  public ShortFieldInfo (String name, int modifiers) {
+    super(name, "S", modifiers);
+  }
+
+  @Override
+  public void setConstantValue(Object constValue){
+    if (constValue instanceof Integer){
+      cv = constValue;
+      init = ((Integer)constValue).shortValue();
+
+    } else {
+      throw new JPFException("illegal short ConstValue=" + constValue);
+    }
+  }
+
+
+  @Override
+  public void initialize (ElementInfo ei, ThreadInfo ti) {
+    ei.getFields().setShortValue(storageOffset, init);
+  }
+
+  @Override
+  public boolean isShortField() {
+    return true;
+  }
+
+  @Override
+  public boolean isNumericField(){
+    return true;
+  }
+
+  @Override
+  public Object getValueObject (Fields f){
+    int i = f.getIntValue(storageOffset);
+    return new Short((short)i);
+  }
+
+  @Override
+  public String valueToString (Fields f) {
+    short i = f.getShortValue(storageOffset);
+    return Short.toString(i);
+  }
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/SingleLockThresholdFli.java b/src/main/gov/nasa/jpf/vm/SingleLockThresholdFli.java
new file mode 100644 (file)
index 0000000..36b6bd6
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a threshold FieldLockInfo with a single lock candidate
+ * This is the base version that does destructive updates. Override singleLockThresholdFli for a persistent version
+ */
+public class SingleLockThresholdFli extends ThresholdFieldLockInfo {
+    protected int lockRef;
+    
+    SingleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks) {
+      super( remainingChecks);
+      
+      tiLastCheck = ti;
+      this.lockRef = lockRef;
+    }
+
+    @Override
+       protected int[] getCandidateLockSet() {
+      int[] set = { lockRef };
+      return set;
+    }
+
+    /**
+     * override this for path local flis
+     */
+    protected SingleLockThresholdFli singleLockThresholdFli (ThreadInfo ti, int lockRef, int remainingChecks){
+      this.lockRef = lockRef;
+      this.tiLastCheck = ti;
+      this.remainingChecks = remainingChecks;
+
+      return this;
+    }
+    
+    @Override
+       public FieldLockInfo checkProtection (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+      int[] currentLockRefs = ti.getLockedObjectReferences();
+      int nLocks = currentLockRefs.length;
+      int nRemaining = Math.max(0, remainingChecks--);
+            
+      for (int i=0; i<nLocks; i++) {
+        if (currentLockRefs[i] == lockRef) {
+          return singleLockThresholdFli( ti, lockRef, nRemaining);
+        }
+      }
+      
+      checkFailedLockAssumption(ti, ei, fi);
+      return empty;
+    }
+
+    /**
+     * only called at the end of the gc on all live objects. The recycled ones
+     * are either already nulled in the heap, or are not marked as live
+     */
+    @Override
+       public FieldLockInfo cleanUp (Heap heap) {
+      ElementInfo ei = heap.get(lockRef);
+      if (!heap.isAlive(ei)) {
+        return FieldLockInfo.empty;
+      } else {
+        return this;
+      }
+    }
+
+    @Override
+       public String toString() {
+      return ("SingleLockThresholdFli {remainingChecks="+remainingChecks+",lock="+lockRef + '}');
+    }  
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/SingleProcessVM.java b/src/main/gov/nasa/jpf/vm/SingleProcessVM.java
new file mode 100644 (file)
index 0000000..d40ad1a
--- /dev/null
@@ -0,0 +1,330 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.Predicate;
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Includes the VM initialization for executing single Java process
+ * 
+ * To use this jpf.properties includes,
+ *              vm.class = gov.nasa.jpf.vm.SingleProcessVM
+ */
+public class SingleProcessVM extends VM {
+
+  protected ApplicationContext appCtx; // we only have one
+  
+  protected Predicate<ThreadInfo> runnablePredicate;
+  protected Predicate<ThreadInfo> daemonRunnable;
+  
+  protected SingleProcessVM (){}
+
+  public SingleProcessVM (JPF jpf, Config conf) {
+    super(jpf, conf);
+    
+    appCtx = createApplicationContext();
+    
+    initializePredicates();
+  }
+    
+  void initializePredicates() {
+    // set predicates used to query from threadlist
+    runnablePredicate = new Predicate<ThreadInfo>(){
+      @Override
+       public boolean isTrue (ThreadInfo ti){
+        return (ti.isRunnable());
+      }
+    };
+    
+    daemonRunnable = new Predicate<ThreadInfo>(){
+      @Override
+       public boolean isTrue (ThreadInfo ti){
+        return (ti.isDaemon() && ti.isRunnable());
+      }
+    };
+  }
+  
+  protected ApplicationContext createApplicationContext (){
+    String clsName;
+    String[] args = null;
+    
+    String[] freeArgs = config.getFreeArgs();
+    clsName = config.getProperty("target"); // explicit 'target' takes precedence
+    
+    if (clsName == null){
+      if (freeArgs != null){ // if it is non-null, there is at least one entry
+        // note that application property filenames have been removed by Config since they are part of its initialization
+        clsName = freeArgs[0];
+        
+        if (freeArgs.length > 1){ // if there is no 'target' command line overrides 'target.args'
+          args = Misc.arrayWithoutFirst(freeArgs, 1);
+        } else {
+          args = config.getStringArray("target.args");
+        }
+      }
+    } else {
+      // since there was a 'target', 'target.args' override command line
+      args = config.getCompactStringArray("target.args");
+      if (args == null){
+        if (freeArgs != null){
+          args = freeArgs;
+        }
+      }
+    }
+    
+    // sanity checks
+    if (args == null){
+      args = EMPTY_ARGS;
+    }
+    if (clsName == null){
+      throw new JPFConfigException("no target class specified, terminating");
+    }
+    if (!isValidClassName(clsName)){
+      throw new JPFConfigException("main class not a valid class name: " + clsName);      
+    }
+    
+    // can be any static method that has a (String[]), (String) or () signature
+    String mainEntry = config.getProperty("target.entry", "main([Ljava/lang/String;)V");
+
+    String host = config.getString("target.host", "localhost");
+    
+    SystemClassLoaderInfo sysCli = createSystemClassLoaderInfo(0);
+    
+    return new ApplicationContext( 0, clsName, mainEntry, args, host, sysCli);
+  }
+  
+  
+  @Override
+  public boolean initialize(){
+    try {
+      // this has to happen before we load the startup classes during initializeMainThread
+      scheduler.initialize(this, appCtx);
+      
+      ThreadInfo tiMain = initializeMainThread(appCtx, 0);
+      initializeFinalizerThread(appCtx, 1);
+
+      if (tiMain == null) {
+        return false; // bail out
+      }
+
+      initSystemState(tiMain);
+      initialized = true;
+      notifyVMInitialized();
+      return true;
+      
+    } catch (JPFConfigException cfe){
+      log.severe(cfe.getMessage());
+      return false;
+    } catch (ClassInfoException cie){
+      log.severe(cie.getMessage());
+      return false;
+    }
+    // all other exceptions are JPF errors that should cause stack traces
+  }
+
+  
+  @Override
+  public int getNumberOfApplications(){
+    return 1;
+  }
+  
+  @Override
+  public String getSUTName() {
+    return appCtx.mainClassName;
+  }
+
+  @Override
+  public String getSUTDescription(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(appCtx.mainClassName);
+    sb.append('.');
+    sb.append(Misc.upToFirst( appCtx.mainEntry, '('));
+    
+    sb.append('(');
+    String[] args = appCtx.args;
+    for (int i=0; i<args.length; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append('"');
+      sb.append(args[i]);
+      sb.append('"');
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+  
+  @Override
+  public ApplicationContext getApplicationContext(int obj) {
+    return appCtx;
+  }
+
+  @Override
+  public ApplicationContext[] getApplicationContexts(){
+    return new ApplicationContext[] { appCtx };
+  }
+  
+  @Override
+  public ApplicationContext getCurrentApplicationContext(){
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    if (ti != null){
+      return ti.getApplicationContext();
+    } else {
+      return appCtx;
+    }
+  }
+  
+  /**
+   * The program is terminated if there are no alive threads, and there is no nonDaemon left.
+   * 
+   * NOTE - this is only approximated in real life. Daemon threads can still run for a few cycles
+   * after the last non-daemon died, which opens an interesting source of errors we
+   * actually might want to check for
+   */
+  @Override
+  public boolean isEndState () {
+    // note this uses 'alive', not 'runnable', hence isEndStateProperty won't
+    // catch deadlocks - but that would be NoDeadlockProperty anyway
+    
+    boolean hasNonTerminatedDaemon = getThreadList().hasAnyMatching(getUserLiveNonDaemonPredicate());
+    boolean hasRunnable = getThreadList().hasAnyMatching(getUserTimedoutRunnablePredicate());
+    boolean isEndState = !(hasNonTerminatedDaemon && hasRunnable);
+    
+    if(processFinalizers) {
+      if(isEndState) {
+        if(getFinalizerThread().isRunnable()) {
+          return false;
+        }
+      }
+    }
+    
+    return isEndState;
+  }
+
+  @Override
+  public boolean isDeadlocked () { 
+    boolean hasNonDaemons = false;
+    boolean hasBlockedThreads = false;
+
+    if (ss.isBlockedInAtomicSection()) {
+      return true; // blocked in atomic section
+    }
+
+    ThreadInfo[] threads = getThreadList().getThreads();
+
+    boolean hasUserThreads = false;
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      
+      if (ti.isAlive()){
+        hasNonDaemons |= !ti.isDaemon();
+
+        // shortcut - if there is at least one runnable, we are not deadlocked
+        if (ti.isTimeoutRunnable()) { // willBeRunnable() ?
+          return false;
+        }
+        
+        if(!ti.isSystemThread()) {
+          hasUserThreads = true;
+        }
+
+        // means it is not NEW or TERMINATED, i.e. live & blocked
+        hasBlockedThreads = true;
+      }
+    }
+
+    boolean isDeadlock = hasNonDaemons && hasBlockedThreads;
+    
+    if(processFinalizers && isDeadlock && !hasUserThreads) {
+      // all threads are blocked, system threads. If the finalizer thread  
+      // is in-use, then this is a deadlocked state.
+      return (!getFinalizerThread().isIdle());
+    }
+    
+    return isDeadlock;
+  }
+  
+  @Override
+  public void terminateProcess (ThreadInfo ti) {
+    SystemState ss = getSystemState();
+    ThreadInfo[] liveThreads = getLiveThreads();
+    ThreadInfo finalizerTi = null;
+
+    for (int i = 0; i < liveThreads.length; i++) {
+      if(!liveThreads[i].isSystemThread()) {
+        // keep the stack frames around, so that we can inspect the snapshot
+        liveThreads[i].setTerminated();
+      } else {
+        // FinalizerThread is not killed at this point. We need to keep it around in 
+        // case fianlizable objects are GCed after System.exit() returns.
+        finalizerTi = liveThreads[i];
+      }
+    }
+    
+    ss.setMandatoryNextChoiceGenerator( new BreakGenerator("exit", ti, true), "exit without break CG");
+    
+    // if there is a finalizer thread, we have to run the last GC, to queue finalizable objects, if any
+    if(finalizerTi != null) {
+      assert finalizerTi.isAlive();
+      activateGC();
+    }
+  }
+  
+  @Override
+  public Map<Integer,IntTable<String>> getInitialInternStringsMap() {
+    Map<Integer,IntTable<String>> interns = new HashMap<Integer,IntTable<String>>();
+    interns.put(0, appCtx.getInternStrings());
+    return interns;
+  }
+  
+  //---------- Predicates used to query threads from ThreadList ---------- //
+  
+  @Override
+  public Predicate<ThreadInfo> getRunnablePredicate() {
+    return runnablePredicate;
+  }
+  
+  @Override
+  public Predicate<ThreadInfo> getAppTimedoutRunnablePredicate() {
+    return getRunnablePredicate();
+  }
+  
+  @Override
+  public Predicate<ThreadInfo> getDaemonRunnablePredicate() {
+    return daemonRunnable;
+  }
+  
+  // ---------- Methods for handling finalizers ---------- //
+
+  @Override
+  void updateFinalizerQueues () {
+    getFinalizerThread().processNewFinalizables();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/SingleSlotFieldInfo.java b/src/main/gov/nasa/jpf/vm/SingleSlotFieldInfo.java
new file mode 100644 (file)
index 0000000..a9bbf86
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * field type that requires a single slot (all but long and double)
+ */
+public abstract class SingleSlotFieldInfo extends FieldInfo {
+
+    protected SingleSlotFieldInfo (String name, String signature, int modifiers) {
+      super(name, signature, modifiers);
+    }
+    
+    @Override
+       public boolean is1SlotField(){
+      return true;
+    }
+}
diff --git a/src/main/gov/nasa/jpf/vm/StackFrame.java b/src/main/gov/nasa/jpf/vm/StackFrame.java
new file mode 100644 (file)
index 0000000..0c642af
--- /dev/null
@@ -0,0 +1,2281 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.BitSetN;
+import gov.nasa.jpf.util.BitSet1024;
+import gov.nasa.jpf.util.BitSet256;
+import gov.nasa.jpf.util.BitSet64;
+import gov.nasa.jpf.util.FixedBitSet;
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.OATHash;
+import gov.nasa.jpf.util.ObjectList;
+import gov.nasa.jpf.util.PrintUtils;
+import gov.nasa.jpf.vm.bytecode.InvokeInstruction;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.Iterator;
+
+
+/**
+ * Describes callerSlots stack frame.
+ *
+ * Java methods always have bounded local and operand stack sizes, computed
+ * at compile time, stored in the classfile, and checked at runtime by the
+ * bytecode verifier. Consequently, we combine locals and operands in one
+ * data structure with the following layout
+ *
+ *   slot[0]                : 'this'
+ *   ..                          .. local vars
+ *   slot[stackBase-1]      : last local var
+ *   slot[stackBase]        : first operand slot
+ *   ..    ^
+ *   ..    | operand stack range
+ *   ..    v
+ *   slot[top]              : highest used operand slot
+ *
+ */
+public abstract class StackFrame implements Cloneable {
+  
+  /**
+   * this StackFrame is not allowed to be modified anymore because it has been state stored.
+   * Set during state storage and checked upon each modification, causing exceptions on attempts
+   * to modify callerSlots frozen instance. The flag is reset in clones
+   */
+  public static final int   ATTR_IS_FROZEN  = 0x100;  
+  static final int ATTR_IS_REFLECTION = 0x1000;  
+   /**
+    * the previous StackFrame (usually the caller, null if first). To be set when
+    * the frame is pushed on the ThreadInfo callstack
+    */
+  protected StackFrame prev;
+
+  /**
+   * state management related attributes similar to ElementInfo. The lower 16 bits
+   * are stored/restored, the upper 16 bits are for transient use
+   */
+  protected int attributes;
+
+  
+  protected int top;                // top index of the operand stack (NOT size)
+                                    // this points to the last pushed value
+
+  protected int thisRef = MJIEnv.NULL;       // slots[0] can change, but we have to keep 'this'
+  protected int stackBase;          // index where the operand stack begins
+
+  protected int[] slots;            // the combined local and operand slots
+  protected FixedBitSet isRef;      // which slots contain references
+
+  protected Object frameAttr;       // optional user attrs for the whole frame
+  
+  /*
+   * This array can be used to store attributes (e.g. variable names) for
+   * operands. We don't do anything with this except of preserving it (across
+   * dups etc.), so it's pretty much up to the VM listeners/peers what's stored
+   *
+   * NOTE: attribute values are not restored upon backtracking per default, but
+   * attribute references are. If you need restoration of values, use copy-on-write
+   * in your clients
+   *
+   * these are set on demand
+   */
+  protected Object[] attrs = null;  // the combined user-defined callerSlots (set on demand)
+
+  protected Instruction pc;         // the next insn to execute (program counter)
+  protected MethodInfo mi;          // which method is executed in this frame
+
+  static final int[] EMPTY_ARRAY = new int[0];
+  static final FixedBitSet EMPTY_BITSET = new BitSet64();
+
+  protected StackFrame (MethodInfo callee, int nLocals, int nOperands){
+    mi = callee;
+    pc = mi.getInstruction(0);
+
+    stackBase = nLocals;
+    top = nLocals-1;
+
+    int nSlots = nLocals + nOperands;
+    if (nSlots > 0){
+      slots = new int[nLocals + nOperands];
+      isRef = createReferenceMap(slots.length);
+    } else {
+      // NativeStackFrames don't use locals or operands, but we
+      // don't want to add tests to all our methods
+      slots = EMPTY_ARRAY;
+      isRef = EMPTY_BITSET;
+    }
+  }
+  
+  public StackFrame (MethodInfo callee){
+    this( callee, callee.getMaxLocals(), callee.getMaxStack());
+  }
+
+
+
+  /**
+   * Creates an empty stack frame. Used by clone.
+   */
+  protected StackFrame () {
+  }
+
+  /**
+   * creates callerSlots dummy Stackframe for testing of operand/local operations
+   * NOTE - TESTING ONLY! this does not have a MethodInfo
+   */
+  protected StackFrame (int nLocals, int nOperands){
+    stackBase = nLocals;
+    slots = new int[nLocals + nOperands];
+    isRef = createReferenceMap(slots.length);
+    top = nLocals-1;  // index, not size!
+  }
+  
+  /**
+   * re-execute method from the beginning - use with care
+   */
+  public void reset() {
+    pc = mi.getInstruction(0);
+  }  
+  
+
+
+  protected FixedBitSet createReferenceMap (int nSlots){
+    if (nSlots <= 64){
+      return new BitSet64();
+    } else if (nSlots <= 256){
+      return new BitSet256();  
+    } else if (nSlots <= 1024) {
+      return new BitSet1024();
+    } else {
+      return new BitSetN(nSlots);
+    }
+  }
+
+  public boolean isNative() {
+    return false;
+  }
+  
+  public StackFrame getCallerFrame (){
+    MethodInfo callee = mi;
+    for (StackFrame frame = getPrevious(); frame != null; frame = frame.getPrevious()){
+      Instruction insn = frame.getPC();
+      if (insn instanceof InvokeInstruction){
+        InvokeInstruction call = (InvokeInstruction)insn;
+        if (call.getInvokedMethod() == callee){
+          return frame;
+        }
+      }
+    }
+    
+    return null;
+  }
+  
+  /**
+   * return the object reference for an instance method to be called (we are still in the
+   * caller's frame). This only makes sense after all params have been pushed, before the
+   * INVOKEx insn is executed
+   */
+  public int getCalleeThis (MethodInfo mi) {
+    return getCalleeThis(mi.getArgumentsSize());
+  }
+
+  /**
+   * return reference of called object in the context of the caller
+   * (i.e. we are in the caller frame)
+   */
+  public int getCalleeThis (int size) {
+    // top is the topmost index
+    int i = size-1;
+    if (top < i) {
+      return MJIEnv.NULL;
+    }
+
+    return slots[top-i];
+  }
+
+  public StackFrame getPrevious() {
+    return prev;
+  }
+  
+  /**
+   * to be set (by ThreadInfo) when the frame is pushed. Can also be used
+   * for non-local gotos, but be warned - that's tricky
+   */
+  public void setPrevious (StackFrame frame){
+    prev = frame;
+  }
+
+  public Object getLocalOrFieldValue (String id) {
+    // try locals first
+    LocalVarInfo lv = mi.getLocalVar(id, pc.getPosition());
+    if (lv != null){
+      return getLocalValueObject(lv);
+    }
+
+    // then fields
+    return getFieldValue(id);
+  }
+
+  public Object getLocalValueObject (LocalVarInfo lv) {
+    if (lv != null) { // might not have been compiled with debug info
+      String sig = lv.getSignature();
+      int slotIdx = lv.getSlotIndex();
+      int v = slots[slotIdx];
+
+      switch (sig.charAt(0)) {
+        case 'Z':
+          return Boolean.valueOf(v != 0);
+        case 'B':
+          return new Byte((byte) v);
+        case 'C':
+          return new Character((char) v);
+        case 'S':
+          return new Short((short) v);
+        case 'I':
+          return new Integer(v);
+        case 'J':
+          return new Long(Types.intsToLong(slots[slotIdx + 1], v)); // Java is big endian, Types expects low,high
+        case 'F':
+          return new Float(Float.intBitsToFloat(v));
+        case 'D':
+          return new Double(Double.longBitsToDouble(Types.intsToLong(slots[slotIdx + 1], v)));
+        default:  // reference
+          if (v >= 0) {
+            return VM.getVM().getHeap().get(v);
+          }
+      }
+    }
+
+    return null;
+  }
+
+  public Object getFieldValue (String id) {
+    // try instance fields first
+    if (thisRef != MJIEnv.NULL) {  // it's an instance method
+      ElementInfo ei = VM.getVM().getHeap().get(thisRef);
+      Object v = ei.getFieldValueObject(id);
+      if (v != null) {
+        return v;
+      }
+    }
+
+    // check static fields (in method class and its superclasses)
+    return mi.getClassInfo().getStaticFieldValueObject(id);
+  }
+
+  public ClassInfo getClassInfo () {
+    return mi.getClassInfo();
+  }
+
+  public String getClassName () {
+    return mi.getClassInfo().getName();
+  }
+
+  public String getSourceFile () {
+    return mi.getClassInfo().getSourceFileName();
+  }
+
+  /**
+   * does any of the 'nTopSlots' hold callerSlots reference value of 'objRef'
+   * 'nTopSlots' is usually obtained from MethodInfo.getNumberOfCallerStackSlots()
+   */
+  public boolean includesReferenceOperand (int nTopSlots, int objRef){
+
+    for (int i=0, j=top-nTopSlots+1; i<nTopSlots && j>=0; i++, j++) {
+      if (isRef.get(j) && (slots[j] == objRef)){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * does any of the operand slots hold callerSlots reference value of 'objRef'
+   */
+  public boolean includesReferenceOperand (int objRef){
+
+    for (int i=stackBase; i<=top; i++) {
+      if (isRef.get(i) && (slots[i] == objRef)){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  /**
+   * is this StackFrame modifying the KernelState
+   * this is true unless this is callerSlots NativeStackFrame
+   */
+  public boolean modifiesState() {
+    return true;
+  }
+
+  public boolean isDirectCallFrame () {
+    return false;
+  }
+
+  public boolean isSynthetic() {
+    return false;
+  }
+
+  // gets and sets some derived information
+  public int getLine () {
+    return mi.getLineNumber(pc);
+  }
+
+
+  /**
+   * generic visitor for reference arguments
+   */
+  public void processRefArguments (MethodInfo miCallee, ReferenceProcessor visitor){
+    int nArgSlots = miCallee.getArgumentsSize();
+
+    for (int i=top-1; i>=top-nArgSlots; i--){
+      if (isRef.get(i)){
+        visitor.processReference(slots[i]);
+      }
+    }
+  }
+
+  public int getSlot(int idx){
+    return slots[idx];
+  }
+  public boolean isReferenceSlot(int idx){
+    return isRef.get(idx);
+  }
+
+
+  public void setOperand (int offset, int v, boolean isRefValue){
+    int i = top-offset;
+    slots[i] = v;
+    isRef.set(i, isRefValue);
+  }
+
+  
+  //----------------------------- various attribute accessors
+
+  public boolean hasAttrs () {
+    return attrs != null;
+  }
+
+  public boolean hasFrameAttr(){
+    return frameAttr != null;
+  }
+  
+  public boolean hasFrameAttr (Class<?> attrType){
+    return ObjectList.containsType(frameAttr, attrType);
+  }
+  
+  public boolean hasFrameAttrValue (Object a){
+    return ObjectList.contains(frameAttr, a);
+  }
+  
+  //--- the frame attr accessors 
+  
+ /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at callerSlots time, or check/process result with ObjectList
+   */
+  public Object getFrameAttr(){
+    return frameAttr;
+  }
+
+  /**
+   * this replaces all of them - use only if you know there are no 
+   * SystemAttributes in the list (which would cause an exception)
+   */
+  public void setFrameAttr (Object attr){
+    frameAttr = ObjectList.set(frameAttr, attr);    
+  }
+
+  public void addFrameAttr (Object attr){
+    frameAttr = ObjectList.add(frameAttr, attr);
+  }
+
+  public void removeFrameAttr (Object attr){
+    frameAttr = ObjectList.remove(frameAttr, attr);
+  }
+
+  public void replaceFrameAttr (Object oldAttr, Object newAttr){
+    frameAttr = ObjectList.replace(frameAttr, oldAttr, newAttr);
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getFrameAttr (Class<T> attrType) {
+    return ObjectList.getFirst(frameAttr, attrType);
+  }
+  
+  public <T> T getAndResetFrameAttr (Class<T> attrType) {
+    T attr = ObjectList.getFirst(frameAttr, attrType);
+    if (attr != null){
+      frameAttr = ObjectList.remove(frameAttr, attr);
+    }
+    return attr;
+  }
+  
+
+  public <T> T getNextFrameAttr (Class<T> attrType, Object prev) {
+    return ObjectList.getNext(frameAttr, attrType, prev);
+  }
+
+  public ObjectList.Iterator frameAttrIterator(){
+    return ObjectList.iterator(frameAttr);
+  }
+  
+  public <T> ObjectList.TypedIterator<T> frameAttrIterator(Class<T> attrType){
+    return ObjectList.typedIterator(frameAttr, attrType);
+  }
+  
+  //--- the top single-slot operand attrs
+
+  public boolean hasOperandAttr(){
+    if ((top >= stackBase) && (attrs != null)){
+      return (attrs[top] != null);
+    }
+    return false;
+  }
+  public boolean hasOperandAttr(Class<?> type){
+    if ((top >= stackBase) && (attrs != null)){
+      return ObjectList.containsType(attrs[top], type);
+    }
+    return false;
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at callerSlots time, or check/process result with ObjectList
+   */
+  public Object getOperandAttr () {
+    if ((top >= stackBase) && (attrs != null)){
+      return attrs[top];
+    }
+    return null;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at callerSlots time
+   *  - you obtained the value you set by callerSlots previous getXAttr()
+   *  - you constructed callerSlots multi value list with ObjectList.createList()
+   */
+  public void setOperandAttr (Object a){
+    assert (top >= stackBase);
+    if (attrs == null) {
+      if (a == null) return;
+      attrs = new Object[slots.length];
+    }
+    attrs[top] = a;
+  }
+
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getOperandAttr (Class<T> attrType){
+    assert (top >= stackBase);
+    
+    if ((attrs != null)){
+      return ObjectList.getFirst(attrs[top], attrType);
+    }
+    return null;
+  }
+  public <T> T getNextOperandAttr (Class<T> attrType, Object prev){
+    assert (top >= stackBase);
+    if (attrs != null){
+      return ObjectList.getNext( attrs[top], attrType, prev);
+    }
+    return null;
+  }
+  public Iterator operandAttrIterator(){
+    assert (top >= stackBase);
+    Object a = (attrs != null) ? attrs[top] : null;
+    return ObjectList.iterator(a);
+  }
+  public <T> Iterator<T> operandAttrIterator(Class<T> attrType){
+    assert (top >= stackBase);
+    Object a = (attrs != null) ? attrs[top] : null;
+    return ObjectList.typedIterator(a, attrType);
+  }
+  
+
+  public void addOperandAttr (Object a){
+    assert (top >= stackBase);
+    if (a != null){
+      if (attrs == null) {
+        attrs = new Object[slots.length];
+      }
+
+      attrs[top] = ObjectList.add(attrs[top], a);
+    }        
+  }
+  
+  public void removeOperandAttr (Object a){
+    assert (top >= stackBase) && (a != null);
+    if (attrs != null){
+      attrs[top] = ObjectList.remove(attrs[top], a);
+    }        
+  }
+  
+  public void replaceOperandAttr (Object oldAttr, Object newAttr){
+    assert (top >= stackBase) && (oldAttr != null) && (newAttr != null);
+    if (attrs != null){
+      attrs[top] = ObjectList.replace(attrs[top], oldAttr, newAttr);
+    }        
+  }
+  
+  
+  //--- offset operand attrs
+
+  public boolean hasOperandAttr(int offset){
+    int i = top-offset;
+    assert (i >= stackBase);
+    if (attrs != null){
+      return (attrs[i] != null);
+    }
+    return false;
+  }
+  public boolean hasOperandAttr(int offset, Class<?> type){
+    int i = top-offset;
+    assert (i >= stackBase);
+    if (attrs != null){
+      return ObjectList.containsType(attrs[i], type);
+    }
+    return false;
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at callerSlots time, or check/process result with ObjectList
+   */
+  public Object getOperandAttr (int offset) {
+    int i = top-offset;
+    assert (i >= stackBase);
+    
+    if (attrs != null) {
+      return attrs[i];
+    }
+    return null;
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at callerSlots time
+   *  - you obtained the value you set by callerSlots previous getXAttr()
+   *  - you constructed callerSlots multi value list with ObjectList.createList()
+   */  
+  public void setOperandAttr (int offset, Object a){
+    int i = top-offset;
+    assert (i >= stackBase);
+
+    if (attrs == null) {
+      if (a == null) return;
+      attrs = new Object[slots.length];
+    }
+    attrs[i] = a;
+  }
+
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getOperandAttr (int offset, Class<T> attrType){
+    int i = top-offset;
+    assert (i >= stackBase) : this;
+    if (attrs != null){
+      return ObjectList.getFirst(attrs[i], attrType);
+    }
+    return null;
+  }
+  public <T> T getNextOperandAttr (int offset, Class<T> attrType, Object prev){
+    int i = top-offset;
+    assert (i >= stackBase);
+    if (attrs != null){
+      return ObjectList.getNext( attrs[i], attrType, prev);
+    }
+    return null;
+  }
+  public ObjectList.Iterator operandAttrIterator(int offset){
+    int i = top-offset;
+    assert (i >= stackBase);
+    Object a = (attrs != null) ? attrs[i] : null;
+    return ObjectList.iterator(a);
+  }
+  public <T> ObjectList.TypedIterator<T> operandAttrIterator(int offset, Class<T> attrType){
+    int i = top-offset;
+    assert (i >= stackBase);
+    Object a = (attrs != null) ? attrs[i] : null;
+    return ObjectList.typedIterator(a, attrType);
+  }
+
+
+  public void addOperandAttr (int offset, Object a){
+    int i = top-offset;
+    assert (i >= stackBase);
+
+    if (a != null){
+      if (attrs == null) {
+        attrs = new Object[slots.length];
+      }
+      attrs[i] = ObjectList.add(attrs[i],a);
+    }    
+  }
+
+  public void removeOperandAttr (int offset, Object a){
+    int i = top-offset;
+    assert (i >= stackBase) && (a != null);
+    if (attrs != null){
+      attrs[i] = ObjectList.remove(attrs[i], a);
+    }        
+  }
+  
+  public void replaceOperandAttr (int offset, Object oldAttr, Object newAttr){
+    int i = top-offset;
+    assert (i >= stackBase) && (oldAttr != null) && (newAttr != null);
+    if (attrs != null){
+      attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr);
+    }        
+  }
+  
+  
+  //--- top double-slot operand attrs
+  // we store attributes for double slot values at the local var index,
+  // which is the lower one. The ..LongOperand.. APIs are handling this offset
+  public boolean hasLongOperandAttr(){
+    return hasOperandAttr(1);
+  }
+  public boolean hasLongOperandAttr(Class<?> type){
+    return hasOperandAttr(1, type);
+  }
+  
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at callerSlots time, or check/process result with ObjectList
+   */
+  public Object getLongOperandAttr () {
+    return getOperandAttr(1);
+  }
+
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at callerSlots time
+   *  - you obtained the value you set by callerSlots previous getXAttr()
+   *  - you constructed callerSlots multi value list with ObjectList.createList()
+   */  
+  public void setLongOperandAttr (Object a){
+    setOperandAttr(1, a);
+  }
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getLongOperandAttr (Class<T> attrType) {
+    return getOperandAttr(1, attrType);
+  }
+  public <T> T getNextLongOperandAttr (Class<T> attrType, Object prev) {
+    return getNextOperandAttr(1, attrType, prev);
+  }
+  public ObjectList.Iterator longOperandAttrIterator(){
+    return operandAttrIterator(1);
+  }
+  public <T> ObjectList.TypedIterator<T> longOperandAttrIterator(Class<T> attrType){
+    return operandAttrIterator(1, attrType);
+  }
+    
+  public void addLongOperandAttr (Object a){
+    addOperandAttr(1, a);
+  }
+
+  public void removeLongOperandAttr (Object a){
+    removeOperandAttr(1, a);
+  }
+
+  public void replaceLongOperandAttr (Object oldAttr, Object newAttr){
+    replaceOperandAttr(1, oldAttr, newAttr);
+  }
+
+
+  //--- local attrs
+  // single- or double-slot - you have to provide the var index anyways)
+  
+  public boolean hasLocalAttr(int index){
+    assert index < stackBase;
+    if (attrs != null){
+      return (attrs[index] != null);
+    }
+    return false;
+  }
+  public boolean hasLocalAttr(int index, Class<?> type){
+    assert index < stackBase;
+    if (attrs != null){
+      return ObjectList.containsType(attrs[index], type);
+    }
+    return false;
+  }
+
+  /**
+   * this returns all of them - use either if you know there will be only
+   * one attribute at callerSlots time, or check/process result with ObjectList
+   */
+  public Object getLocalAttr (int index){
+    assert index < stackBase;
+    if (attrs != null){
+      return attrs[index];
+    }
+    return null;
+  }
+   
+  public Object getLongLocalAttr (int index){
+    return getLocalAttr( index);
+  }
+  
+  /**
+   * this replaces all of them - use only if you know 
+   *  - there will be only one attribute at callerSlots time
+   *  - you obtained the value you set by callerSlots previous getXAttr()
+   *  - you constructed callerSlots multi value list with ObjectList.createList()
+   */  
+  public void setLocalAttr (int index, Object a) {
+    assert index < stackBase;
+    if (attrs == null){
+      if (a == null) return;
+      attrs = new Object[slots.length];
+    }
+    attrs[index] = a;
+  }
+
+  public void setLongLocalAttr (int index, Object a){
+    setLocalAttr( index, a);
+  }
+  
+  public void addLongLocalAttr (int index, Object a){
+    addLocalAttr( index, a);
+  }
+  
+  /**
+   * this only returns the first attr of this type, there can be more
+   * if you don't use client private types or the provided type is too general
+   */
+  public <T> T getLocalAttr (int index, Class<T> attrType){
+    assert index < stackBase;
+    if (attrs != null){
+      return ObjectList.getFirst( attrs[index], attrType);
+    }
+    return null;
+  }
+  public <T> T getNextLocalAttr (int index, Class<T> attrType, Object prev){
+    assert index < stackBase;
+    if (attrs != null){
+      return ObjectList.getNext( attrs[index], attrType, prev);
+    }
+    return null;
+  }
+  public ObjectList.Iterator localAttrIterator(int index){
+    assert index < stackBase;
+    Object a = (attrs != null) ? attrs[index] : null;
+    return ObjectList.iterator(a);
+  }
+  public <T> ObjectList.TypedIterator<T> localAttrIterator(int index, Class<T> attrType){
+    assert index < stackBase;
+    Object a = (attrs != null) ? attrs[index] : null;
+    return ObjectList.typedIterator(a, attrType);
+  }
+  
+
+  public void addLocalAttr (int index, Object attr){
+    assert index < stackBase;
+    if (attrs == null){
+      if (attr == null) return;
+      attrs = new Object[slots.length];
+    }
+    attrs[index] = ObjectList.add(attrs[index], attr);
+  }
+  
+  public void removeLocalAttr (int index, Object attr){
+    assert index < stackBase && attr != null;
+    if (attr != null){
+      attrs[index] = ObjectList.remove(attrs[index], attr);    
+    }
+  }
+
+  public void replaceLocalAttr (int index, Object oldAttr, Object newAttr){
+    assert index < stackBase && oldAttr != null && newAttr != null;
+    if (attrs == null){
+      attrs[index] = ObjectList.replace(attrs[index], oldAttr, newAttr);    
+    }
+  }
+  
+  //--- various special attr accessors
+
+  /**
+   * helper to quickly find out if any of the locals slots holds
+   * an attribute of the provided type
+   * 
+   * @param attrType type of attribute to look for
+   * @param startIdx local index to start from
+   * @return index of local slot with attribute, -1 if none found
+   */
+  public int getLocalAttrIndex (Class<?> attrType, int startIdx){
+    if (attrs != null){
+      for (int i=startIdx; i<stackBase; i++){
+        Object a = attrs[i];
+        if (ObjectList.containsType(a, attrType)){
+          return i;
+        }
+      }
+    }
+
+    return -1;
+  }
+  
+  // <2do> this is machine dependent since it uses the operand stack. Only here because there
+  // is no suitable place to factor this out between xStackFrame, xNativeStackFrame and xDirectCallStackFrame
+  // (another example of missing multiple inheritance)
+  // Needs to be overridden for Dalvik
+  
+  /**
+   * this retrieves the argument values from the caller, i.e. the previous stackframe 
+   * 
+   * references are returned as ElementInfos or null
+   * primitive values are returned as box objects (e.g. int -> Integer)
+   */
+  public Object[] getArgumentValues (ThreadInfo ti){
+    StackFrame callerFrame = getCallerFrame();
+    if (callerFrame != null){
+      return callerFrame.getCallArguments(ti);
+    } else {
+      // <2do> what about main(String[] args) ?
+    }
+    
+    return null;
+  }
+  
+  /**
+   * get the arguments of the executed call
+   * Note - this throws an exception if the StackFrame pc is not an InvokeInstruction
+   */
+  public Object[] getCallArguments (ThreadInfo ti){
+    if (pc == null || !(pc instanceof InvokeInstruction)){
+      throw new JPFException("stackframe not executing invoke: " + pc);
+    }
+    
+    InvokeInstruction call = (InvokeInstruction) pc;    
+    MethodInfo callee = call.getInvokedMethod();
+
+    byte[] argTypes = callee.getArgumentTypes();
+
+    return getArgumentsValues(ti, argTypes);
+  }
+
+  public Object[] getArgumentsValues (ThreadInfo ti, byte[] argTypes){
+    int n = argTypes.length;
+    Object[] args = new Object[n];
+
+    for (int i=n-1, off=0; i>=0; i--) {
+      switch (argTypes[i]) {
+      case Types.T_ARRAY:
+      //case Types.T_OBJECT:
+      case Types.T_REFERENCE:
+        int ref = peek(off);
+        if (ref != MJIEnv.NULL) {
+          args[i] = ti.getElementInfo(ref);
+        } else {
+          args[i] = null;
+        }
+        off++;
+        break;
+
+      case Types.T_LONG:
+        args[i] = new Long(peekLong(off));
+        off+=2;
+        break;
+      case Types.T_DOUBLE:
+        args[i] = new Double(Types.longToDouble(peekLong(off)));
+        off+=2;
+        break;
+
+      case Types.T_BOOLEAN:
+        args[i] = new Boolean(peek(off) != 0);
+        off++;
+        break;
+      case Types.T_BYTE:
+        args[i] = new Byte((byte)peek(off));
+        off++;
+        break;
+      case Types.T_CHAR:
+        args[i] = new Character((char)peek(off));
+        off++;
+        break;
+      case Types.T_SHORT:
+        args[i] = new Short((short)peek(off));
+        off++;
+        break;
+      case Types.T_INT:
+        args[i] = new Integer(peek(off));
+        off++;
+        break;
+      case Types.T_FLOAT:
+        args[i] = new Float(Types.intToFloat(peek(off)));
+        off++;
+        break;
+      default:
+        // error, unknown argument type
+      }
+    }
+    return args;
+  }
+  
+  /**
+   * return an array of all argument attrs, which in turn can be lists. If
+   * you have to retrieve values, use the ObjectList APIs
+   * 
+   * this is here (and not in ThreadInfo) because we might call it
+   * on callerSlots cached/cloned StackFrame (caller stack might be already
+   * modified, e.g. for callerSlots native method).
+   * to be used from listeners.
+   */
+  public Object[] getArgumentAttrs (MethodInfo miCallee) {
+    if (attrs != null) {
+      int nArgs = miCallee.getNumberOfArguments();
+      byte[] at = miCallee.getArgumentTypes();
+      Object[] a;
+
+      if (!miCallee.isStatic()) {
+        a = new Object[nArgs+1];
+        a[0] = getOperandAttr(miCallee.getArgumentsSize()-1);
+      } else {
+        a = new Object[nArgs];
+      }
+
+      for (int i=nArgs-1, off=0, j=a.length-1; i>=0; i--, j--) {
+        byte argType = at[i];
+        if (argType == Types.T_LONG || argType == Types.T_DOUBLE) {
+          a[j] = getOperandAttr(off+1);
+          off +=2;
+        } else {
+          a[j] = getOperandAttr(off);
+          off++;
+        }
+      }
+
+      return a;
+
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * check if there is any argument attr of the provided type on the operand stack
+   * this is far more efficient than retrieving attribute values (we don't
+   * care for argument types)
+   */
+  public boolean hasArgumentAttr (MethodInfo miCallee, Class<?> attrType){
+    if (attrs != null) {
+      int nArgSlots = miCallee.getArgumentsSize();
+
+      for (int i=0; i<nArgSlots; i++){
+        Object a = getOperandAttr(i);
+        if (ObjectList.containsType(a, attrType)){
+          return true;
+        }
+      }
+    }
+
+    return false;
+  }
+
+  public boolean hasArgumentObjectAttr (ThreadInfo ti, MethodInfo miCallee, Class<?> type){
+    int nArgSlots = miCallee.getArgumentsSize();
+    for (int i=0; i<nArgSlots; i++){
+      if (isOperandRef(i)){
+        int objRef = peek(i);
+        if (objRef != MJIEnv.NULL){
+          ElementInfo ei = ti.getElementInfo(objRef);
+          if (ei.getObjectAttr(type) != null) {
+            return true;
+          }
+        }
+      }
+    }
+
+    return false;
+  }
+  
+  
+  // -- end attrs --
+  
+  public void setLocalReferenceVariable (int index, int ref){
+    if (slots[index] != MJIEnv.NULL){
+      VM.getVM().getSystemState().activateGC();
+    }
+    
+    slots[index] = ref;
+    isRef.set(index);
+  }
+
+  public void setLocalVariable (int index, int v){
+    // Hmm, should we treat this an error?
+    if (isRef.get(index) && slots[index] != MJIEnv.NULL){
+      VM.getVM().getSystemState().activateGC();      
+    }
+    
+    slots[index] = v;
+    isRef.clear(index);
+  }
+  
+  public void setFloatLocalVariable (int index, float f){
+    setLocalVariable( index, Float.floatToIntBits(f));
+  }
+
+  public void setDoubleLocalVariable (int index, double f){
+    setLongLocalVariable( index, Double.doubleToLongBits(f));
+  }
+
+  
+  // <2do> replace with non-ref version
+  public void setLocalVariable (int index, int v, boolean ref) {
+    // <2do> activateGc should be replaced by local refChanged
+    boolean activateGc = ref || (isRef.get(index) && (slots[index] != MJIEnv.NULL));
+
+    slots[index] = v;
+    isRef.set(index,ref);
+
+    if (activateGc) {
+        VM.getVM().getSystemState().activateGC();
+    }
+  }
+
+  public int getLocalVariable (int i) {
+    return slots[i];
+  }
+
+  public int getLocalVariable (String name) {
+    int idx = getLocalVariableSlotIndex(name);
+    if (idx >= 0) {
+      return getLocalVariable(idx);
+    } else {
+      throw new JPFException("local variable not found: " + name);
+    }
+  }
+
+  public int getLocalVariableCount() {
+    return stackBase;
+  }
+
+  /**
+   * <2do> - this should return only LocalVarInfo for the current pc
+   */
+  public LocalVarInfo[] getLocalVars () {
+    return mi.getLocalVars();
+  }
+
+
+  public boolean isLocalVariableRef (int idx) {
+    return isRef.get(idx);
+  }
+
+  public String getLocalVariableType (String name) {
+    LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition()+pc.getLength());
+    if (lv != null){
+      return lv.getType();
+    }
+
+    return null;
+  }
+
+  public String getLocalVariableType (int idx){
+    LocalVarInfo lv = mi.getLocalVar(idx, pc.getPosition()+pc.getLength());
+    if (lv != null){
+      return lv.getType();
+    }
+
+    return null;
+  }
+
+  public LocalVarInfo getLocalVarInfo (String name){
+    return mi.getLocalVar(name, pc.getPosition()+pc.getLength());
+  }
+
+  public LocalVarInfo getLocalVarInfo (int idx){
+    return mi.getLocalVar(idx, pc.getPosition()+pc.getLength());
+  }
+
+  public void setThis (int objRef){
+    thisRef = objRef;
+  }
+  
+  public FixedBitSet getReferenceMap(){
+    return isRef;
+  }
+
+  //--- direct slot access - provided for machine-independent clients
+  
+  public int[] getSlots () {
+    return slots; // we should probably clone
+  }
+  public Object[] getSlotAttrs(){
+    return attrs;
+  }
+  public Object getSlotAttr (int i){
+    if (attrs != null){
+      return attrs[i];
+    }
+    return null;
+  }
+  public <T> T getSlotAttr (int i, Class<T> attrType){
+    if (attrs != null){
+      return ObjectList.getFirst( attrs[i], attrType);
+    }
+    return null;
+  }  
+  public void setSlotAttr (int i, Object a){
+    if (attrs == null){
+      attrs = new Object[slots.length];
+    }
+    attrs[i] = a;
+  }
+  public void addSlotAttr (int i, Object a){
+    if (a != null){
+      if (attrs == null) {
+        attrs = new Object[slots.length];
+      }
+
+      attrs[i] = ObjectList.add(attrs[i], a);
+    }        
+  }  
+  public void replaceSlotAttr (int i, Object oldAttr, Object newAttr){
+    if (attrs != null){
+      attrs[i] = ObjectList.replace(attrs[i], oldAttr, newAttr);
+    }        
+  }
+  
+  
+  
+  public void visitReferenceSlots (ReferenceProcessor visitor){
+    for (int i=isRef.nextSetBit(0); i>=0 && i<=top; i=isRef.nextSetBit(i+1)){
+      visitor.processReference(slots[i]);
+    }
+  }
+
+  public void setLongLocalVariable (int index, long v) {
+    // WATCH OUT: apparently, slots can change type, so we have to
+    // reset the reference flag (happened in JavaSeq)
+
+    slots[index] = Types.hiLong(v);
+    isRef.clear(index);
+
+    index++;
+    slots[index] = Types.loLong(v);
+    isRef.clear(index);
+  }
+
+  public long getLongLocalVariable (int idx) {
+    return Types.intsToLong(slots[idx + 1], slots[idx]);
+  }
+  
+  public double getDoubleLocalVariable (int idx) {
+    return Types.intsToDouble(slots[idx + 1], slots[idx]);
+  }
+
+  public float getFloatLocalVariable (int idx) {
+    int bits = slots[idx];
+    return Float.intBitsToFloat(bits);
+  }
+
+  public double getDoubleLocalVariable (String name) {
+    int idx = getLocalVariableSlotIndex(name);
+    if (idx >= 0) {
+      return getDoubleLocalVariable(idx);
+    } else {
+      throw new JPFException("long local variable not found: " + name);
+    }    
+  }
+  
+  public long getLongLocalVariable (String name) {
+    int idx = getLocalVariableSlotIndex(name);
+
+    if (idx >= 0) {
+      return getLongLocalVariable(idx);
+    } else {
+      throw new JPFException("long local variable not found: " + name);
+    }
+  }
+
+  public MethodInfo getMethodInfo () {
+    return mi;
+  }
+
+  public String getMethodName () {
+    return mi.getName();
+  }
+
+  public boolean isOperandRef (int offset) {
+    return isRef.get(top-offset);
+  }
+
+  public boolean isOperandRef () {
+    return isRef.get(top);
+  }
+
+  //--- direct pc modification
+  // NOTE: this is dangerous, caller has to guarantee stack consistency
+  public void setPC (Instruction newpc) {
+    pc = newpc;
+  }
+
+  public Instruction getPC () {
+    return pc;
+  }
+
+  public void advancePC() {
+    int i = pc.getInstructionIndex() + 1;
+    if (i < mi.getNumberOfInstructions()) {
+      pc = mi.getInstruction(i);
+    } else {
+      pc = null;
+    }
+  }
+
+  public int getTopPos() {
+    return top;
+  }
+
+  ExceptionHandler getHandlerFor (ClassInfo ciException){
+    return mi.getHandlerFor (ciException, pc);
+  }
+  
+  public boolean isFirewall (){
+    return mi.isFirewall();
+  }
+  
+  public String getStackTraceInfo () {
+    StringBuilder sb = new StringBuilder(128);
+
+    if(!mi.isJPFInternal()) {
+       sb.append(mi.getStackTraceName());
+       
+       if(pc != null) {
+               sb.append('(');
+            sb.append( pc.getFilePos());
+            sb.append(')');
+       }
+    } else {
+       sb.append(mi.getName());
+       
+       if(mi.isMJI()) {
+               sb.append("(Native)");
+       } else {
+               sb.append("(Synthetic)");
+       }
+    }
+
+    return sb.toString();
+  }
+
+  /**
+   * if this is an instance method, return the reference of the corresponding object
+   * (note this only has to be in slot 0 upon entry)
+   */
+  public int getThis () {
+    return thisRef;
+  }
+
+  // stack operations
+  public void clearOperandStack () {
+    if (attrs != null){
+      for (int i=stackBase; i<= top; i++){
+        attrs[i] = null;
+      }
+    }
+    
+    top = stackBase-1;
+  }
+  
+  // this is callerSlots deep copy
+  @Override
+  public StackFrame clone () {
+    try {
+      StackFrame sf = (StackFrame) super.clone();
+
+      sf.defreeze();
+      
+      sf.slots = slots.clone();
+      sf.isRef = isRef.clone();
+
+      if (attrs != null){
+        sf.attrs = attrs.clone();
+      }
+
+      // frameAttr is not cloned to allow search global use 
+
+      return sf;
+    } catch (CloneNotSupportedException cnsx) {
+      throw new JPFException(cnsx);
+    }
+  }
+  
+  //--- change management
+  
+  protected void checkIsModifiable() {
+    if ((attributes & ATTR_IS_FROZEN) != 0) {
+      throw new JPFException("attempt to modify frozen stackframe: " + this);
+    }
+  }
+  
+  public void freeze() {
+    attributes |= ATTR_IS_FROZEN;
+  }
+
+  public void defreeze() {
+    attributes &= ~ATTR_IS_FROZEN;
+  }
+  
+  public boolean isFrozen() {
+    return ((attributes & ATTR_IS_FROZEN) != 0);    
+  }
+  
+  
+  public void setReflection(){
+    attributes |= ATTR_IS_REFLECTION;
+  }
+  
+  public boolean isReflection(){
+    return ((attributes & ATTR_IS_REFLECTION) != 0);
+  }
+
+  // all the dupses don't have any GC side effect (everything is already
+  // on the stack), so skip the GC requests associated with push()/pop()
+
+  public void dup () {
+    // .. A     =>
+    // .. A A
+    //    ^
+
+    int t= top;
+
+    int td=t+1;
+    slots[td] = slots[t];
+    isRef.set(td, isRef.get(t));
+
+    if (attrs != null){
+      attrs[td] = attrs[t];
+    }
+
+    top = td;
+  }
+
+  public void dup2 () {
+    // .. A B        =>
+    // .. A B A B
+    //      ^
+
+    int ts, td;
+    int t=top;
+
+    // duplicate A
+    td = t+1; ts = t-1;
+    slots[td] = slots[ts];
+    isRef.set(td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // duplicate B
+    td++; ts=t;
+    slots[td] = slots[ts];
+    isRef.set(td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    top = td;
+  }
+
+  public void dup2_x1 () {
+    // .. A B C       =>
+    // .. B C A B C
+    //        ^
+
+    int b, c;
+    boolean bRef, cRef;
+    Object bAnn = null, cAnn = null;
+    int ts, td;
+    int t = top;
+
+    // duplicate C
+    ts=t; td = t+2;                              // ts=top, td=top+2
+    slots[td] = c = slots[ts];
+    cRef = isRef.get(ts);
+    isRef.set(td,cRef);
+    if (attrs != null){
+      attrs[td] = cAnn = attrs[ts];
+    }
+
+    // duplicate B
+    ts--; td--;                                  // ts=top-1, td=top+1
+    slots[td] = b = slots[ts];
+    bRef = isRef.get(ts);
+    isRef.set(td, bRef);
+    if (attrs != null){
+      attrs[td] = bAnn = attrs[ts];
+    }
+
+    // shuffle A
+    ts=t-2; td=t;                                // ts=top-2, td=top
+    slots[td] = slots[ts];
+    isRef.set(td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle B
+    td = ts;                                     // td=top-2
+    slots[td] = b;
+    isRef.set(td, bRef);
+    if (attrs != null){
+      attrs[td] = bAnn;
+    }
+
+    // shuffle C
+    td++;                                        // td=top-1
+    slots[td] = c;
+    isRef.set(td, cRef);
+    if (attrs != null){
+      attrs[td] = cAnn;
+    }
+
+    top += 2;
+  }
+
+  public void dup2_x2 () {
+    // .. A B C D       =>
+    // .. C D A B C D
+    //          ^
+
+    int c, d;
+    boolean cRef, dRef;
+    Object cAnn = null, dAnn = null;
+    int ts, td;
+    int t = top;
+
+    // duplicate C
+    ts = t-1; td = t+1;                          // ts=top-1, td=top+1
+    slots[td] = c = slots[ts];
+    cRef = isRef.get(ts);
+    isRef.set(td, cRef);
+    if (attrs != null){
+      attrs[td] = cAnn = attrs[ts];
+    }
+
+    // duplicate D
+    ts=t; td++;                                  // ts=top, td=top+2
+    slots[td] = d = slots[ts];
+    dRef = isRef.get(ts);
+    isRef.set(td, dRef);
+    if (attrs != null){
+      attrs[td] = dAnn = attrs[ts];
+    }
+
+    // shuffle A
+    ts = t-3; td = t-1;                          // ts=top-3, td=top-1
+    slots[td] = slots[ts];
+    isRef.set( td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle B
+    ts++; td = t;                                // ts = top-2
+    slots[td] = slots[ts];
+    isRef.set( td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle D
+    td = ts;                                     // td = top-2
+    slots[td] = d;
+    isRef.set( td, dRef);
+    if (attrs != null){
+      attrs[td] = dAnn;
+    }
+
+    // shuffle C
+    td--;                                        // td = top-3
+    slots[td] = c;
+    isRef.set(td, cRef);
+    if (attrs != null){
+      attrs[td] = cAnn;
+    }
+
+    top += 2;
+  }
+
+  public void dup_x1 () {
+    // .. A B     =>
+    // .. B A B
+    //      ^
+
+    int b;
+    boolean bRef;
+    Object bAnn = null;
+    int ts, td;
+    int t = top;
+
+    // duplicate B
+    ts = t; td = t+1;
+    slots[td] = b = slots[ts];
+    bRef = isRef.get(ts);
+    isRef.set(td, bRef);
+    if (attrs != null){
+      attrs[td] = bAnn = attrs[ts];
+    }
+
+    // shuffle A
+    ts--; td = t;       // ts=top-1, td = top
+    slots[td] = slots[ts];
+    isRef.set( td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle B
+    td = ts;            // td=top-1
+    slots[td] = b;
+    isRef.set( td, bRef);
+    if (attrs != null){
+      attrs[td] = bAnn;
+    }
+
+    top++;
+  }
+
+  public void dup_x2 () {
+    // .. A B C     =>
+    // .. C A B C
+    //        ^
+
+    int c;
+    boolean cRef;
+    Object cAnn = null;
+    int ts, td;
+    int t = top;
+
+    // duplicate C
+    ts = t; td = t+1;
+    slots[td] = c = slots[ts];
+    cRef = isRef.get(ts);
+    isRef.set( td, cRef);
+    if (attrs != null){
+      attrs[td] = cAnn = attrs[ts];
+    }
+
+    // shuffle B
+    td = ts; ts--;               // td=top, ts=top-1
+    slots[td] = slots[ts];
+    isRef.set( td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle A
+    td=ts; ts--;                 // td=top-1, ts=top-2
+    slots[td] = slots[ts];
+    isRef.set( td, isRef.get(ts));
+    if (attrs != null){
+      attrs[td] = attrs[ts];
+    }
+
+    // shuffle C
+    td = ts;                     // td = top-2
+    slots[td] = c;
+    isRef.set(td, cRef);
+    if (attrs != null){
+      attrs[td] = cAnn;
+    }
+
+    top++;
+  }
+
+  /**
+   * to be used to check if a StackFrame got cloned due to its execution
+   * changing attributes and/or slots, but otherwise represents the same
+   * execution
+   */
+  public boolean originatesFrom (StackFrame other){
+    if (other == this){
+      return true;
+    } else {
+      return ((mi == other.mi) &&
+              (prev == other.prev) &&
+              (top == other.top) &&
+              (getClass() == other.getClass()));
+    }
+  }
+  
+  
+  // <2do> pcm - I assume this compares snapshots, not types. Otherwise it
+  // would be pointless to equals stack/local values
+  @Override
+  public boolean equals (Object o) {
+    if (o instanceof StackFrame){
+      StackFrame other = (StackFrame)o;
+
+      if (prev != other.prev) {
+        return false;
+      }
+      if (pc != other.pc) {
+        return false;
+      }
+      if (mi != other.mi) {
+        return false;
+      }
+      if (top != other.top){
+        return false;
+      }
+
+      int[] otherSlots = other.slots;
+      FixedBitSet otherIsRef = other.isRef;
+      for (int i=0; i<=top; i++){
+        if ( slots[i] != otherSlots[i]){
+          return false;
+        }
+        if ( isRef.get(i) != otherIsRef.get(i)){
+          return false;
+        }
+      }
+
+      if (!Misc.compare(top,attrs,other.attrs)){
+        return false;
+      }
+      
+      if (!ObjectList.equals(frameAttr, other.frameAttr)){
+        return false;
+      }
+
+      return true;
+    }
+
+    return false;
+  }
+  
+  public boolean hasAnyRef () {
+    return isRef.cardinality() > 0;
+  }
+  
+  public int mixinExecutionStateHash (int h) {
+    h = OATHash.hashMixin( h, mi.getGlobalId());
+    
+    if (pc != null){
+      h = OATHash.hashMixin(h, pc.getInstructionIndex());
+      // we don't need the bytecode since there can only be one insn with this index in this method
+    }
+    
+    for (int i=0; i<top; i++) {
+      h = OATHash.hashMixin(h, slots[i]);
+    }
+   
+    return h;
+  }
+
+  protected void hash (HashData hd) {
+    if (prev != null){
+      hd.add(prev.objectHashCode());
+    }
+    hd.add(mi.getGlobalId());
+
+    if (pc != null){
+      hd.add(pc.getInstructionIndex());
+    }
+
+    for (int i=0; i<=top; i++){
+      hd.add(slots[i]);
+    }
+
+    isRef.hash(hd);
+
+    // it's debatable if we add the attributes to the state, but whatever it
+    // is, it should be kept consistent with the Fields.hash()
+    if (attrs != null){
+      for (int i=0; i<=top; i++){
+        ObjectList.hash( attrs[i], hd);
+      }
+    }
+    
+    if (frameAttr != null){
+      ObjectList.hash(frameAttr, hd);
+    }
+  }
+
+  // computes an hash code for the hash table
+  // the default hash code is different for each object
+  // we need to redifine it to make the hash table work
+  @Override
+  public int hashCode () {
+    HashData hd = new HashData();
+    hash(hd);
+    return hd.getValue();
+  }
+
+  /**
+   * mark all objects reachable from local or operand stack positions containing
+   * references. Done during phase1 marking of threads (the stack is one of the
+   * Thread gc roots)
+   */
+  public void markThreadRoots (Heap heap, int tid) {
+
+    /**
+    for (int i = isRef.nextSetBit(0); i>=0 && i<=top; i = isRef.nextSetBit(i + 1)) {
+      int objref = slots[i];
+      if (objref != MJIEnv.NULL) {
+        heap.markThreadRoot(objref, tid);
+      }
+    }
+    **/
+    for (int i = 0; i <= top; i++) {
+      if (isRef.get(i)) {
+        int objref = slots[i];
+        if (objref != MJIEnv.NULL) {
+          heap.markThreadRoot(objref, tid);
+        }
+      }
+    }
+  }
+
+  //--- debugging methods
+
+  public void printOperands (PrintStream pw){
+    pw.print("operands = [");
+    for (int i=stackBase; i<=top; i++){
+      if (i>0){
+        pw.print(',');
+      }
+      if (isOperandRef(i)){
+        pw.print('^');
+      }
+      pw.print(slots[i]);
+      Object a = getOperandAttr(top-i);
+      if (a != null){
+        pw.print(" {");
+        pw.print(a);
+        pw.print('}');
+      }
+    }
+    pw.println(']');
+  }
+
+  /**
+   * this includes locals and pc
+   */
+  public void printStackContent () {
+    PrintStream pw = System.out;
+
+    pw.print( "\tat ");
+    pw.print( mi.getFullName());
+
+    if (pc != null) {
+      pw.println( ":" + pc.getPosition());
+    } else {
+      pw.println();
+    }
+
+    pw.print("\t slots: ");
+    for (int i=0; i<=top; i++){
+      if (i == stackBase){
+        pw.println("\t      ----------- operand stack");
+      }
+
+      pw.print( "\t    [");
+      pw.print(i);
+      pw.print("] ");
+      if (isRef.get(i)) {
+        pw.print( "@");
+      }
+      pw.print( slots[i]);
+
+      if (attrs != null){
+        pw.print("  attr=");
+        pw.print(attrs[i]);
+      }
+
+      pw.println();
+    }
+  }
+
+  public void printStackTrace () {
+    System.out.println( getStackTraceInfo());
+  }
+
+  public void swap () {
+    int t = top-1;
+
+    int v = slots[top];
+    boolean isTopRef = isRef.get(top);
+
+    slots[top] = slots[t];
+    isRef.set( top, isRef.get(t));
+
+    slots[t] = v;
+    isRef.set( t, isTopRef);
+
+    if (attrs != null){
+      Object a = attrs[top];
+      attrs[top] = attrs[t];
+      attrs[t] = a;
+    }
+  }
+
+  protected void printContentsOn(PrintWriter pw){
+    pw.print("isFrozen=");
+    pw.print(isFrozen());
+    pw.print(",mi=");
+    pw.print( mi != null ? mi.getUniqueName() : "null");
+    pw.print(",top="); pw.print(top);
+    pw.print(",slots=[");
+
+    for (int i = 0; i <= top; i++) {
+      if (i == stackBase){
+        pw.print("||");
+      } else {
+        if (i != 0) {
+          pw.print(',');
+        }
+      }
+
+      if (isRef.get(i)){
+        pw.print('@');
+      }
+      pw.print(slots[i]);
+
+      if (attrs != null && attrs[i] != null) {
+        pw.print('(');
+        pw.print(attrs[i]);
+        pw.print(')');
+      }
+    }
+
+    pw.print("],pc=");
+    pw.print(pc != null ? pc.getPosition() : "null");
+
+    pw.print(']');
+
+  }
+  
+  // <2do> there are way too many different print/debug methods here
+  public void printSlots (PrintStream ps){
+    for (int i = 0; i <= top; i++) {
+      if (i == stackBase){
+        ps.print("||");
+      } else {
+        if (i != 0) {
+          ps.print(',');
+        }
+      }
+
+      if (isRef.get(i)){
+        PrintUtils.printReference(ps, slots[i]);
+      } else {
+        ps.print(slots[i]);
+      }
+    }    
+  }
+
+  public int getDepth(){
+    int depth = 0;
+    
+    for (StackFrame frame = prev; frame != null; frame = frame.prev){
+      depth++;
+    }
+    
+    return depth;
+  }
+  
+  protected int objectHashCode() {
+    return super.hashCode();
+  }
+
+  @Override
+  public String toString () {
+    StringWriter sw = new StringWriter(128);
+    PrintWriter pw = new PrintWriter(sw);
+
+    pw.print(getClass().getSimpleName() + '{');
+    //pw.print(Integer.toHexString(objectHashCode()));
+    printContentsOn(pw);
+    pw.print('}');
+
+    return sw.toString();
+  }
+
+  public float peekFloat() {
+    return Float.intBitsToFloat(slots[top]);
+  }
+
+  public float peekFloat (int offset){
+    return Float.intBitsToFloat(slots[top-offset]);    
+  }
+  
+  public double peekDouble() {
+    int i = top;
+    return Types.intsToDouble( slots[i], slots[i-1]);
+  }
+  
+  public double peekDouble (int offset){
+    int i = top-offset;
+    return Types.intsToDouble( slots[i], slots[i-1]);
+  }
+  
+  public long peekLong () {
+    int i = top;
+    return Types.intsToLong( slots[i], slots[i-1]);
+  }
+
+  public long peekLong (int offset) {
+    int i = top - offset;
+    return Types.intsToLong( slots[i], slots[i-1]);
+  }
+
+  public void pushLong (long v) {
+    push( (int) (v>>32));
+    push( (int) v);
+  }
+
+  public void pushDouble (double v) {
+    long l = Double.doubleToLongBits(v);
+    push( (int) (l>>32));
+    push( (int) l);
+  }
+
+  public void pushFloat (float v) {
+    push( Float.floatToIntBits(v));
+  }
+  
+  public double popDouble () {
+    int i = top;
+
+    int lo = slots[i--];
+    int hi = slots[i--];
+
+    if (attrs != null){
+      i = top;
+      attrs[i--] = null; // not really required
+      attrs[i--] = null; // that's where the attribute should be
+    }
+
+    top = i;
+    return Types.intsToDouble(lo, hi);
+  }
+
+  public long popLong () {
+    int i = top;
+
+    int lo = slots[i--];
+    int hi = slots[i--];
+
+    if (attrs != null){
+      i = top;
+      attrs[i--] = null; // not really required
+      attrs[i--] = null; // that's where the attribute should be
+    }
+
+    top = i;
+    return Types.intsToLong(lo, hi);
+  }
+
+  public int peek () {
+    return slots[top];
+  }
+
+  public int peek (int offset) {
+    return slots[top-offset];
+  }
+
+  public void removeArguments (MethodInfo mi) {
+    int i = mi.getArgumentsSize();
+
+    if (i != 0) {
+      pop(i);
+    }
+  }
+  
+  public void pop (int n) {
+    //assert (top >= stackBase) : "stack empty";
+
+    int t = top - n;
+
+    // <2do> get rid of this !
+    for (int i=top; i>t; i--) {
+      if (isRef.get(i) && (slots[i] != MJIEnv.NULL)) {
+        VM.getVM().getSystemState().activateGC();
+        break;
+      }
+    }
+
+    if (attrs != null){  // just to avoid memory leaks
+      for (int i=top; i>t; i--){
+        attrs[i] = null;
+      }
+    }
+
+    top = t;
+  }
+
+  public float popFloat() {    
+    int v = slots[top];
+
+    if (attrs != null){ // just to avoid memory leaks
+      attrs[top] = null;
+    }
+
+    top--;
+
+    return Float.intBitsToFloat(v);
+  }
+  
+  public int pop () {
+    //assert (top >= stackBase) : "stack empty";
+    
+    int v = slots[top];
+
+    // <2do> get rid of this
+    if (isRef.get(top)) {
+      if (v != MJIEnv.NULL) {
+        VM.getVM().getSystemState().activateGC();
+      }
+    }
+
+    if (attrs != null){ // just to avoid memory leaks
+      attrs[top] = null;
+    }
+
+    top--;
+
+    // note that we don't reset the operands or oRefs values, so that
+    // we can still access them after the insn doing the pop got executed
+    // (e.g. useful for listeners)
+
+    return v;
+  }
+  
+  public void pushLocal (int index) {
+    top++;
+    slots[top] = slots[index];
+    isRef.set(top, isRef.get(index));
+
+    if (attrs != null){
+      attrs[top] = attrs[index];
+    }
+  }
+
+  public void pushLongLocal (int index){
+    int t = top;
+
+    slots[++t] = slots[index];
+    isRef.clear(t);
+    slots[++t] = slots[index+1];
+    isRef.clear(t);
+
+    if (attrs != null){
+      attrs[t-1] = attrs[index];
+      attrs[t] = null;
+    }
+
+    top = t;
+  }
+
+  public void storeOperand (int index){
+    slots[index] = slots[top];
+    isRef.set( index, isRef.get(top));
+
+    if (attrs != null){
+      attrs[index] = attrs[top];
+      attrs[top] = null;
+    }
+
+    top--;
+  }
+
+  public void storeLongOperand (int index){
+    int t = top-1;
+    int i = index;
+
+    slots[i] = slots[t];
+    isRef.clear(i);
+
+    slots[++i] = slots[t+1];
+    isRef.clear(i);
+
+    if (attrs != null){
+      attrs[index] = attrs[t]; // its in the lower word
+      attrs[i] = null;
+
+      attrs[t] = null;
+      attrs[t+1] = null;
+    }
+
+    top -=2;
+  }
+
+  public void push (int v){
+    top++;
+    slots[top] = v;
+    isRef.clear(top);
+
+    //if (attrs != null){ // done on pop
+    //  attrs[top] = null;
+    //}
+  }
+
+  public void pushRef (int ref){
+    top++;
+    slots[top] = ref;
+    isRef.set(top);
+
+    //if (attrs != null){ // done on pop
+    //  attrs[top] = null;
+    //}
+
+    if (ref != MJIEnv.NULL) {
+      VM.getVM().getSystemState().activateGC();
+    }
+  }
+
+  public void push (int v, boolean ref) {
+    top++;
+    slots[top] = v;
+    isRef.set(top, ref);
+
+    //if (attrs != null){ // done on pop
+    //  attrs[top] = null;
+    //}
+
+    if (ref && (v != MJIEnv.NULL)) {
+      VM.getVM().getSystemState().activateGC();
+    }
+  }
+
+  // return the value of callerSlots variable given the name
+  public int getLocalVariableSlotIndex (String name) {
+    LocalVarInfo lv = mi.getLocalVar(name, pc.getPosition());
+
+    if (lv != null){
+      return lv.getSlotIndex();
+    }
+
+    return -1;
+  }
+
+  //--- abstract argument & return passing that can have VM dependend implementation
+  
+  public void setReferenceResult (int ref, Object attr){
+    pushRef(ref);
+    if (attr != null){
+      setOperandAttr(attr);
+    }
+  }
+  
+  public void setResult (int r, Object attr){
+    push(r);
+    if (attr != null){
+      setOperandAttr(attr);
+    }    
+  }
+  
+  public void setResult (long r, Object attr){
+    pushLong(r);
+    if (attr != null){
+      setLongOperandAttr(attr);
+    }    
+  }
+  
+  public int getResult(){
+    return pop();
+  }
+  
+  public long getLongResult(){
+    return popLong();
+  }
+
+  public int getReferenceResult () {
+    return pop();
+  }
+  
+  public Object getResultAttr () {
+    return getOperandAttr();
+  }
+
+  public Object getLongResultAttr () {
+    return getLongOperandAttr();
+  }
+  
+  public float getFloatResult(){
+    return Float.intBitsToFloat(getResult());    
+  }
+  public double getDoubleResult(){
+    return Double.longBitsToDouble(getLongResult());
+  }
+  public Object getFloatResultAttr(){
+    return getResultAttr();
+  }
+  public Object getDoubleResultAttr(){
+    return getLongResultAttr();
+  }
+
+  
+  //--- VM independent exception handler setup
+  
+  public void setExceptionReference (int exRef){
+    pushRef(exRef);
+  }
+  
+  public int getExceptionReference (){
+    return pop();
+  }
+  
+  public void setExceptionReferenceAttribute (Object attr){
+    setOperandAttr(attr);
+  }
+  
+  public Object getExceptionReferenceAttribute (){
+    return getOperandAttr();
+  }
+  
+  
+  // those set the local vars that are normally initialized from call arguments
+  public abstract void setArgumentLocal (int idx, int value, Object attr);
+  public abstract void setLongArgumentLocal (int idx, long value, Object attr);
+  public abstract void setReferenceArgumentLocal (int idx, int ref, Object attr);
+
+  public void setFloatArgumentLocal (int idx, float value, Object attr){
+    setArgumentLocal( idx, Float.floatToIntBits(value), attr);
+  }
+  public void setDoubleArgumentLocal (int idx, double value, Object attr){
+    setLongArgumentLocal( idx, Double.doubleToLongBits(value), attr);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/StateRestorer.java b/src/main/gov/nasa/jpf/vm/StateRestorer.java
new file mode 100644 (file)
index 0000000..3da2dc0
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+
+public interface StateRestorer<Saved> {
+  void attach(VM vm);
+  
+  /**
+   * get saved version of current KernelState. 
+   */
+  Saved getRestorableData();
+  
+  /**
+   * restore from saved KernelState.
+   */
+  void restore(Saved data);
+}
diff --git a/src/main/gov/nasa/jpf/vm/StateSerializer.java b/src/main/gov/nasa/jpf/vm/StateSerializer.java
new file mode 100644 (file)
index 0000000..df6f84c
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+
+public interface StateSerializer {
+  void attach(VM vm);
+  
+  /**
+   * serialize the current state and return as int[] 
+   */
+  int[] getStoringData();
+  
+  
+  void addNativeStateHolder (NativeStateHolder nsh);
+}
diff --git a/src/main/gov/nasa/jpf/vm/StateSet.java b/src/main/gov/nasa/jpf/vm/StateSet.java
new file mode 100644 (file)
index 0000000..cea5ea9
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+
+/**
+ * interface to encapsulate an ADT (conceptually a set) used to answer if
+ * a state has been seen already
+ */
+public interface StateSet {
+  
+  static final int UNKNOWN_ID = -1;
+
+  void attach(VM vm);
+  
+  /**
+   * check if a state is already in the set, and add it if not. Answer
+   * it's numeric id
+   */
+  int addCurrent ();
+  
+  /**
+   * how many states already in the set.  also, index of next newly-added state.
+   */
+  int size ();
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/StaticElementInfo.java b/src/main/gov/nasa/jpf/vm/StaticElementInfo.java
new file mode 100644 (file)
index 0000000..287dbaf
--- /dev/null
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.HashData;
+
+/**
+ * A specialized version of ElementInfo that is used for static fields. Each
+ * registered ClassInfo instance has its own StaticElementInfo instance
+ */
+public final class StaticElementInfo extends ElementInfo {
+
+  // this is kind of dangerous - make sure these flags are still unused in ElementInfo
+  static final int ATTR_COR_CHANGED    = 0x100000;
+  static final int ATTR_STATUS_CHANGED = 0x200000;
+
+  static final int ATTR_ANY_CHANGED = ElementInfo.ATTR_ANY_CHANGED | ATTR_COR_CHANGED | ATTR_STATUS_CHANGED;
+  
+  protected int classObjectRef = MJIEnv.NULL;
+  protected int status = ClassInfo.UNINITIALIZED;
+
+  
+  public StaticElementInfo () {
+  }
+
+  public StaticElementInfo (int id, ClassInfo ci, Fields f, Monitor m, ThreadInfo ti, ElementInfo eiClsObj) {
+    super(id, ci, f, m, ti);
+
+    // startup classes don't have a class object yet
+    if (eiClsObj != null) {
+      classObjectRef = eiClsObj.getObjectRef();
+    }
+
+    ti.getScheduler().initializeClassSharedness(ti, this);
+  }
+  
+  @Override
+  public ElementInfo getModifiableInstance() {
+    if (!isFrozen()) {
+      return this;
+    } else {
+      Statics statics = ci.getStatics();
+      return statics.getModifiable( objRef);
+    }
+  }
+  
+  @Override
+  public boolean isObject(){
+    return false;
+  }
+
+  @Override
+  public boolean isArray(){
+    return false;
+  }
+  
+  @Override
+  public boolean hasFinalizer() {
+    return false;
+  }
+  
+  @Override
+  protected int getNumberOfFieldsOrElements(){
+    // static fields can't be arrays, those are always heap objects
+    return ci.getNumberOfStaticFields();
+  }
+
+  @Override
+  public boolean hasChanged() {
+    return (attributes & ATTR_ANY_CHANGED) != 0;
+  }
+
+  @Override
+  public void markUnchanged() {
+    attributes &= ~ATTR_ANY_CHANGED;
+  }
+  
+  @Override
+  public void hash(HashData hd) {
+    super.hash(hd);
+    hd.add(classObjectRef);
+    hd.add(status);
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (super.equals(o) && o instanceof StaticElementInfo) {
+      StaticElementInfo other = (StaticElementInfo) o;
+
+      if (classObjectRef != other.classObjectRef) {
+        return false;
+      }
+      if (status != other.status) {
+        return false;
+      }
+
+      return true;
+
+    } else {
+      return false;
+    }
+  }
+
+
+  /**
+  public boolean isShared() {
+    // static fields are always thread global
+    return true;
+  }
+  **/
+
+  public int getStatus() {
+    return status;
+  }
+  
+  void setStatus (int newStatus) {
+    checkIsModifiable();
+    
+    if (status != newStatus) {
+      status = newStatus;
+      attributes |= ATTR_STATUS_CHANGED;
+    }
+  }
+
+  
+  @Override
+  protected FieldInfo getDeclaredFieldInfo (String clsBase, String fname) {
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsBase); // <2do> should use CL argument
+    FieldInfo fi = ci.getDeclaredStaticField(fname);
+    
+    if (fi == null) {
+      throw new JPFException("class " + ci.getName() +
+                                         " has no static field " + fname);
+    }
+    return fi;
+  }
+
+  @Override
+  public FieldInfo getFieldInfo (String fname) {
+    ClassInfo ci = getClassInfo();
+    return ci.getStaticField(fname);
+  }
+  
+  protected void checkFieldInfo (FieldInfo fi) {
+    if (getClassInfo() != fi.getClassInfo()) {
+      throw new JPFException("wrong static FieldInfo : " + fi.getName()
+          + " , no such field in class " + getClassInfo().getName());
+    }
+  }
+
+  @Override
+  public int getNumberOfFields () {
+    return getClassInfo().getNumberOfStaticFields();
+  }
+  
+  @Override
+  public FieldInfo getFieldInfo (int fieldIndex) {
+    return getClassInfo().getStaticField(fieldIndex);
+  }
+    
+  /**
+   * gc mark all objects stored in static reference fields
+   */
+  void markStaticRoot (Heap heap) {
+    ClassInfo ci = getClassInfo();
+    int n = ci.getNumberOfStaticFields();
+    
+    for (int i=0; i<n; i++) {
+      FieldInfo fi = ci.getStaticField(i);
+      if (fi.isReference()) {
+        int objref = fields.getIntValue(fi.getStorageOffset());
+        heap.markStaticRoot(objref);
+      }
+    }
+    
+    // don't forget the class object itself (which is not a field)
+    heap.markStaticRoot(classObjectRef);
+  }
+      
+  public int getClassObjectRef () {
+    return classObjectRef;
+  }
+  
+  public void setClassObjectRef(int r) {
+    checkIsModifiable();
+    
+    classObjectRef = r;
+    attributes |= ATTR_COR_CHANGED;
+  }
+
+  @Override
+  public String toString() {
+    return getClassInfo().getName(); // don't append objRef (useless and misleading for statics)
+  }
+
+  protected ElementInfo getReferencedElementInfo (FieldInfo fi){
+    assert fi.isReference();
+    Heap heap = VM.getVM().getHeap();
+    return heap.get(getIntField(fi));
+  }
+
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/Statics.java b/src/main/gov/nasa/jpf/vm/Statics.java
new file mode 100644 (file)
index 0000000..eb0af6b
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * abstraction for the container of StaticElementInfos, which manages static fields.
+ * Note that there is a Statics instance per ClassLoaderInfo, i.e. ids are only unique within each
+ * ClassLoader namespace.
+ * 
+ * This container is only growing - we don't remove/recycle classes yet
+ * 
+ * Since Statics instances have to be obtained from their respective ClassLoaderInfo, and
+ * ClassLoaderInfos are the ones that map type names to ClassInfos, Statics does not include
+ * methods for name lookup. This allows implementors to use efficient lookup based on the numerical
+ * ClassInfo id (which is only unique within this Statics / ClassLoader namespace)
+ */
+public interface Statics extends Iterable<ElementInfo> {
+
+  //--- construction
+  
+  /**
+   * startup classes are registered and initialized in two steps since object
+   * creation has to be deferred until we have at least Object and Class ClassInfos
+   */
+  StaticElementInfo newStartupClass (ClassInfo ci, ThreadInfo ti);
+  
+  /**
+   * this returns the search global id which is unique within this ClassLoader namespace.
+   * This id is also stored in the respective java.lang.Class object
+   */
+  StaticElementInfo newClass (ClassInfo ci, ThreadInfo ti, ElementInfo eiClsObj);
+  
+  
+  //--- accessors 
+  
+  /**
+   * get an ElementInfo that might or might not be suitable for modification. This should only
+   * be used when retrieving field values. The 'id' argument has to be the result of a previous 'newClass()' call
+   */
+  StaticElementInfo get (int id);
+  
+  /**
+   * get an ElementInfo that is guaranteed to be modifiable. This should be used when modifying
+   * field values.  The 'id' argument has to be the result of a previous 'newClass()' call
+   */
+  StaticElementInfo getModifiable (int id);
+
+  
+  //--- housekeeping
+  
+  Iterable<StaticElementInfo> liveStatics();
+  
+  void markRoots (Heap heap);
+  void cleanUpDanglingReferences (Heap heap);
+  
+  
+  //--- state management
+  
+  Memento<Statics> getMemento(MementoFactory factory);
+  Memento<Statics> getMemento();
+  
+  int size();
+}
diff --git a/src/main/gov/nasa/jpf/vm/StatisticFieldLockInfoFactory.java b/src/main/gov/nasa/jpf/vm/StatisticFieldLockInfoFactory.java
new file mode 100644 (file)
index 0000000..1f414bf
--- /dev/null
@@ -0,0 +1,377 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+
+import java.util.logging.Logger;
+
+/**
+ * a FieldLockInfo implementation with the following strategy:
+ * 
+ *   - at each check, store the intersection of the current threads lock set
+ *     with the previous field lock set
+ *   - if the access was checked less than CHECK_THRESHOLD times, report the
+ *     field as unprotected
+ *   - if the field lock set doesn't become empty after CHECK_THRESHOLD, report
+ *     the field as protected
+ *   - as an optimization, raise the check level above the threshold if we
+ *     have a good probability that a current lock is a protection lock for this
+ *     field
+ *   - continue to check even after reaching the threshold, so that we
+ *     can at least report a violated assumption
+ *     
+ *  NOTE there is a subtle problem: if we ever falsely assume lock protection
+ *  in a path that subsequently recycles the shared object (e.g. by leading
+ *  into an end state), we loose the assumption. If this is followed by
+ *  a backtrack and execution of a path that uses a conflicting assumption
+ *  (different or no lock), we will NOT detect potential races unless
+ *  vm.por.sync_detection.pindown is set (which has some runtime costs)
+ */
+
+public class StatisticFieldLockInfoFactory implements FieldLockInfoFactory {
+
+  static Logger log = JPF.getLogger("gov.nasa.jpf.vm.FieldLockInfo");
+  
+  /**
+   * the number of checks after which we decide if a non-empty lock set
+   * means this field is protected
+   */
+  static int CHECK_THRESHOLD = 5;
+  
+  /**
+   * do we want objects with final field lock assumptions to be pinned
+   * down (not garbage collected), to make sure we don't loose these
+   * assumptions and subsequently fail to detect an assumption violation
+   * after backtracking (see above)
+   */
+  static boolean PINDOWN = false;
+
+  /**
+   * do we look for strong locking candidates (i.e. assume protection
+   * if there is a lock related to the object).
+   * NOTE this can lead to undetected race conditions if the assumption
+   * subsequently fails
+   */
+  static boolean AGRESSIVE = false;
+  
+  
+  public StatisticFieldLockInfoFactory (Config conf) {
+    CHECK_THRESHOLD = conf.getInt("vm.por.sync_detection.threshold", CHECK_THRESHOLD);
+    PINDOWN = conf.getBoolean("vm.por.sync_detection.pindown", PINDOWN);
+    AGRESSIVE = conf.getBoolean("vm.por.sync_detection.agressive",AGRESSIVE);    
+  }
+  
+  @Override
+  public FieldLockInfo createFieldLockInfo (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    int[] currentLockRefs = ti.getLockedObjectReferences();
+    int nLocks = currentLockRefs.length;
+
+    if (nLocks == 0) {
+      return FieldLockInfo.empty; // not protected, never will
+      
+    } else {
+      
+      if (AGRESSIVE) {
+        int lockCandidateRef = strongProtectionCandidate(ei,fi,currentLockRefs);
+        if (lockCandidateRef != MJIEnv.NULL) {
+          // NOTE we raise the checklevel
+          return new SingleLockFli( ti, lockCandidateRef, CHECK_THRESHOLD);
+        }
+      }
+      
+      if (nLocks == 1) { // most common case
+        return new SingleLockFli( ti, currentLockRefs[0], 0);
+      
+      } else {
+        return new MultiLockFli( ti, fi, currentLockRefs);
+      }
+    }
+  }
+
+  /**
+   * check if the current thread lockset contains a lock with a high probability
+   * that it is a protection lock for this field. We need this to avoid
+   * state explosion due to the number of fields to check. Note that we don't
+   * necessarily have to answer/decide which one is the best match in case of
+   * several candidates (if we don't use this to reduce to StatisticFieldLockInfo1)
+   *
+   * For instance fields, this would be a lock with a distance <= 1.
+   * For static fields, the corresponding class object is a good candidate.
+   */
+  int strongProtectionCandidate (ElementInfo ei, FieldInfo fi, int[] currentLockRefs) {
+    int n = currentLockRefs.length;
+    Heap heap = VM.getVM().getHeap();
+
+    if (fi.isStatic()) { // static field, check for class object locking
+      ClassInfo ci = fi.getClassInfo();
+      int cref = ci.getClassObjectRef();
+
+      for (int i=0; i<n; i++) {
+        if (currentLockRefs[i] == cref) {
+          ElementInfo e = heap.get(cref);
+          log.info("sync-detection: " + ei + " assumed to be synced on class object: @" + e);
+          return cref;
+        }
+      }
+
+    } else { // instance field, use lock distance as a heuristic
+      int objRef = ei.getObjectRef();
+      
+      for (int i=0; i<n; i++) {
+        int eidx = currentLockRefs[i];
+
+        // case 1: synchronization on field owner itself
+        if (eidx == objRef) {
+          log.info("sync-detection: " + ei + " assumed to be synced on itself");
+          return objRef;
+        }
+
+        ElementInfo e = heap.get(eidx);
+        
+        // case 2: synchronization on sibling field that is a private lock object
+        if (ei.hasRefField(eidx)) {
+          log.info("sync-detection: "+ ei + " assumed to be synced on sibling: " + e);
+          return eidx;
+        }
+        
+        // case 3: synchronization on owner of object holding field (sync wrapper)
+        if (e.hasRefField(objRef)) {
+          log.info("sync-detection: " + ei + " assumed to be synced on object wrapper: " + e);
+          return eidx;
+        }
+      }
+    }
+
+    return -1;
+  }
+
+  
+  
+  //--- root for our concrete FieldLockInfo classes
+  static abstract class StatisticFieldLockInfo extends FieldLockInfo {
+    int checkLevel;
+
+    @Override
+       public boolean isProtected () {
+      return (checkLevel >= CHECK_THRESHOLD);
+    }
+
+    @Override
+       public boolean needsPindown (ElementInfo ei) {
+      return PINDOWN && (checkLevel >= CHECK_THRESHOLD);
+    }
+
+    protected void checkFailedLockAssumption(ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+      if (checkLevel >= CHECK_THRESHOLD) {
+        lockAssumptionFailed(ti,ei,fi);
+      }
+    }
+  }
+  
+  //--- Fli for a single lock
+  static class SingleLockFli extends StatisticFieldLockInfo {
+    int lockRef;
+    
+    SingleLockFli (ThreadInfo ti, int lockRef, int nChecks) {
+      tiLastCheck = ti;
+
+      this.lockRef = lockRef;
+      checkLevel = nChecks;
+    }
+
+    @Override
+       protected int[] getCandidateLockSet() {
+      int[] set = { lockRef };
+      return set;
+    }
+    
+
+    @Override
+       public FieldLockInfo checkProtection (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+      int[] currentLockRefs = ti.getLockedObjectReferences();
+      int nLocks = currentLockRefs.length;
+      
+      checkLevel++;
+      
+      for (int i=0; i<nLocks; i++) {
+        if (currentLockRefs[i] == lockRef) {
+          return this;
+        }
+      }
+      
+      checkFailedLockAssumption(ti, ei, fi);
+      return empty;
+    }
+
+    /**
+     * only called at the end of the gc on all live objects. The recycled ones
+     * are either already nulled in the heap, or are not marked as live
+     */
+    @Override
+       public FieldLockInfo cleanUp (Heap heap) {
+      ElementInfo ei = heap.get(lockRef);
+      if (!heap.isAlive(ei)) {
+        return FieldLockInfo.empty;
+      } else {
+        return this;
+      }
+    }
+
+    @Override
+       public String toString() {
+      return ("SingleLockFli {checkLevel="+checkLevel+",lock="+lockRef + '}');
+    }  
+  }
+  
+  
+  //--- StatisticFieldLockInfo for lock sets
+  static class MultiLockFli extends StatisticFieldLockInfo {
+
+    int[] lockRefSet;
+      
+    // this is only used once during prototype generation
+    public MultiLockFli (ThreadInfo ti, FieldInfo fi, int[] currentLockRefs) {
+      lockRefSet = currentLockRefs;
+    }
+    
+    @Override
+       protected int[] getCandidateLockSet() {
+      return lockRefSet;
+    }
+      
+
+    @Override
+       public FieldLockInfo checkProtection (ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+      int[] currentLockRefs = ti.getLockedObjectReferences();      
+      int nLocks = currentLockRefs.length;
+          
+      checkLevel++;
+
+      if (nLocks == 0) { // no current locks, so intersection is empty
+        checkFailedLockAssumption(ti, ei, fi);
+        return empty;
+
+      } else { // we had a lock set, and there currently is at least one lock held
+        int l =0;
+        int[] newLset = new int[lockRefSet.length];
+
+        for (int i=0; i<nLocks; i++) { // get the set intersection
+          int leidx = currentLockRefs[i];
+
+          for (int j=0; j<lockRefSet.length; j++) {
+            if (lockRefSet[j] == leidx) {
+              newLset[l++] = leidx;
+              break; // sets don't contain duplicates
+            }
+          }
+        }
+
+        if (l == 0) { // intersection empty
+          checkFailedLockAssumption(ti, ei, fi);
+          return empty;
+          
+        } else if (l == 1) { // only one candidate left 
+          return new SingleLockFli( ti, newLset[0], checkLevel);
+        
+        } else if (l < newLset.length) { // candidate set did shrink
+          lockRefSet = new int[l];
+          System.arraycopy(newLset, 0, lockRefSet, 0, l);
+          
+        } else {
+          // no change
+        }
+      }
+
+      tiLastCheck = ti;
+      return this;
+    }
+
+    /**
+     * only called at the end of the gc on all live objects. The recycled ones
+     * are either already nulled in the heap, or are not marked as live
+     */
+    @Override
+       public FieldLockInfo cleanUp (Heap heap) {
+      int[] newSet = null;
+      int l = 0;
+
+      if (lockRefSet != null) {
+        for (int i=0; i<lockRefSet.length; i++) {
+          ElementInfo ei = heap.get(lockRefSet[i]);
+
+          if (!heap.isAlive(ei)) { // we got a stale one, so we have to change us
+            if (newSet == null) { // first one, copy everything up to it
+              newSet = new int[lockRefSet.length-1];
+              if (i > 0) {
+                System.arraycopy(lockRefSet, 0, newSet, 0, i);
+                l = i;
+              }
+            }
+
+          } else {
+            if (newSet != null) { // we already had a dangling ref, now copy the live ones
+              newSet[l++] = lockRefSet[i];
+            }
+          }
+        }
+      }
+
+      if (l == 1) {
+          assert (newSet != null);
+          return new SingleLockFli(tiLastCheck, newSet[0], checkLevel);
+          
+      } else {
+        if (newSet != null) {
+          if (l == newSet.length) { // we just had one stale ref
+            lockRefSet = newSet;
+          } else { // several stales - make a new copy
+            if (l == 0) {
+              return empty;
+            } else {
+              lockRefSet = new int[l];
+              System.arraycopy(newSet, 0, lockRefSet, 0, l);
+            }
+          }
+        }
+        return this;
+      }
+    }
+
+    @Override
+       public String toString() {
+      StringBuilder sb = new StringBuilder();
+      sb.append("MultiLockFli {");
+      sb.append("checkLevel=");
+      sb.append(checkLevel);
+      sb.append(",lset=[");
+      if (lockRefSet != null) {
+        for (int i=0; i<lockRefSet.length; i++) {
+          if (i>0) {
+            sb.append(',');
+          }
+          sb.append(lockRefSet[i]);
+        }
+      }
+      sb.append("]}");
+
+      return sb.toString();
+    }
+  }  
+}
diff --git a/src/main/gov/nasa/jpf/vm/Step.java b/src/main/gov/nasa/jpf/vm/Step.java
new file mode 100644 (file)
index 0000000..4119d47
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.Source;
+
+import java.util.WeakHashMap;
+
+/**
+ * this corresponds to an executed instruction. Note that we can have a
+ * potentially huge number of Steps, hence we want to save objects here
+ * (e.g. Collection overhead)
+ */
+public class Step {
+
+  private static WeakHashMap<Step, String> s_comments = new WeakHashMap<Step, String>();  // Not every Step gets a comment.  So save memory and put comments in a global comment HashMap.  Make this a WeakHashMap so that old Step objects can be GCed.
+
+  private final Instruction insn;
+  Step next;
+
+  public Step (Instruction insn) {
+    if (insn == null)
+      throw new IllegalArgumentException("insn == null");
+
+    this.insn = insn;
+  }
+
+  public Step getNext() {
+    return next;
+  }
+
+  public Instruction getInstruction() {
+    return insn;
+  }
+
+  public void setComment (String s) {
+    s_comments.put(this, s);
+  }
+
+  public String getComment () {
+    return s_comments.get(this);
+  }
+
+  public String getLineString () {
+    MethodInfo mi = insn.getMethodInfo();
+    if (mi != null) {
+      Source source = Source.getSource(mi.getSourceFileName());
+      if (source != null) {
+        int line = mi.getLineNumber(insn);
+        if (line > 0) {
+          return source.getLine(line);
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public boolean sameSourceLocation (Step other){
+    
+    if (other != null){
+      MethodInfo mi = insn.getMethodInfo();
+      MethodInfo miOther = other.insn.getMethodInfo();
+      if (mi == miOther){
+        return (mi.getLineNumber(insn) == miOther.getLineNumber(other.insn));
+      }
+    }
+    
+    return false;
+  }
+  
+  public String getLocationString() {
+    MethodInfo mi = insn.getMethodInfo();
+    if (mi != null) {
+      return mi.getSourceFileName() + ':' + mi.getLineNumber(insn);
+    }
+
+    return "?:?";
+  }
+
+  @Override
+  public String toString() {
+    return getLocationString();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/Storable.java b/src/main/gov/nasa/jpf/vm/Storable.java
new file mode 100644 (file)
index 0000000..6bdcf01
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * generic Memento interface for things that need to be stored/backtracked
+ * @deprecated foo
+ */
+@Deprecated
+public interface Storable {
+
+  void backtrackTo (ArrayOffset storing);
+}
diff --git a/src/main/gov/nasa/jpf/vm/SuperTypeAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/SuperTypeAnnotationInfo.java
new file mode 100644 (file)
index 0000000..e243656
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for super types (superclass and interfaces)
+ */
+public class SuperTypeAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int typeIndex;
+  
+  public SuperTypeAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath, int superTypeIndex) {
+    super( base, targetType, typePath);
+    
+    this.typeIndex = typeIndex;
+  }
+  
+  public boolean isSuperClassAnnotation(){
+    return typeIndex == 65535;
+  }
+  
+  public int getSuperTypeIndex(){
+    return typeIndex;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/SyncPolicy.java b/src/main/gov/nasa/jpf/vm/SyncPolicy.java
new file mode 100644 (file)
index 0000000..8dfa176
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * a policy interface related to locking, blocking and thread lifetime
+ * events.
+ * 
+ * As opposed to field access and SharednessPolicy, most of these events
+ * are of mandatory nature, but we want to support varying order and degrees of
+ * precision with respect to scheduling candidates
+ * 
+ * NOTE - this interface is part of dividing the old SchedulerFactory (which was
+ * mostly concerned about scheduling order) into separate policy interfaces
+ * that allow control over
+ *  - CG type
+ *  - choice values
+ *  - choice order
+ * based on operation (instruction type, MJI methods), which might require
+ * additional, policy related state (e.g. threads competing for the same object).
+ * 
+ * Since the policies are interfaces kept at the same level, and are only
+ * accessed through ThreadInfo facades, they can be implemented by a single
+ * class/object
+ */
+public interface SyncPolicy {
+
+  //--- system scheduling points
+  public static String ROOT = "ROOT";
+  public static String POST_FINALIZE = "POST_FINALIZE";
+  
+  //--- thread sync and lifetime
+  public static String START = "START";
+  public static String BLOCK = "BLOCK";
+  public static String LOCK = "LOCK";
+  public static String RELEASE = "RELEASE";
+  public static String WAIT = "WAIT";
+  public static String JOIN = "JOIN";
+  public static String NOTIFY = "NOTIFY";
+  public static String NOTIFYALL = "NOTIFYALL";
+  public static String SLEEP = "SLEEP";
+  public static String YIELD = "YIELD";
+  public static String PRIORITY = "PRIORITY";
+  public static String INTERRUPT = "INTERRUPT";
+  public static String SUSPEND = "SUSPEND";
+  public static String RESUME = "RESUME";
+  public static String STOP = "STOP";
+  public static String PARK = "PARK";
+  public static String UNPARK = "UNPARK";
+  public static String BEGIN_ATOMIC = "BEGIN_ATOMIC";
+  public static String END_ATOMIC = "END_ATOMIC";
+  public static String RESCHEDULE = "SCHEDULE";
+  public static String TERMINATE = "TERMINATE";
+
+  
+  /**
+   * called once per application, after the VM is fully initialized 
+   */
+  void initializeSyncPolicy (VM vm, ApplicationContext appCtx);
+  
+  /**
+   * called during ThreadInfo initialization, before Thread.start()
+   */
+  void initializeThreadSync (ThreadInfo tiCurrent, ThreadInfo tiNew);
+  
+  /**
+   * set the very first CG, which is not optional
+   */
+  void setRootCG ();
+  
+  //--- locks
+  boolean setsBlockedThreadCG (ThreadInfo ti, ElementInfo ei);
+  boolean setsLockAcquisitionCG (ThreadInfo ti, ElementInfo ei);
+  boolean setsLockReleaseCG (ThreadInfo ti, ElementInfo ei, boolean didUnblock);
+
+  //--- thread termination
+  boolean setsTerminationCG (ThreadInfo ti);
+  
+  //--- java.lang.Object APIs
+  boolean setsWaitCG (ThreadInfo ti, long timeout);
+  boolean setsNotifyCG (ThreadInfo ti, boolean didNotify);
+  boolean setsNotifyAllCG (ThreadInfo ti, boolean didNotify);
+    
+  //--- the java.lang.Thread APIs
+  boolean setsStartCG (ThreadInfo tiCurrent, ThreadInfo tiStarted);
+  boolean setsYieldCG (ThreadInfo ti);
+  boolean setsPriorityCG (ThreadInfo ti);
+  boolean setsSleepCG (ThreadInfo ti, long millis, int nanos);
+  boolean setsSuspendCG (ThreadInfo tiCurrent, ThreadInfo tiSuspended);
+  boolean setsResumeCG (ThreadInfo tiCurrent, ThreadInfo tiResumed);
+  boolean setsJoinCG (ThreadInfo tiCurrent, ThreadInfo tiJoin, long timeout);
+  boolean setsStopCG (ThreadInfo tiCurrent, ThreadInfo tiStopped);
+  boolean setsInterruptCG (ThreadInfo tiCurrent, ThreadInfo tiInterrupted);
+  
+  //--- sun.misc.Unsafe
+  boolean setsParkCG (ThreadInfo ti, boolean isAbsTime, long timeout);
+  boolean setsUnparkCG (ThreadInfo tiCurrent, ThreadInfo tiUnparked);
+  
+  //--- gov.nasa.jpf.vm.Verify
+  boolean setsBeginAtomicCG (ThreadInfo ti);
+  boolean setsEndAtomicCG (ThreadInfo ti);
+  
+  //--- ThreadInfo reschedule request
+  boolean setsRescheduleCG (ThreadInfo ti, String reason);
+  
+  //--- FinalizerThread
+  boolean setsPostFinalizeCG (ThreadInfo tiFinalizer);
+}
diff --git a/src/main/gov/nasa/jpf/vm/SystemClassLoaderInfo.java b/src/main/gov/nasa/jpf/vm/SystemClassLoaderInfo.java
new file mode 100644 (file)
index 0000000..ce2866a
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.JPFLogger;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Represents the JPF system classloader which models the following hierarchy.
+ * 
+ *            ----------------
+ *            | Bootstrap CL |
+ *            ----------------
+ *                   |
+ *            ----------------
+ *            | Extension CL |
+ *            ----------------
+ *                   |
+ *           ------------------
+ *           | Application CL |
+ *           ------------------
+ *           
+ * Since in the standard VM user does not have any control over the built-in 
+ * classloaders hierarchy, in JPF, we model all three by an instance of 
+ * SystemClassLoader which is responsible to load classes from Java API, 
+ * standard extensions packages, and the local file system.     
+ */
+public abstract class SystemClassLoaderInfo extends ClassLoaderInfo {
+
+  static JPFLogger log = JPF.getLogger("class");
+  
+  // we need to keep track of this in case something needs the current SystemClassLoaderInfo before we have a main thread
+  static SystemClassLoaderInfo lastInstance;  
+  
+  // note that initialization requires these to be startup classes
+  protected ClassInfo classLoaderClassInfo;
+  protected ClassInfo objectClassInfo;
+  protected ClassInfo classClassInfo;
+  protected ClassInfo stringClassInfo;
+  protected ClassInfo weakRefClassInfo;
+  protected ClassInfo refClassInfo;
+  protected ClassInfo enumClassInfo;
+  protected ClassInfo threadClassInfo;
+  protected ClassInfo threadGroupClassInfo;
+  protected ClassInfo charArrayClassInfo;
+
+  protected int unCachedClasses = 10;
+  
+  /**
+   * list of configurable Attributors for ClassInfos, MethodInfos and FieldInfos
+   * that are consulted after creating the ClassInfo but before notifying classLoaded() listeners
+   */
+  protected List<Attributor> attributors;
+  
+  
+  public SystemClassLoaderInfo (VM vm, int appId){
+     super(vm);
+
+     lastInstance = this;
+
+    // this is a hack - for user ClassLoaderInfos, we compute the id from the corresponding
+    // objRef of the JPF ClassLoader object. For SystemClassLoaderInfos we can't do that because
+    // they are created before we can create JPF objects. However, this is safe if we know
+    // the provided id is never going to be the objRef of a future ClassLoader object, which is
+    // a safe bet since the first objects created are all system Class objects that are never going to
+    // be recycled.
+    this.id = computeId(appId);
+    
+    initializeSystemClassPath( vm, appId);
+    initializeAttributors( vm, appId);
+  }
+  
+  protected abstract void initializeSystemClassPath (VM vm, int appId);
+  
+  protected void initializeAttributors (VM vm, int appId){
+    attributors = new ArrayList<Attributor>();
+    
+    Config conf = vm.getConfig();
+    String key = conf.getIndexableKey("vm.attributors", appId);
+    if (key != null){
+      for (Attributor a : conf.getInstances(key, Attributor.class)){
+        attributors.add(a);
+      }
+    }
+  }
+
+  public void addAttributor (Attributor a){
+    attributors.add(a);
+  }
+  
+  /**
+   * to be called on each ClassInfo created in the realm of this SystemClassLoader
+   */
+  @Override
+  protected void setAttributes (ClassInfo ci){
+    for (Attributor a: attributors){
+      a.setAttributes(ci);
+    }
+  }
+  
+  //--- these can be used to build the app specific system CP
+  protected File[] getPathElements (Config conf, String keyBase, int appId) {
+    File[] pathElements = null;
+
+    // try appId indexed key first
+    String key = keyBase + '.' + appId;
+    if (conf.containsKey(key)) {
+      pathElements = conf.getPathArray(key);
+
+    } else { // fall back to keyBase
+      pathElements = conf.getPathArray(keyBase);
+    }
+
+    return pathElements;
+  }
+  
+  @Override
+  public SystemClassLoaderInfo getSystemClassLoader() {
+    return this;
+  }
+
+  
+  @Override
+  public ClassInfo getResolvedClassInfo (String clsName){
+    ClassInfo ci = super.getResolvedClassInfo(clsName);
+    
+    if (unCachedClasses > 0){
+      updateCachedClassInfos(ci);
+    }
+    
+    return ci;
+  }
+
+  @Override
+  public boolean isSystemClassLoader() {
+    return true;
+  }
+
+  static boolean checkClassName (String clsName) {
+    if ( !clsName.matches("[a-zA-Z_$][a-zA-Z_$0-9.]*")) {
+      return false;
+    }
+
+    // well, those two could be part of valid class names, but
+    // in all likeliness somebody specified a filename instead of
+    // a classname
+    if (clsName.endsWith(".java")) {
+      return false;
+    }
+    if (clsName.endsWith(".class")) {
+      return false;
+    }
+
+    return true;
+  }
+  
+
+  @Override
+  public ClassInfo loadClass(String cname) {
+    return getResolvedClassInfo(cname);
+  }
+
+  @Override
+  protected ClassInfo loadSystemClass (String typeName){
+    return new ClassInfo( typeName, this);
+  }
+
+  protected void setClassLoaderObject (ElementInfo ei){
+    objRef = ei.getObjectRef();
+    //id = computeId(objRef);
+    
+    // cross link
+    ei.setIntField(ID_FIELD, id);
+  }
+  
+
+  //-- ClassInfos cache management --
+
+  protected void updateCachedClassInfos (ClassInfo ci) {
+    String name = ci.name;
+
+    if ((objectClassInfo == null) && name.equals("java.lang.Object")) {
+      objectClassInfo = ci; unCachedClasses--;
+    } else if ((classClassInfo == null) && name.equals("java.lang.Class")) {
+      classClassInfo = ci; unCachedClasses--;
+    } else if ((classLoaderClassInfo == null) && name.equals("java.lang.ClassLoader")) {
+      classInfo = ci;
+      classLoaderClassInfo = ci;  unCachedClasses--;
+    } else if ((stringClassInfo == null) && name.equals("java.lang.String")) {
+      stringClassInfo = ci; unCachedClasses--;
+    } else if ((charArrayClassInfo == null) && name.equals("[C")) {
+      charArrayClassInfo = ci; unCachedClasses--;
+    } else if ((weakRefClassInfo == null) && name.equals("java.lang.ref.WeakReference")) {
+      weakRefClassInfo = ci; unCachedClasses--;
+    } else if ((refClassInfo == null) && name.equals("java.lang.ref.Reference")) {
+      refClassInfo = ci; unCachedClasses--;
+    } else if ((enumClassInfo == null) && name.equals("java.lang.Enum")) {
+      enumClassInfo = ci; unCachedClasses--;
+    } else if ((threadClassInfo == null) && name.equals("java.lang.Thread")) {
+      threadClassInfo = ci; unCachedClasses--;
+    } else if ((threadGroupClassInfo == null) && name.equals("java.lang.ThreadGroup")) {
+      threadGroupClassInfo = ci; unCachedClasses--;
+    }
+  }
+  
+  protected ClassInfo getObjectClassInfo() {
+    return objectClassInfo;
+  }
+
+  protected ClassInfo getClassClassInfo() {
+    return classClassInfo;
+  }
+
+  protected ClassInfo getClassLoaderClassInfo() {
+    return classLoaderClassInfo;
+  }
+
+  protected ClassInfo getStringClassInfo() {
+    return stringClassInfo;
+  }
+  
+  protected ClassInfo getCharArrayClassInfo() {
+    return charArrayClassInfo;
+  }
+
+  protected ClassInfo getEnumClassInfo() {
+    return enumClassInfo;
+  }
+
+  protected ClassInfo getThreadClassInfo() {
+    return threadClassInfo;
+  }
+
+  protected ClassInfo getThreadGroupClassInfo() {
+    return threadGroupClassInfo;
+  }
+
+  protected ClassInfo getReferenceClassInfo() {
+    return refClassInfo;
+  }
+
+  protected ClassInfo getWeakReferenceClassInfo() {
+    return weakRefClassInfo;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/SystemState.java b/src/main/gov/nasa/jpf/vm/SystemState.java
new file mode 100644 (file)
index 0000000..c60a4aa
--- /dev/null
@@ -0,0 +1,935 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.TypeSpecMatcher;
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+
+import java.io.PrintWriter;
+import java.util.LinkedHashMap;
+
+
+/**
+ * the class that encapsulates not only the current execution state of the VM
+ * (the KernelState), but also the part of it's history that is required
+ * by VM to backtrack, plus some potential annotations that can be used to
+ * control the search (i.e. forward/backtrack calls)
+ */
+public class SystemState {
+
+  /**
+   * instances of this class are used to store the SystemState parts which are
+   * subject to backtracking/state resetting. At some point, we might have
+   * stripped SystemState down enough to just store the SystemState itself
+   * (so far, we don't change it's identity, there is only one)
+   * the KernelState is still stored separately (which seems to be another
+   * anachronism)
+   *
+   * NOTE: this gets stored at the end of a transition, i.e. if we need a value
+   * to be restored to it's transition entry state (like atomicLevel), we have
+   * to do that explicitly. Alternatively we could create the Memento before
+   * we start to enter the step, but then we have to update the nextCg in the
+   * snapshot, since it's only set at the transition end (required for
+   * restore(), i.e.  HeuristicSearches)
+   * 
+   * NOTE: the plain Memento doesn't deep copy the CGs, which means it can
+   * only be used for depth first search, where the parent CG states are always
+   * current if we encounter an error. If general state restoration is
+   * required (where the parent CGs might have been changed at the time we
+   * restore), we have to use a RestorableMemento
+   * <2do> this separation is error prone and fragile. It depends on correct
+   * ChoiceGenerator deepCopy() implementations and a separate state acquisition
+   * for restorable states. Currently, the gate for this is VM.getRestorableState(),
+   * but this could be bypassed.
+   */
+  static class Memento {
+    ChoiceGenerator<?> curCg;  // the ChoiceGenerator for the current transition
+    ChoiceGenerator<?> nextCg;
+    int atomicLevel;
+    ChoicePoint trace;
+    ThreadInfo execThread;
+    int id;              // the state id
+    LinkedHashMap<Object,ClosedMemento> restorers;
+    
+    static protected ChoiceGenerator<?> cloneCG( ChoiceGenerator<?> cg){
+      if (cg != null){
+        try {
+          return cg.deepClone();
+        } catch (CloneNotSupportedException cnsx){
+          throw new JPFException("clone failed: " + cg);          
+        }
+      } else {
+        return null;
+      }
+    }
+    
+    Memento (SystemState ss) {
+      nextCg = ss.nextCg;      
+      curCg = ss.curCg;
+      
+      atomicLevel = ss.entryAtomicLevel; // store the value we had when we started the transition
+      id = ss.id;
+      execThread = ss.execThread;
+      
+      // we can just copy the reference since it is re-created in each transition
+      restorers = ss.restorers;
+    }
+
+    /**
+     * this one is used to restore to a state which will re-enter with the next choice
+     * of the same CG, i.e. nextCG is reset
+     */
+    void backtrack (SystemState ss) {
+      ss.nextCg = null; // this is important - the nextCG will be set by the next Transition      
+      ss.curCg = curCg;
+      
+      ss.atomicLevel = atomicLevel;
+      ss.id = id;
+      ss.execThread = execThread;
+      
+      if (restorers != null){
+        for (ClosedMemento r : restorers.values()){
+          r.restore();
+        }
+      }
+    }
+
+    void restore (SystemState ss) {
+      throw new JPFException("can't restore a SystemState.Memento that was created for backtracking");
+      
+      /**
+      ss.nextCg = nextCg;
+      ss.curCg = curCg;
+
+      ss.atomicLevel = atomicLevel;
+      ss.id = id;
+      ss.execThread = execThread;
+      **/
+    }
+  }
+  
+  /**
+   * a Memento that can be restored, not just backtracked to. Be aware this can
+   * be a lot more expensive since it has to deep copy CGs so that we have
+   * the state of the parent CGs restored properly
+   */
+  static class RestorableMemento extends Memento {
+    RestorableMemento (SystemState ss){
+      super(ss);
+      
+      nextCg = cloneCG(nextCg);
+      curCg = cloneCG( curCg);
+    }
+    
+    @Override
+       void backtrack (SystemState ss){
+      super.backtrack(ss);
+      ss.curCg = cloneCG(curCg);
+    }
+    
+    /**
+     * this one is used if we restore and then advance, i.e. it might change the CG on
+     * the next advance (if nextCg was set)
+     */
+    @Override
+       void restore (SystemState ss) {      
+      // if we don't clone them on restore, it means we can only restore this memento once
+      ss.nextCg = cloneCG(nextCg);
+      ss.curCg = cloneCG(curCg);
+
+      ss.atomicLevel = atomicLevel;
+      ss.id = id;
+      ss.execThread = execThread;
+      
+      if (restorers != null){
+        for (ClosedMemento r : restorers.values()){
+          r.restore();
+        }
+      }
+    }  
+  }
+
+  int id;                   /** the state id */
+
+  ChoiceGenerator<?> nextCg;   // the ChoiceGenerator for the next transition
+  ChoiceGenerator<?>  curCg;   // the ChoiceGenerator used in the current transition
+  ThreadInfo execThread;    // currently executing thread, reset by ThreadChoiceGenerators
+  
+  // on-demand list of optional restorers that run if we backtrack to this state
+  // this is reset before each transition
+  LinkedHashMap<Object,ClosedMemento> restorers;
+  
+
+  /** current execution state of the VM (stored separately by VM) */
+  public KernelState ks;
+
+  public Transition trail;      /** trace information */
+
+  //--- attributes that can be explicitly set for a state
+
+  boolean retainAttributes; // as long as this is set, we don't reset attributes
+
+  //--- ignored and isNewState are imperative
+  boolean isIgnored; // treat this as a matched state, i.e. backtrack
+  boolean isForced;  // treat this as a new state
+
+  //--- those are hints (e.g. for HeuristicSearches)
+  boolean isInteresting;
+  boolean isBoring;
+
+  boolean isBlockedInAtomicSection;
+
+  /** uncaught exception in current transition */
+  public UncaughtException uncaughtException;
+
+  /** set to true if garbage collection is necessary */
+  boolean GCNeeded = false;
+
+  // this is an optimization - long transitions can cause a lot of short-living
+  // garbage, which in turn can slow down the system considerably (heap size)
+  // by setting 'nAllocGCThreshold', we can do sync. on-the-fly gc when the
+  // number of new allocs within a single transition exceeds this value
+  int maxAllocGC;
+  int nAlloc;
+
+  /** NOTE: this has changed its meaning again. Now it once more is an
+   * optimization that can be used by applications calling Verify.begin/endAtomic(),
+   * but be aware of that it now reports a deadlock property violation in
+   * case of a blocking op inside an atomic section
+   * Data CGs however are now allowed to be inside atomic sections
+   *
+   * BEWARE - It is in the nature of atomic sections that they might loose paths that
+   * are relevant. This is esp. true for Thread.start() within AS if the starter
+   * runs to completion without further scheduling points (DiningPhil problem).
+   */
+  int atomicLevel;
+  int entryAtomicLevel;
+
+  /** do we want executed insns to be recorded */
+  boolean recordSteps;
+
+  /** CG types for which we extend transitions if the CG has only non-rescheduling single choices */
+  TypeSpecMatcher extendTransitions;
+  
+  /**
+   * Creates a new system state.
+   */
+  public SystemState (Config config, VM vm) {
+    ks = new KernelState(config);
+    id = StateSet.UNKNOWN_ID;
+
+    Class<?>[] argTypes = { Config.class, VM.class, SystemState.class };
+
+    // we can't yet initialize the trail until we have the start thread
+    
+    maxAllocGC = config.getInt("vm.max_alloc_gc", Integer.MAX_VALUE);
+    if (maxAllocGC <= 0){
+      maxAllocGC = Integer.MAX_VALUE;
+    }
+
+    extendTransitions = TypeSpecMatcher.create(config.getStringArray("vm.extend_transitions"));
+    // recordSteps is set later by VM, first we need a reporter (which requires the VM)
+  }
+
+  protected SystemState() {
+    // just for unit test mockups
+  }
+
+  public void setStartThread (ThreadInfo ti) {
+    execThread = ti;
+    trail = new Transition(nextCg, execThread);
+  }
+
+  public int getId () {
+    return id;
+  }
+
+  public void setId (int newId) {
+    id = newId;
+    trail.setStateId(newId);
+    
+    if (nextCg != null){
+      nextCg.setStateId(newId);
+    }
+  }
+
+  public void recordSteps (boolean cond) {
+    recordSteps = cond;
+  }
+
+  /**
+   * use those with extreme care, it overrides scheduling choices
+   */
+  public void incAtomic () {
+    atomicLevel++;
+  }
+
+  public void decAtomic () {
+    if (atomicLevel > 0) {
+      atomicLevel--;
+    }
+  }
+  public void clearAtomic() {
+    atomicLevel = 0;
+  }
+
+  public boolean isAtomic () {
+    return (atomicLevel > 0);
+  }
+
+  public boolean isBlockedInAtomicSection() {
+    return isBlockedInAtomicSection;
+  }
+
+  public void setBlockedInAtomicSection() {
+    isBlockedInAtomicSection = true;
+  }
+
+  public Transition getTrail() {
+    return trail;
+  }
+
+  public KernelState getKernelState() {
+    return ks;
+  }
+
+  public Heap getHeap() {
+    return ks.getHeap();
+  }
+
+  //--- these are the various choice generator retrievers
+
+  /**
+   * answer the ChoiceGenerator that is used in the current transition
+   */
+  public ChoiceGenerator<?> getChoiceGenerator () {
+    return curCg;
+  }
+
+  public ChoiceGenerator<?> getChoiceGenerator (String id) {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getPreviousChoiceGenerator()){
+      if (id.equals(cg.getId())){
+        return cg;
+      }
+    }
+
+    return null;
+  }
+  
+  /**
+   * return the whole stack of CGs of the current path
+   */
+  public ChoiceGenerator<?>[] getChoiceGenerators () {
+    if (curCg != null){
+      return curCg.getAll();
+    } else {
+      return null;
+    }
+  }
+
+  public ChoiceGenerator<?> getLastChoiceGeneratorInThread (ThreadInfo ti){
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getPreviousChoiceGenerator()){
+      if (cg.getThreadInfo() == ti){
+        return cg;
+      }
+    }
+    
+    return null;
+  }
+  
+  
+  public <T extends ChoiceGenerator<?>> T[] getChoiceGeneratorsOfType (Class<T> cgType) {
+    if (curCg != null){
+      return curCg.getAllOfType(cgType);
+    } else {
+      return null;
+    }
+  }
+
+
+  public <T extends ChoiceGenerator<?>> T getLastChoiceGeneratorOfType (Class<T> cgType) {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getPreviousChoiceGenerator()){
+      if (cgType.isAssignableFrom(cg.getClass())) {
+        return (T)cg;
+      }
+    }
+
+    return null;
+  }
+
+  public <T> ChoiceGenerator<T> getLastChoiceGeneratorOfChoiceType (String id, Class<T> choiceType){
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getPreviousChoiceGenerator()){
+      if ((id == null || id.equals(cg.getId())) && choiceType.isAssignableFrom(cg.getChoiceType())) {
+        return (ChoiceGenerator<T>)cg;
+      }
+    }
+
+    return null;    
+  }
+
+  
+  public <T extends ChoiceGenerator<?>> T getCurrentChoiceGeneratorOfType (Class<T> cgType) {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getCascadedParent()){
+      if (cgType.isAssignableFrom(cg.getClass())){
+        return (T)cg;
+      }
+    }
+
+    return null;
+  }
+
+  public <T extends ChoiceGenerator<?>> T getCurrentChoiceGenerator (String id, Class<T> cgType) {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getCascadedParent()){
+      if (id.equals(cg.getId()) && cgType.isAssignableFrom(cg.getClass())){
+        return (T)cg;
+      }
+    }
+
+    return null;
+  }
+  
+  public <T> ChoiceGenerator<T> getCurrentChoiceGeneratorForChoiceType (String id, Class<T> choiceType){
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getCascadedParent()){
+      if ((id == null || id.equals(cg.getId())) && choiceType.isAssignableFrom(cg.getChoiceType())){
+        return (ChoiceGenerator<T>)cg;
+      }
+    }
+
+    return null;    
+  }
+
+
+  public ChoiceGenerator<?> getCurrentChoiceGenerator (String id) {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getCascadedParent()){
+      if (id.equals(cg.getId())){
+        return cg;
+      }
+    }
+
+    return null;
+  }
+
+  public ChoiceGenerator<?> getCurrentChoiceGenerator (ChoiceGenerator<?> cgPrev) {
+    if (cgPrev == null){
+      return curCg;
+    } else {
+      return cgPrev.getCascadedParent();
+    }
+  }
+
+  /**
+   * this returns the most recently registered ThreadChoiceGenerator that is 
+   * also a scheduling point, or 'null' if there is none in the list of current CGs
+   */
+  public ThreadChoiceGenerator getCurrentSchedulingPoint () {
+    for (ChoiceGenerator<?> cg = curCg; cg != null; cg = cg.getCascadedParent()){
+      if (cg instanceof ThreadChoiceGenerator){
+        ThreadChoiceGenerator tcg = (ThreadChoiceGenerator)cg;
+        if (tcg.isSchedulingPoint()){
+          return tcg;
+        }
+      }
+    }
+
+    return null;
+  }
+
+  public ChoiceGenerator<?>[] getCurrentChoiceGenerators () {
+    return curCg.getCascade();
+  }
+
+  
+  public <T extends ChoiceGenerator<?>> T getInsnChoiceGeneratorOfType (Class<T> cgType, Instruction insn, ChoiceGenerator<?> cgPrev){
+    ChoiceGenerator<?> cg = cgPrev != null ? cgPrev.getPreviousChoiceGenerator() : curCg;
+
+    if (cg != null && cg.getInsn() == insn && cgType.isAssignableFrom(cg.getClass())){
+      return (T)cg;
+    }
+
+    return null;
+  }
+
+  public ChoiceGenerator<?> getNextChoiceGenerator () {
+    return nextCg;
+  }
+
+  /**
+   * set the ChoiceGenerator to be used in the next transition
+   * @return true if there is a nextCg set after registration and listener notification
+   */
+  public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg) {
+    if (isIgnored){
+      // if this transition is already marked as ignored, we are not allowed
+      // to set nextCg because 'isIgnored' results in a shortcut backtrack that
+      // is not handed back to the Search (its solely in VM forward)
+      return false;
+    }
+
+    if (cg != null){
+      // first, check if we have to randomize it. Note this might change the CG
+      // instance since some algorithmic CG types need to be transformed into
+      // explicit choice lists
+      if (ChoiceGeneratorBase.useRandomization()) {
+        cg = cg.randomize();
+      }
+
+      // set its context (thread and insn)
+      cg.setContext(execThread);
+
+      // do we already have a nextCG, which means this one is a cascaded CG
+      if (nextCg != null) {
+        cg.setPreviousChoiceGenerator(nextCg);
+        nextCg.setCascaded(); // note the last registered CG is NOT set cascaded
+
+      } else {
+        cg.setPreviousChoiceGenerator(curCg);
+      }
+
+      nextCg = cg;
+
+      execThread.getVM().notifyChoiceGeneratorRegistered(cg, execThread); // <2do> we need a better way to get the vm
+    }
+
+    // a choiceGeneratorRegistered listener might have removed this CG
+    return (nextCg != null);
+  }
+
+  public void setMandatoryNextChoiceGenerator (ChoiceGenerator<?> cg, String failMsg){
+    if (!setNextChoiceGenerator(cg)){
+      throw new JPFException(failMsg);
+    }
+  }
+
+  /**
+   * remove the current 'nextCg'
+   * Note this has to be called in a loop if all cascaded CGs have to be removed 
+   */
+  public void removeNextChoiceGenerator (){
+    if (nextCg != null){
+      nextCg = nextCg.getPreviousChoiceGenerator();
+    }
+  }
+
+  /**
+   * remove the whole chain of currently registered nextCGs
+   */
+  public void removeAllNextChoiceGenerators(){
+    while (nextCg != null){
+      nextCg = nextCg.getPreviousChoiceGenerator();
+    }
+  }
+
+  
+  public Object getBacktrackData () {
+    return new Memento(this);
+  }
+
+  public void backtrackTo (Object backtrackData) {
+    ((Memento) backtrackData).backtrack( this);
+  }
+
+  public Object getRestoreData(){
+    return new RestorableMemento(this);
+  }
+  
+  public void restoreTo (Object backtrackData) {
+    ((Memento) backtrackData).restore( this);
+  }
+
+  public void retainAttributes (boolean b){
+    retainAttributes = b;
+  }
+
+  public boolean getRetainAttributes() {
+    return retainAttributes;
+  }
+
+  /**
+   * this can be called anywhere from within a transition, to revert it and
+   * go on with the next choice. This is mostly used explicitly in the app
+   * via Verify.ignoreIf(..)
+   *
+   * calling setIgnored() also breaks the current transition, i.e. no further
+   * instructions are executed within this step
+   */
+  public void setIgnored (boolean b) {
+    isIgnored = b;
+
+    if (b){
+      isForced = false; // mutually exclusive
+    }
+  }
+
+  public boolean isIgnored () {
+    return isIgnored;
+  }
+
+  public void setForced (boolean b){
+    isForced = b;
+
+    if (b){
+      isIgnored = false; // mutually exclusive
+    }
+  }
+
+  public boolean isForced () {
+    return isForced;
+  }
+
+  public void setInteresting (boolean b) {
+    isInteresting = b;
+
+    if (b){
+      isBoring = false;
+    }
+  }
+
+  public boolean isInteresting () {
+    return isInteresting;
+  }
+
+  public void setBoring (boolean b) {
+    isBoring = b;
+
+    if (b){
+      isInteresting = false;
+    }
+  }
+
+  public boolean isBoring () {
+    return isBoring;
+  }
+
+  public boolean isInitState () {
+    return (id == StateSet.UNKNOWN_ID);
+  }
+
+  public int getThreadCount () {
+    return ks.threads.length();
+  }
+
+  public UncaughtException getUncaughtException () {
+    return uncaughtException;
+  }
+
+  public void activateGC () {
+    GCNeeded = true;
+  }
+
+  public boolean hasRestorer (Object key){
+    if (restorers != null){
+      return restorers.containsKey(key);
+    }
+    
+    return false;
+  }
+  
+  public ClosedMemento getRestorer( Object key){
+    if (restorers != null){
+      return restorers.get(key);
+    }
+    
+    return null;    
+  }
+  
+  /**
+   * call the provided restorer each time we get back to this state
+   * 
+   * @param key usually the object this restorer encapsulates
+   * @param restorer the ClosedMemento that restores the state of the object
+   * it encapsulates once we backtrack/restore this program state
+   * 
+   * Note that restorers are called in the order of registration, but in
+   * general it is not a good idea to depend on order since restorers can
+   * be set from different locations (listeners, peers, instructions)
+   */
+  public void putRestorer (Object key, ClosedMemento restorer){
+    if (restorers == null){
+      restorers = new LinkedHashMap<Object,ClosedMemento>();
+    }
+    
+    // we only support one restorer per target for now
+    restorers.put(key,restorer);
+  }
+  
+  public boolean gcIfNeeded () {
+    boolean needed = false;
+    if (GCNeeded) {
+      ks.gc();
+      GCNeeded = false;
+      needed = true;
+    }
+
+    nAlloc = 0;
+    return needed;
+  }
+
+  /**
+   * check if number of allocations since last GC exceed the maxAllocGC
+   * threshold, perform on-the-fly GC if yes. This is aimed at avoiding a lot
+   * of short-living garbage in long transitions, which slows down the heap
+   * exponentially
+   */
+  public void checkGC () {
+    if (nAlloc++ > maxAllocGC){
+      gcIfNeeded();
+    }
+  }
+
+
+  void dumpThreadCG (ThreadChoiceGenerator cg) {
+    PrintWriter pw = new PrintWriter(System.out, true);
+    cg.printOn(pw);
+    pw.flush();
+  }
+
+  /**
+   * reset the SystemState and initialize the next CG. This gets called
+   * *before* the restorer computes the KernelState snapshot, i.e. it is *not*
+   * allowed to change anything in the program state. The reason for splitting
+   * CG initialization from transition execution is to avoid KernelState storage
+   * in case the initialization does not produce a next choice and we have to
+   * backtrack.
+   *
+   * @see VM.forward()
+   * 
+   * @return 'true' if there is a next choice, i.e. a next transition to enter.
+   * 'false' if there is no next choice and the system has to backtrack
+   */
+  public boolean initializeNextTransition(VM vm) {
+
+    // set this before any choiceGeneratorSet or choiceGeneratorAdvanced
+    // notification (which can override it)
+    if (!retainAttributes){
+      isIgnored = false;
+      isForced = false;
+      isInteresting = false;
+      isBoring = false;
+    }
+
+    restorers = null;
+    
+    // 'nextCg' got set at the end of the previous transition (or a preceding
+    // choiceGeneratorSet() notification).
+    // Be aware of that 'nextCg' is only the *last* CG that was registered, i.e.
+    // there can be any number of CGs between the previous 'curCg' and 'nextCg'
+    // that were registered for the same insn.
+    while (nextCg != null) {
+      curCg = nextCg;
+      nextCg = null;
+
+      // these are hooks that can be used to do context specific CG initialization
+      curCg.setCurrent();
+      notifyChoiceGeneratorSet(vm, curCg);
+    }
+
+    assert (curCg != null) : "transition without choice generator";
+
+    return advanceCurCg(vm);
+  }
+
+  /**
+   * enter all instructions that constitute the next transition.
+   *
+   * Note this gets called *after* storing the KernelState, i.e. is allowed to
+   * modify thread states and fields
+   *
+   * @see VM.forward()
+   */
+  public void executeNextTransition (VM vm){
+     // do we have a thread context switch? (this sets execThread)
+    setExecThread( vm);
+
+    assert execThread.isRunnable() : "next transition thread not runnable: " + execThread.getStateDescription();
+
+    trail = new Transition(curCg, execThread);
+    entryAtomicLevel = atomicLevel; // store before we start to enter
+
+    execThread.executeTransition(this);    
+  }
+
+  /**
+   * check if we can extend the current transition without state storing/matching
+   * This is useful for non-cascaded single choice CGs that do not cause
+   * rescheduling. Such CGs are never backtracked to anyways (they are processed
+   * on their first advance).
+   * 
+   * NOTE: this is on top of CG type specific optimizations that are controlled
+   * by cg.break_single_choice (unset by default). If the respective CG creator
+   * is single choice aware it might not create / register a CG in the first
+   * place and we never get here. This is only called if somebody did create
+   * and register a CG
+   * 
+   * note also that we don't eliminate BreakGenerators since their only purpose
+   * in life is to explicitly cause transition breaks. We don't want to override
+   * the override here.
+   */
+  protected boolean extendTransition (){
+    ChoiceGenerator<?> ncg = nextCg;
+    if (ncg != null){
+      if (CheckExtendTransition.isMarked(ncg) ||
+              ((extendTransitions != null) && extendTransitions.matches(ncg.getClass()))){
+        if (ncg.getTotalNumberOfChoices() == 1 && !ncg.isCascaded()){
+          if (ncg instanceof ThreadChoiceGenerator){
+            if ((ncg instanceof BreakGenerator) || !((ThreadChoiceGenerator) ncg).contains(execThread)){
+              return false;
+            }
+          }
+
+          initializeNextTransition(execThread.getVM());
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  protected void setExecThread( VM vm){
+    ThreadChoiceGenerator tcg = getCurrentSchedulingPoint();
+    if (tcg != null){
+      ThreadInfo tiNext = tcg.getNextChoice();
+      if (tiNext != execThread) {
+        vm.notifyThreadScheduled(tiNext);
+        execThread = tiNext;
+      }
+    }
+
+    if (execThread.isTimeoutWaiting()) {
+      execThread.setTimedOut();
+    }
+  }
+
+
+  // the number of advanced choice generators in this step
+  protected int nAdvancedCGs;
+
+  protected void advance( VM vm, ChoiceGenerator<?> cg){
+    while (true) {
+      if (cg.hasMoreChoices()){
+        cg.advance();
+        isIgnored = false;
+        vm.notifyChoiceGeneratorAdvanced(cg);
+        
+        if (!isIgnored){
+          // this seems redundant, but the CG or the listener might actually skip choices,
+          // in which case we can't execute the next transition.
+          // NOTE - this causes backtracking
+          // <2do> it's debatable if we should treat this as a processed CG
+          if (cg.getNextChoice() != null){
+            nAdvancedCGs++;
+          }
+          break;
+        }
+        
+      } else {
+        vm.notifyChoiceGeneratorProcessed(cg);
+        break;
+      }
+    }
+  }
+
+  protected void advanceAllCascadedParents( VM vm, ChoiceGenerator<?> cg){
+    ChoiceGenerator<?> parent = cg.getCascadedParent();
+    if (parent != null){
+      advanceAllCascadedParents(vm, parent);
+    }
+    advance(vm, cg);
+  }
+
+  protected boolean advanceCascadedParent (VM vm, ChoiceGenerator<?> cg){
+    if (cg.hasMoreChoices()){
+      advance(vm,cg);
+      return true;
+
+    } else {
+      vm.notifyChoiceGeneratorProcessed(cg);
+
+      ChoiceGenerator<?> parent = cg.getCascadedParent();
+      if (parent != null){
+        if (advanceCascadedParent(vm,parent)){
+          cg.reset();
+          advance(vm,cg);
+          return true;
+        }
+      }
+      return false;
+    }
+  }
+
+  protected boolean advanceCurCg (VM vm){
+    nAdvancedCGs = 0;
+
+    ChoiceGenerator<?> cg = curCg;
+    ChoiceGenerator<?> parent = cg.getCascadedParent();
+
+    if (cg.hasMoreChoices()){
+      // check if this is the first time, for which we also have to advance our parents
+      if (parent != null && parent.getProcessedNumberOfChoices() == 0){
+        advanceAllCascadedParents(vm,parent);
+      }
+      advance(vm, cg);
+
+    } else { // this one is done, but how about our parents
+      vm.notifyChoiceGeneratorProcessed(cg);
+
+      if (parent != null){
+        if (advanceCascadedParent(vm,parent)){
+          cg.reset();
+          advance(vm,cg);
+        }
+      }
+    }
+
+    return (nAdvancedCGs > 0);
+  }
+
+
+
+  protected void notifyChoiceGeneratorSet (VM vm, ChoiceGenerator<?> cg){
+    ChoiceGenerator<?> parent = cg.getCascadedParent();
+    if (parent != null) {
+      notifyChoiceGeneratorSet(vm, parent);
+    }
+    vm.notifyChoiceGeneratorSet(cg); // notify top down
+  }
+
+
+  // this is called on every executeInstruction from the running thread
+  public boolean breakTransition () {
+    return ((nextCg != null) || isIgnored);
+  }
+
+  void recordExecutionStep (Instruction pc) {
+    // this can require a lot of memory, so we should only store
+    // executed insns if we have to
+    if (recordSteps) {
+      Step step = new Step(pc);
+      trail.addStep( step);
+    } else {
+      trail.incStepCount();
+    }
+  }
+
+  // the three primitive ops used from within VM.forward()
+
+
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/SystemTime.java b/src/main/gov/nasa/jpf/vm/SystemTime.java
new file mode 100644 (file)
index 0000000..6ae1074
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * simple delegating TimeModel implementation that just returns System time.
+ * While elapsed time is not path sensitive, it is at least strictly increasing
+ * along any given path
+ */
+public class SystemTime implements TimeModel {
+
+  public SystemTime(VM vm, Config conf){
+    // we don't need these, but it speeds up VM initialization
+  }
+  
+  @Override
+  public long currentTimeMillis() {
+    return System.currentTimeMillis();
+  }
+
+  @Override
+  public long nanoTime() {
+    return System.nanoTime();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThreadChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/ThreadChoiceGenerator.java
new file mode 100644 (file)
index 0000000..36e883a
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.PrintWriter;
+
+public interface ThreadChoiceGenerator extends ChoiceGenerator<ThreadInfo> {
+  
+  void printOn (PrintWriter pw);
+  
+  boolean contains (ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThreadData.java b/src/main/gov/nasa/jpf/vm/ThreadData.java
new file mode 100644 (file)
index 0000000..a00c031
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.HashData;
+
+
+/**
+ * this is the mutable Thread data we have to keep track of for storing/restoring states
+ */
+public class ThreadData {
+  /**
+   * Current state of the thread.
+   */
+  ThreadInfo.State state;
+
+  /** the scheduler priority of this thread */
+  int priority;
+
+  /**
+   * the name of this thread
+   * (only temporarily unset, between NEW and INVOKESPECIAL)
+   */
+  String name = "?";
+
+  /** is this a daemon thread */
+  boolean isDaemon;
+
+  /**
+   * The lock counter when the object got into a wait. This value
+   * is used to restore the object lock count once this thread
+   * gets notified
+   */
+  int lockCount;
+
+  /**
+   * The suspend count of the thread. See ThreadInfo.suspend() for a discussion
+   * of how faithful this is (it is an over approximation)
+   */
+  int suspendCount;
+
+
+  @Override
+  public ThreadData clone () {
+    ThreadData t = new ThreadData();
+
+    t.state = state;
+    t.lockCount = lockCount;
+    t.suspendCount = suspendCount;
+
+    t.priority = priority;
+    t.name = name;
+    t.isDaemon = isDaemon;
+
+    return t;
+  }
+
+  @Override
+  public boolean equals (Object o) {
+    if ((o == null) || !(o instanceof ThreadData)) {
+      return false;
+    }
+
+    ThreadData t = (ThreadData) o;
+
+    return ((state == t.state) && 
+            (priority == t.priority) &&
+            (isDaemon == t.isDaemon) && 
+            (lockCount == t.lockCount) &&
+            (suspendCount == t.suspendCount) && 
+            (name.equals(t.name)));
+  }
+
+  public void hash (HashData hd) {
+    hd.add(state);
+    hd.add(lockCount);
+    hd.add(suspendCount);
+    hd.add(priority);
+    hd.add(isDaemon);
+    hd.add(name);
+  }
+
+  @Override
+  public int hashCode () {
+    HashData hd = new HashData();
+
+    hash(hd);
+
+    return hd.getValue();
+  }
+
+  @Override
+  public String toString () {
+    return ("ThreadData{" + getFieldValues() + '}');
+  }
+
+  public String getFieldValues () {
+    StringBuilder sb = new StringBuilder("name:");
+
+    sb.append(name);
+    sb.append(",status:");
+    sb.append(state.name());
+    sb.append(",priority:");
+    sb.append(priority);
+    sb.append(",isDaemon:");
+    sb.append(isDaemon);
+    sb.append(",lockCount:");
+    sb.append(lockCount);
+    sb.append(",suspendCount:");
+    sb.append(suspendCount);
+
+    return sb.toString();
+  }
+
+  public ThreadInfo.State getState() { return state; }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThreadInfo.java b/src/main/gov/nasa/jpf/vm/ThreadInfo.java
new file mode 100644 (file)
index 0000000..4d2e95a
--- /dev/null
@@ -0,0 +1,3452 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.vm.bytecode.ReturnInstruction;
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.SystemAttribute;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.jvm.bytecode.INVOKESTATIC;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.Predicate;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+
+import java.io.PrintWriter;
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.logging.Level;
+
+
+/**
+ * Represents a thread. It contains the state of the thread, static
+ * information about the thread, and the stack frames.
+ * Race detection and lock order also store some information
+ * in this data structure.
+ *
+ * Note that we preserve identities according to their associated java.lang.Thread object
+ * (oref). This esp. means along the same path, a ThreadInfo reference
+ * is kept invariant
+ *
+ * <2do> remove EXECUTENATIVE,INVOKESTATIC .bytecode dependencies
+ */
+public class ThreadInfo extends InfoObject
+     implements Iterable<StackFrame>, Comparable<ThreadInfo>, Restorable<ThreadInfo> {
+
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.vm.ThreadInfo");
+  static int[] emptyLockRefs = new int[0];
+
+  //--- our internal thread states
+  public enum State {
+    NEW,  // means created but not yet started
+    RUNNING,
+    BLOCKED,  // waiting to acquire a lock
+    UNBLOCKED,  // was BLOCKED but can acquire the lock now
+    WAITING,  // waiting to be notified
+    TIMEOUT_WAITING,
+    NOTIFIED,  // was WAITING and got notified, but is still blocked
+    INTERRUPTED,  // was WAITING and got interrupted
+    TIMEDOUT,  // was TIMEOUT_WAITING and timed out
+    TERMINATED,
+    SLEEPING
+  };
+
+  static final int[] emptyRefArray = new int[0];
+  static final String MAIN_NAME = "main";
+  
+  
+  static ThreadInfo currentThread;
+
+  protected class StackIterator implements Iterator<StackFrame> {
+    StackFrame frame = top;
+
+    @Override
+       public boolean hasNext() {
+      return frame != null;
+    }
+
+    @Override
+       public StackFrame next() {
+      if (frame != null){
+        StackFrame ret = frame;
+        frame = frame.getPrevious();
+        return ret;
+
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("can't remove StackFrames");
+    }
+  }
+
+  protected class InvokedStackIterator extends StackIterator implements Iterator<StackFrame> {
+    InvokedStackIterator() {
+      frame = getLastInvokedStackFrame();
+    }
+
+    @Override
+       public StackFrame next() {
+      if (frame != null){
+        StackFrame ret = frame;
+        frame = null;
+        for (StackFrame f=ret.getPrevious(); f != null; f = f.getPrevious()){
+          if (!f.isDirectCallFrame()){
+            frame = f;
+            break;
+          }
+        }
+        return ret;
+
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+  }
+  
+  
+  //--- instance fields
+      
+  // transient, not state stored. This is reset when backtracking or starting a new transition
+  protected ExceptionInfo pendingException;
+
+  // state managed data that is copy-on-first-write
+  protected ThreadData threadData;
+
+  
+  //<2do> Hmm, why are these not in ThreadData?
+  // the top stack frame
+  protected StackFrame top = null;
+
+  // the current stack depth (number of frames)
+  protected int stackDepth;
+
+  
+  // something that tells the ThreadList how to look this up efficiently (e.g. index)
+  // note - this is for internal purposes only, there is no public accessor
+  // (we don't want to expose/hardwire ThreadList implementation)
+  // note also that the ThreadList is allowed to move this thread around, in which
+  // case update is the ThreadLists responsibility
+  protected int tlIdx;
+
+  
+  //--- the invariants
+
+  // we need this mostly for getting our SystemClassLoader
+  protected ApplicationContext appCtx;
+  
+  // search global id, which is the basis for canonical order of threads
+  protected int id;
+  
+  protected int objRef; // the java.lang.Thread object reference
+  protected ClassInfo ci; // the classinfo associated with the thread object
+  protected int targetRef; // the associated java.lang.Runnable
+  
+  // which attributes are stored/restored
+  static final int   ATTR_STORE_MASK = 0x0000ffff;
+
+  //--- the transient (un(re)stored) attributes
+  static final int ATTR_DATA_CHANGED       = 0x10000;
+  static final int ATTR_STACK_CHANGED      = 0x20000;
+  
+ // allow CG in next re-exec
+  static final int ATTR_ENABLE_EMPTY_TRANSITION = 0x4000;
+  
+  // don't call insn.execute()
+  static final int ATTR_SKIP_INSN_EXEC      = 0x100000;
+  
+  // don't store insn execution as transition step
+  static final int ATTR_SKIP_INSN_LOG       = 0x200000;
+  
+  
+  static final int ATTR_ATTRIBUTE_CHANGED  = 0x80000000;
+
+  //--- state stored/restored attributes  
+  // this is a typical "orthogonal" thread state we have to remember, but
+  // that should not affect any locking, blocking, notifying or such
+  static final int ATTR_STOPPED = 0x0001;
+
+  //--- change sets
+  static final int ATTR_ANY_CHANGED = (ATTR_DATA_CHANGED | ATTR_STACK_CHANGED | ATTR_ATTRIBUTE_CHANGED);
+  static final int ATTR_SET_STOPPED = (ATTR_STOPPED | ATTR_ATTRIBUTE_CHANGED);
+
+  protected int attributes;
+
+  
+  /** counter for executed instructions along current transition */
+  protected int executedInstructions;
+
+  /** a listener or peer request for throwing an exception into the SUT, to be processed in executeInstruction */
+  protected SUTExceptionRequest pendingSUTExceptionRequest;
+  
+  /** the last returned direct call frame */
+  protected DirectCallStackFrame returnedDirectCall;
+
+  /** the next insn to enter (null prior to execution) */
+  protected Instruction nextPc;
+
+  /**
+   * not so nice we cross-couple the NativePeers with ThreadInfo,
+   * but to carry on with the JNI analogy, a MJIEnv is clearly
+   * owned by a thread (even though we don't have much ThreadInfo
+   * state dependency in here (yet), hence the simplistic init)
+   */
+  MJIEnv env;
+
+  /**
+   * the VM we are running on. Bad backlink, but then again, we can't really
+   * test a ThreadInfo outside a VM context anyways.
+   * <2do> If we keep 'list' as a field, 'vm' might be moved over there
+   * (all threads in the list share the same VM)
+   */
+  VM vm;
+
+  /**
+   * !! this is a volatile object, i.e. it has to be re-computed explicitly
+   * !! after each backtrack (we don't want to duplicate state storage)
+   * list of lock objects currently held by this thread.
+   * unfortunately, we cannot organize this as a stack, since it might get
+   * restored (from the heap) in random order
+   */
+  int[] lockedObjectReferences;
+
+  /**
+   * !! this is also volatile -> has to be reset after backtrack
+   * the reference of the object if this thread is blocked or waiting for
+   */
+  int lockRef = MJIEnv.NULL;
+
+  Memento<ThreadInfo> cachedMemento; // cache for unchanged ThreadInfos
+
+
+  static class TiMemento implements Memento<ThreadInfo> {
+    // note that we don't have to store the invariants (id, oref, runnableRef, ciException)
+    ThreadInfo ti;
+
+    ThreadData threadData;
+    StackFrame top;
+    int stackDepth;
+    int attributes;
+
+    TiMemento (ThreadInfo ti){
+      this.ti = ti;
+      
+      threadData = ti.threadData;  // no need to clone - it's copy on first write
+      top = ti.top; // likewise
+      stackDepth = ti.stackDepth; // we just copy this for efficiency reasons
+      attributes = (ti.attributes & ATTR_STORE_MASK);
+
+      ti.freeze();
+      ti.markUnchanged();
+    }
+
+    @Override
+       public ThreadInfo restore(ThreadInfo ignored) {
+      ti.resetVolatiles();
+
+      ti.threadData = threadData;
+      ti.top = top;
+      ti.stackDepth = stackDepth;
+      ti.attributes = attributes;
+
+      ti.markUnchanged();
+
+      return ti;
+    }
+  }
+
+
+  // the following parameters are configurable. Would be nice if we could keep
+  // them on a per-instance basis, but there are a few locations
+  // (e.g. ThreadList) where we loose the init context, and it's questionable
+  // if we want to change this at runtime, or POR might make sense on a per-thread
+  // basis
+
+  /** do we halt on each throw, i.e. don't look for an exception handler?
+   * Useful to find empty handler blocks, or misusd exceptionHandlers
+   */
+  static StringSetMatcher haltOnThrow;
+
+  /**
+   * do we delegate to Thread.UncaughtExceptionHandlers (in case there is any
+   * other than the standard ThreadGroup)
+   */
+  static boolean ignoreUncaughtHandlers;
+  
+  /**
+   * do we go on if we return from an UncaughtExceptionHandler, or do we still
+   * regard this as a NoUncaughtExceptionProperty violation
+   */
+  static boolean passUncaughtHandler;
+
+  
+  /**
+   * break the current transition after this number of instructions.
+   * This is a safeguard against paths that won't break because potentially
+   * shared fields are not yet accessed by a second thread (existence of such
+   * paths is the downside of our access tracking). Note that we only break on
+   * backjumps once this count gets exceeded, to give state matching a better
+   * chance and avoid interference with the IdleLoop listener
+   */
+  static int maxTransitionLength;
+
+  /**
+   * reset ThreadInfo statics (e.g. to reinitialize JPF) 
+   */
+  static boolean init (Config config) {
+    currentThread = null;
+    
+    globalTids = new HashMap<Integer, Integer>();
+
+    String[] haltOnThrowSpecs = config.getStringArray("vm.halt_on_throw");
+    if (haltOnThrowSpecs != null){
+      haltOnThrow = new StringSetMatcher(haltOnThrowSpecs);
+    }
+    
+    ignoreUncaughtHandlers = config.getBoolean( "vm.ignore_uncaught_handler", true);
+    passUncaughtHandler = config.getBoolean( "vm.pass_uncaught_handler", true);
+
+    maxTransitionLength = config.getInt("vm.max_transition_length", 5000);
+
+    return true;
+  }
+    
+  //--- factory methods
+  // <2do> this is going to be a configurable factory method  
+  
+  /*
+   * search global cache for dense ThreadInfo ids. We could just use oref since those are
+   * guaranteed to be global, but not dense. The ids are search global, i.e. there is no
+   * need to store/restore, but it needs to be (re)set during init()  
+   */
+  static Map<Integer, Integer> globalTids;  // initialized by init
+  
+  
+  protected int computeId (int objRef) {
+    Integer id = globalTids.get(objRef);
+    
+    if(id == null) {
+      id = globalTids.size();
+      addId(objRef, id);
+    }
+
+    return id;
+  }
+
+  static void addId(int objRef, int id) {
+    globalTids.put(objRef, id);
+  }
+  
+  /**
+   * mainThread ctor called by the VM. Note we don't have a thread object yet (hen-and-egg problem
+   * since we can't allocate objects without a ThreadInfo)
+   */
+  protected ThreadInfo (VM vm, int id, ApplicationContext appCtx) {
+    this.id = id;
+    this.appCtx = appCtx;
+    
+    init(vm);
+    // we don't have the group yet, no Runnable or parent, and the name is fixed
+    // the thread is also not yet in the ThreadList
+    
+    ci = appCtx.getSystemClassLoader().getThreadClassInfo();
+    targetRef = MJIEnv.NULL;
+    threadData.name = MAIN_NAME;
+  }
+
+  /**
+   * the ctor for all explicitly (bytecode) created threads. At this point, there is at least
+   * a mainThread and we have a corresponding java.lang.Thread object
+   */
+  protected ThreadInfo (VM vm, int objRef, int groupRef, int runnableRef, int nameRef, ThreadInfo parent) {
+    id = computeId(objRef);
+    this.appCtx = parent.getApplicationContext();
+    
+    init(vm); // this is only partial, we still have to link/set the references
+    
+    ElementInfo ei = vm.getModifiableElementInfo(objRef);  
+    ei.setExposed(parent, null);        // all explicitly creatd threads are per se exposed
+    
+    this.ci = ei.getClassInfo();    
+    this.objRef = objRef;
+    this.targetRef = runnableRef;
+   
+    threadData.name = vm.getElementInfo(nameRef).asString();
+    
+    vm.getScheduler().initializeThreadSync(parent, this);
+    
+    // note the thread is not yet in the ThreadList, we have to register from the caller
+  }
+  
+  protected void init(VM vm){
+    // 'gid' is set by the factory method
+    // we can't set the 'id' field of the corresponding java.lang.Thread object until we have one
+    
+    this.vm = vm;
+
+    threadData = new ThreadData();
+    threadData.state = State.NEW;
+    threadData.priority = Thread.NORM_PRIORITY;
+    threadData.isDaemon = false;
+    threadData.lockCount = 0;
+    threadData.suspendCount = 0;
+    // threadData.name not yet known
+    
+    // 'priority', 'name', 'target' and 'group' are not taken
+    // from the object, but set within the java.lang.Thread ctors
+
+    top = null;
+    stackDepth = 0;
+
+    lockedObjectReferences = emptyLockRefs;
+
+    markUnchanged();
+    attributes |= ATTR_DATA_CHANGED; 
+    env = new MJIEnv(this);
+  }
+  
+  @Override
+  public Memento<ThreadInfo> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+
+  public Memento<ThreadInfo> getMemento(){
+    return new TiMemento(this);
+  }
+  
+  void freeze() {
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
+      frame.freeze();
+    }
+  }
+
+  //--- cached mementos are only supposed to be accessed from the Restorer
+
+  public Memento<ThreadInfo> getCachedMemento(){
+    return cachedMemento;
+  }
+
+  public void setCachedMemento(Memento<ThreadInfo> memento){
+    cachedMemento = memento;
+  }
+
+  public static ThreadInfo getCurrentThread() {
+    return currentThread;
+  }
+
+  public boolean isExecutingAtomically () {
+    return vm.getSystemState().isAtomic();
+  }
+
+  public boolean holdsLock (ElementInfo ei) {
+    int objRef = ei.getObjectRef();
+    
+    for (int i=0; i<lockedObjectReferences.length; i++) {
+      if (lockedObjectReferences[i] == objRef) {
+        return true;
+      }
+    }
+    
+    return false;
+  }
+
+  public VM getVM () {
+    return vm;
+  }
+
+  /**
+   * is *this* transition allowed to be empty (i.e. allowed to set a CG
+   * during re-execution of the current insn)
+   * reset before each instruction.execute()
+   */
+  public boolean isEmptyTransitionEnabled (){
+    return (attributes & ATTR_ENABLE_EMPTY_TRANSITION) != 0;
+  }
+  
+  public void enableEmptyTransition (){
+    attributes |= ATTR_ENABLE_EMPTY_TRANSITION;
+  }
+  
+  public void resetEmptyTransition(){
+    attributes &= ~ATTR_ENABLE_EMPTY_TRANSITION;
+  }
+  
+  /**
+   * answers if is this the first instruction within the current transition.
+   * This is mostly used to tell the top- from the bottom-half of a native method
+   * or Instruction.enter(), so that only one (the top half) registers the CG
+   * (top = register CG and reschedule insn, bottom = re-enter insn and process choice
+   * at beginning of new transition)
+   * 
+   * This can be used in both pre- and post-exec notification listeners, 
+   * the executedInstructions number is incremented before notifying
+   * instructionExecuted()
+   * 
+   * this method depends on the sequence of operations in ThreadInfo.executeInstruction,
+   * which is:
+   *   nextPc = null
+   *   notify executeInstruction
+   *   nextPc = insn.execute
+   *   increment executedInstructions
+   *   notify instructionExecuted
+   */
+  public boolean isFirstStepInsn() {
+    int nInsn = executedInstructions;
+    
+    if (nInsn == 0) {
+      // that would be a break in execute() or instructionExecuted()
+      return true;
+      
+    } else if (nInsn == 1 && nextPc != null) {
+      // that is for setting the CG in executeInsn and then testing in
+      // instructionExecuted. Note that nextPc is reset before pre-exec notification
+      // and hence should only be non-null from insn.execute() up to the next
+      // ThreadInfo.executeInstruction()
+      return true;
+    }
+    
+    return false;
+  }
+  
+  /**
+   * get the number of instructions executed in the current transition. This
+   * gets incremented after calling Instruction.enter(), i.e. before
+   * notifying instructionExecuted() listeners
+   */
+  public int getExecutedInstructions(){
+    return executedInstructions;
+  }
+  
+  /**
+   * to be used from methods called from listeners, to find out if we are in a
+   * pre- or post-exec notification
+   */
+  public boolean isPreExec() {
+    return (nextPc == null);
+  }
+
+
+  //--- various thread state related methods
+
+  /**
+   * Updates the status of the thread.
+   */
+  public void setState (State newStatus) {
+    State oldStatus = threadData.state;
+
+    if (oldStatus != newStatus) {
+
+      assert (oldStatus != State.TERMINATED) : "can't resurrect thread " + this + " to " + newStatus.name();
+
+      threadDataClone().state = newStatus;
+
+      switch (newStatus) {
+      case NEW:
+        break; // Hmm, shall we report a thread object creation?
+      case RUNNING:
+        // nothing. the notifyThreadStarted has to happen from
+        // Thread.start(), since the thread could have been blocked
+        // at the time with a sync run() method
+       // assert lockRef == MJIEnv.NULL;
+        break;
+      case TERMINATED:
+        vm.notifyThreadTerminated(this);
+        break;
+      case BLOCKED:
+        assert lockRef != MJIEnv.NULL;
+        vm.notifyThreadBlocked(this);
+        break;
+      case UNBLOCKED:
+        assert lockRef == MJIEnv.NULL;
+        break; // nothing to notify
+      case WAITING:
+        assert lockRef != MJIEnv.NULL;
+        vm.notifyThreadWaiting(this);
+        break;
+      case INTERRUPTED:
+        vm.notifyThreadInterrupted(this);
+        break;
+      case NOTIFIED:
+        assert lockRef != MJIEnv.NULL;
+        vm.notifyThreadNotified(this);
+        break;
+      }
+
+      if (log.isLoggable(Level.FINE)){
+        log.fine("setStatus of " + getName() + " from "
+                 + oldStatus.name() + " to " + newStatus.name());
+      }
+    }
+  }
+
+  void setBlockedState (int objref) {
+    
+    State currentState = threadData.state;
+    switch (currentState){
+      case NEW:
+      case RUNNING:
+      case UNBLOCKED:
+        lockRef = objref;
+        setState(State.BLOCKED);
+        break;
+
+      default:
+        assert false : "thread " + this + "can't be blocked in state: " + currentState.name();
+    }
+  }
+
+  void setNotifiedState() {
+    State currentState = threadData.state;
+    switch (currentState){
+      case BLOCKED:
+      case INTERRUPTED: // too late, we are already interrupted
+      case NOTIFIED:
+        // can happen in a Thread.join()
+        break;
+      case WAITING:
+      case TIMEOUT_WAITING:
+        setState(State.NOTIFIED);
+        break;
+
+      default:
+        assert false : "thread " + this + "can't be notified in state: " + currentState.name();
+    }
+  }
+
+  /**
+   * Returns the current status of the thread.
+   */
+  public State getState () {
+    return threadData.state;
+  }
+
+
+  /**
+   * Returns true if this thread is either RUNNING or UNBLOCKED
+   */
+  public boolean isRunnable () {
+    if (threadData.suspendCount != 0)
+      return false;
+
+    switch (threadData.state) {
+    case RUNNING:
+    case UNBLOCKED:
+      return true;
+    case SLEEPING:
+      return true;    // that's arguable, but since we don't model time we treat it like runnable
+    case TIMEDOUT:
+      return true;    // would have been set to blocked if it couldn't reacquire the lock
+    default:
+      return false;
+    }
+  }
+
+  public boolean willBeRunnable () {
+    if (threadData.suspendCount != 0)
+      return false;
+
+    switch (threadData.state) {
+    case RUNNING:
+    case UNBLOCKED:
+      return true;
+    case TIMEOUT_WAITING: // it's not yet, but it will be at the time it gets scheduled
+    case SLEEPING:
+      return true;
+    default:
+      return false;
+    }
+  }
+
+  public boolean isNew () {
+    return (threadData.state == State.NEW);
+  }
+
+  public boolean isTimeoutRunnable () {
+    if (threadData.suspendCount != 0)
+      return false;
+
+    switch (threadData.state) {
+
+    case RUNNING:
+    case UNBLOCKED:
+    case SLEEPING:
+      return true;
+
+    case TIMEOUT_WAITING:
+      // depends on if we can re-acquire the lock
+      //assert lockRef != MJIEnv.NULL : "timeout waiting but no blocked object";
+      if (lockRef != MJIEnv.NULL){
+        ElementInfo ei = vm.getElementInfo(lockRef);
+        return ei.canLock(this);
+      } else {
+        return true;
+      }
+
+    default:
+      return false;
+    }
+  }
+
+  public boolean isTimedOut() {
+    return (threadData.state == State.TIMEDOUT);
+  }
+
+  public boolean isTimeoutWaiting() {
+    return (threadData.state == State.TIMEOUT_WAITING);
+  }
+
+  public void setTimedOut() {
+    setState(State.TIMEDOUT);
+  }
+
+  public void setTerminated() {
+    setState(State.TERMINATED);
+  }
+
+  public void resetTimedOut() {
+    // should probably check for TIMEDOUT
+    setState(State.TIMEOUT_WAITING);
+  }
+
+  public void setSleeping() {
+    setState(State.SLEEPING);
+  }
+
+  public boolean isSleeping(){
+    return (threadData.state == State.SLEEPING);
+  }
+
+  public void setRunning() {
+    setState(State.RUNNING);
+  }
+
+  public void setStopped(int throwableRef){
+    if (isTerminated()){
+      // no need to kill twice
+      return;
+    }
+
+    attributes |= ATTR_SET_STOPPED;
+
+    if (!hasBeenStarted()){
+      // that one is easy - just remember the state so that a subsequent start()
+      // does nothing
+      return;
+    }
+
+    // for all other cases, we need to have a proper stopping Throwable that does not
+    // fall victim to GC, and that does not cause NoUncaughtExcceptionsProperty violations
+    if (throwableRef == MJIEnv.NULL){
+      // if no throwable was provided (the normal case), throw a java.lang.ThreadDeath Error
+      ClassInfo cix = ClassInfo.getInitializedSystemClassInfo("java.lang.ThreadDeath", this);
+      throwableRef = createException(cix, null, MJIEnv.NULL);
+    }
+
+    // now the tricky part - this thread is alive but might be blocked, notified
+    // or waiting. In any case, exception action should not take place before
+    // the thread becomes scheduled again, which
+    // means we are not allowed to fiddle with its state in any way that changes
+    // scheduling/locking behavior. On the other hand, if this is the currently
+    // executing thread, take immediate action
+
+    if (isCurrentThread()){ // we are suicidal
+      if (isInNativeMethod()){
+        // remember the exception to be thrown when we return from the native method
+        env.throwException(throwableRef);
+      } else {
+        Instruction nextPc = throwException(throwableRef);
+        setNextPC(nextPc);
+      }
+
+    } else { // this thread is not currently running, this is an external kill
+
+      // remember there was a pending exception that has to be thrown the next
+      // time this gets scheduled, and make sure the exception object does
+      // not get GCed prematurely
+      ElementInfo eit = getModifiableElementInfo(objRef);
+      eit.setReferenceField("stopException", throwableRef);
+    }
+  }
+
+  public boolean isCurrentThread(){
+    return this == currentThread;
+  }
+
+  public boolean isInCurrentThreadList(){
+    return vm.getThreadList().contains(this);
+  }
+  
+  /**
+   * An alive thread is anything but TERMINATED or NEW
+   */
+  public boolean isAlive () {
+    State state = threadData.state;
+    return (state != State.TERMINATED && state != State.NEW);
+  }
+
+  public boolean isWaiting () {
+    State state = threadData.state;
+    return (state == State.WAITING) || (state == State.TIMEOUT_WAITING);
+  }
+
+  public boolean isWaitingOrTimedOut (){
+    State state = threadData.state;
+    return (state == State.WAITING) || (state == State.TIMEOUT_WAITING) || (state == State.TIMEDOUT);
+  }
+
+  public boolean isNotified () {
+    return (threadData.state == State.NOTIFIED);
+  }
+
+  public boolean isUnblocked () {
+    State state = threadData.state;
+    return (state == State.UNBLOCKED) || (state == State.TIMEDOUT);
+  }
+
+  public boolean isBlocked () {
+    return (threadData.state == State.BLOCKED);
+  }
+
+  public boolean isTerminated () {
+    return (threadData.state == State.TERMINATED);
+  }
+
+  public boolean isAtomic (){
+    return vm.getSystemState().isAtomic();
+  }
+  
+  public void setBlockedInAtomicSection (){
+    vm.getSystemState().setBlockedInAtomicSection();
+  }
+  
+  MethodInfo getExitMethod() {
+    MethodInfo mi = getClassInfo().getMethod("exit()V", true);
+    return mi;
+  }
+
+  public boolean isBlockedOrNotified() {
+    State state = threadData.state;
+    return (state == State.BLOCKED) || (state == State.NOTIFIED);
+  }
+
+  // this is just a state attribute
+  public boolean isStopped() {
+    return (attributes & ATTR_STOPPED) != 0;
+  }
+
+  public boolean isInNativeMethod(){
+    return top != null && top.isNative();
+  }
+
+  public boolean hasBeenStarted(){
+    return (threadData.state != State.NEW);
+  }
+
+  public String getStateName () {
+    return threadData.getState().name();
+  }
+
+  @Override
+  public Iterator<StackFrame> iterator () {
+    return new StackIterator();
+  }
+
+  public Iterable<StackFrame> invokedStackFrames () {
+    return new Iterable<StackFrame>() {
+      @Override
+       public Iterator<StackFrame> iterator() {
+        return new InvokedStackIterator();
+      }
+    };
+  }
+
+  /**
+   * this returns a copy of the StackFrames in reverse order. Note this is
+   * redundant because the frames are linked explicitly
+   * @deprecated - use Iterable<StackFrame>
+   */
+  @Deprecated
+  public List<StackFrame> getStack() {
+    ArrayList<StackFrame> list = new ArrayList<StackFrame>(stackDepth);
+
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      list.add(frame);
+    }
+
+    Collections.reverse(list);
+
+    return list;
+  }
+
+  /**
+   * returns StackFrames which have been entered through a corresponding
+   * invoke instruction (in top first order)
+   */
+  public List<StackFrame> getInvokedStackFrames() {
+    ArrayList<StackFrame> list = new ArrayList<StackFrame>(stackDepth);
+
+    int i = stackDepth-1;
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      if (!frame.isDirectCallFrame()){
+        list.add( frame);
+      }
+    }
+    Collections.reverse(list);
+
+    return list;
+  }
+
+  public Scheduler getScheduler(){
+    return vm.getScheduler();
+  }
+  
+  public int getStackDepth() {
+    return stackDepth;
+  }
+  
+  public MethodInfo getEntryMethod(){    
+    return appCtx.getEntryMethod();
+  }
+
+  public StackFrame getCallerStackFrame (int offset){
+    int n = offset;
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      if (n < 0){
+        break;
+      } else if (n == 0){
+        return frame;
+      }
+      n--;
+    }
+    return null;
+  }
+
+  public StackFrame getLastInvokedStackFrame() {
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      if (!frame.isDirectCallFrame()){
+        return frame;
+      }
+    }
+
+    return null;
+  }
+
+  public StackFrame getLastNonSyntheticStackFrame (){
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      if (!frame.isSynthetic()){
+        return frame;
+      }
+    }
+
+    return null;
+  }
+  
+  // this is ugly - it can modify deeper stack frames
+  public StackFrame getModifiableLastNonSyntheticStackFrame (){
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      if (!frame.isSynthetic()){
+        if (frame.isFrozen()) {
+          StackFrame newFrame = frame.clone();
+          
+          if (frame == top) {
+            frame = newFrame;
+            top = newFrame;
+            
+          } else {
+            // Ughh, now we have to clone all frozen frames above
+            StackFrame fLast = null;
+            for (StackFrame f = getModifiableTopFrame(); f != frame; f = f
+                .getPrevious()) {
+              if (f.isFrozen()) {
+                f = f.clone();
+                if (fLast != null) {
+                  fLast.setPrevious(f);
+                }
+              }
+              fLast = f;
+            }
+            if (fLast != null) {
+              fLast.setPrevious(newFrame);
+            }
+
+            frame = newFrame;
+          }
+        }
+        
+        return frame;
+      }
+    }
+
+    return null;
+  }
+  
+
+  /**
+   * Returns the this pointer of the callee from the stack.
+   */
+  public int getCalleeThis (MethodInfo mi) {
+    return top.getCalleeThis(mi);
+  }
+
+  /**
+   * Returns the this pointer of the callee from the stack.
+   */
+  public int getCalleeThis (int size) {
+    return top.getCalleeThis(size);
+  }
+
+  public ClassInfo getClassInfo (int objref) {
+    return env.getClassInfo(objref);
+  }
+
+  public boolean isCalleeThis (ElementInfo r) {
+    if (top == null || r == null) {
+      return false;
+    }
+
+    Instruction pc = getPC();
+
+    if (pc == null ||
+        !(pc instanceof JVMInvokeInstruction) ||
+        pc instanceof INVOKESTATIC) {
+      return false;
+    }
+
+    JVMInvokeInstruction call = (JVMInvokeInstruction) pc;
+
+    return getCalleeThis(Types.getArgumentsSize(call.getInvokedMethodSignature()) + 1) == r.getObjectRef();
+  }
+
+  public ApplicationContext getApplicationContext(){
+    return appCtx;
+  }
+  
+  public SystemClassLoaderInfo getSystemClassLoaderInfo(){
+    return appCtx.sysCl;
+  }
+  
+  /**
+   * Returns the class information.
+   */
+  public ClassInfo getClassInfo () {
+    return ci;
+  }
+
+  public MJIEnv getEnv() {
+    return env;
+  }
+
+  public boolean isInterrupted (boolean resetStatus) {
+    ElementInfo ei = getElementInfo(getThreadObjectRef());
+    boolean status =  ei.getBooleanField("interrupted");
+
+    if (resetStatus && status) {
+      ei = ei.getModifiableInstance();
+      ei.setBooleanField("interrupted", false);
+    }
+
+    return status;
+  }
+
+  /**
+   * path local unique id for live threads. This is what we use for the
+   * public java.lang.Thread.getId() that can be called from SUT code. It is
+   * NOT what we use for our canonical root set
+   */
+  public int getId () {
+    return id;
+  }
+
+  /**
+   * this is our internal, search global id that is used for the
+   * canonical root set
+   */
+  public int getGlobalId(){
+    return id;
+  }
+  
+  
+  /**
+   * record what this thread is being blocked on.
+   */
+  void setLockRef (int objref) {
+/**
+    assert ((lockRef == MJIEnv.NULL) || (lockRef == objref)) :
+      "attempt to overwrite lockRef: " + vm.getHeap().get(lockRef) +
+      " with: " + vm.getHeap().get(objref);
+**/
+    lockRef = objref;
+  }
+
+  /**
+   * thread is not blocked anymore
+   * needs to be public since we have to use it from INVOKECLINIT (during call skipping)
+   */
+  public void resetLockRef () {
+    lockRef = MJIEnv.NULL;
+  }
+
+  public int getLockRef() {
+    return lockRef;
+  }
+
+  public ElementInfo getLockObject () {
+    if (lockRef == MJIEnv.NULL) {
+      return null;
+    } else {
+      return vm.getElementInfo(lockRef);
+    }
+  }
+
+  /**
+   * Returns the line number of the program counter of the top stack frame.
+   */
+  public int getLine () {
+    if (top == null) {
+      return -1;
+    } else {
+      return top.getLine();
+    }
+  }
+  
+  //--- suspend/resume modeling
+  // modeling this with a count is an approximation. In reality it behaves
+  // rather like a race that /sometimes/ causes the resume to fail, but its
+  // Ok if we overapproximate on the safe side, since suspend/resume is such
+  // an inherently unsafe thing. What we *do* want to preserve faithfully is 
+  // that locks held by the suspended thread are not released
+  
+  /**
+   * set suspension status
+   * @return true if thread was not suspended
+   */
+  public boolean suspend() {
+    return threadDataClone().suspendCount++ == 0;
+  }
+
+  /**
+   * unset suspension status
+   * @return true if thread was suspended
+   */
+  public boolean resume() {
+    return (threadData.suspendCount > 0) && (--threadDataClone().suspendCount == 0);
+  }
+  
+  public boolean isSuspended() {
+    return threadData.suspendCount > 0;
+  }
+
+
+  //--- locks
+  
+  /**
+   * Sets the number of locks held at the time of a wait.
+   */
+  public void setLockCount (int l) {
+    if (threadData.lockCount != l) {
+      threadDataClone().lockCount = l;
+    }
+  }
+
+  /**
+   * Returns the number of locks in the last wait.
+   */
+  public int getLockCount () {
+    return threadData.lockCount;
+  }
+
+  // avoid use in performance critical code
+  public List<ElementInfo> getLockedObjects () {
+    List<ElementInfo> lockedObjects = new LinkedList<ElementInfo>();
+    Heap heap = vm.getHeap();
+    
+    for (int i=0; i<lockedObjectReferences.length; i++) {
+      ElementInfo ei = heap.get(lockedObjectReferences[i]);
+      lockedObjects.add(ei);
+    }
+    
+    return lockedObjects;
+  }
+
+  public boolean hasLockedObjects() {
+    return lockedObjectReferences.length > 0;
+  }
+  
+  public int[] getLockedObjectReferences () {
+    return lockedObjectReferences;
+  }
+
+  public boolean isLockOwner (ElementInfo ei){
+    return ei.getLockingThread() == this;
+  }
+  
+  /**
+   * returns the current method in the top stack frame, which is always a
+   * bytecode method (executed by JPF)
+   */
+  public MethodInfo getTopFrameMethodInfo () {
+    if (top != null) {
+      return top.getMethodInfo();
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * return the ClassInfo of the topmost stackframe that is not a direct call 
+   */
+  public ClassInfo getExecutingClassInfo(){
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      MethodInfo miExecuting = frame.getMethodInfo();
+      ClassInfo ciExecuting = miExecuting.getClassInfo();
+      if (ciExecuting != null){
+        return ciExecuting;
+      }
+    }
+    
+    return null;
+  }
+  
+  
+  
+  public ClassInfo resolveReferencedClass (String clsName){
+    ClassInfo ciTop = top.getClassInfo();
+    return ciTop.resolveReferencedClass(clsName);
+            
+    //return ClassLoaderInfo.getCurrentClassLoader(this).getResolvedClassInfo(clsName);
+  }
+  
+  public boolean isInCtor () {
+    // <2do> - hmm, if we don't do this the whole stack, we miss factored
+    // init funcs
+    MethodInfo mi = getTopFrameMethodInfo();
+    if (mi != null) {
+      return mi.isCtor();
+    } else {
+      return false;
+    }
+  }
+
+  public boolean isCtorOnStack (int objRef){
+    for (StackFrame f = top; f != null; f = f.getPrevious()){
+      if (f.getThis() == objRef && f.getMethodInfo().isCtor()){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  public boolean isClinitOnStack (ClassInfo ci){
+    for (StackFrame f = top; f != null; f = f.getPrevious()){
+      MethodInfo mi = f.getMethodInfo();
+      if (mi.isClinit(ci)){
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+
+  public String getName () {
+    return threadData.name;
+  }
+
+
+
+  /**
+   * Returns the object reference.
+   */
+  public int getThreadObjectRef () {
+    return objRef;
+  }
+
+  public ElementInfo getThreadObject(){
+    return getElementInfo(objRef);
+  }
+
+  public ElementInfo getModifiableThreadObject() {
+    return getModifiableElementInfo(objRef);
+  }
+  
+
+  /**
+   * Sets the program counter of the top stack frame.
+   */
+  public void setPC (Instruction pc) {
+    getModifiableTopFrame().setPC(pc);
+  }
+
+  public void advancePC () {
+    getModifiableTopFrame().advancePC();
+  }
+
+  /**
+   * Returns the program counter of the top stack frame.
+   */
+  public Instruction getPC () {
+    if (top != null) {
+      return top.getPC();
+    } else {
+      return null;
+    }
+  }
+
+  public Instruction getNextPC () {
+    return nextPc;
+  }
+
+
+  /**
+   * get the current stack trace of this thread
+   * this is called during creation of a Throwable, hence we should skip
+   * all throwable ctors in here
+   * <2do> this is only a partial solution,since we don't catch exceptionHandlers
+   * in Throwable ctors yet
+   */
+  public String getStackTrace () {
+    StringBuilder sb = new StringBuilder(256);
+
+    for (StackFrame sf = top; sf != null; sf = sf.getPrevious()){
+      MethodInfo mi = sf.getMethodInfo();
+
+      if (mi.isCtor()){
+        ClassInfo ci = mi.getClassInfo();
+        if (ci.isInstanceOf("java.lang.Throwable")) {
+          continue;
+        }
+      }
+
+      sb.append("\tat ");
+      sb.append(sf.getStackTraceInfo());
+      sb.append('\n');
+    }
+
+    return sb.toString();
+  }
+
+
+  /**
+   * Returns the information necessary to store.
+   *
+   * <2do> pcm - not clear to me how lower stack frames can contribute to
+   * a different threadinfo state hash - only the current one can be changed
+   * by the executing method
+   */
+  public void dumpStoringData (IntVector v) {
+    v = null;  // Get rid of IDE warnings
+  }
+
+  /**
+   * Returns the object reference of the target.
+   */
+  public int getRunnableRef () {
+    return targetRef;
+  }
+
+  /**
+   * Returns the pointer to the object reference of the executing method
+   */
+  public int getThis () {
+    return top.getThis();
+  }
+
+  public ElementInfo getThisElementInfo(){
+    return getElementInfo(getThis());
+  }
+
+  public boolean isThis (ElementInfo ei) {
+    if (ei == null) {
+      return false;
+    }
+
+    if (top == null) {
+      return false;
+    }
+
+    if (getTopFrameMethodInfo().isStatic()) {
+      return false;
+    } else {
+      int thisRef = top.getThis();
+      return ei.getObjectRef() == thisRef;
+    }
+  }
+
+  public boolean atMethod (String mname) {
+    return top != null && getTopFrameMethodInfo().getFullName().equals(mname);
+  }
+
+  public boolean atPosition (int position) {
+    if (top == null) {
+      return false;
+    } else {
+      Instruction pc = getPC();
+      return pc != null && pc.getPosition() == position;
+    }
+  }
+
+  public boolean atReturn () {
+    if (top == null) {
+      return false;
+    } else {
+      Instruction pc = getPC();
+      return pc instanceof ReturnInstruction;
+    }
+  }
+
+
+  /**
+   * reset any information that has to be re-computed in a backtrack
+   * (i.e. hasn't been stored explicitly)
+   */
+  void resetVolatiles () {
+    // resetting lock sets goes here
+    lockedObjectReferences = emptyLockRefs;
+
+    // the ref of the object we are blocked on or waiting for
+    lockRef = MJIEnv.NULL;
+
+    pendingException = null;
+  }
+  
+  /**
+   * this is used when restoring states
+   */
+  void updateLockedObject (ElementInfo ei) {
+    int n = lockedObjectReferences.length;    
+    int[] a = new int[n+1];
+    System.arraycopy(lockedObjectReferences, 0, a, 0, n);
+    a[n] = ei.getObjectRef();
+    lockedObjectReferences = a;
+    
+    // don't notify here, it's just a restore
+  }
+
+  void addLockedObject (ElementInfo ei) {
+    int n = lockedObjectReferences.length;    
+    int[] a = new int[n+1];
+    System.arraycopy(lockedObjectReferences, 0, a, 0, n);
+    a[n] = ei.getObjectRef();
+    lockedObjectReferences = a;
+    
+    vm.notifyObjectLocked(this, ei);
+  }
+
+  void removeLockedObject (ElementInfo ei) {
+    int objRef = ei.getObjectRef();
+    int n = lockedObjectReferences.length;
+    
+    if (n == 1) {
+      assert lockedObjectReferences[0] == objRef;
+      lockedObjectReferences = emptyLockRefs;
+      
+    } else {
+      int[] a = new int[n - 1];
+
+      for (int i = 0, j = 0; i < n; i++) {
+        if (lockedObjectReferences[i] != objRef) {
+          a[j++] = lockedObjectReferences[i];
+        }
+      }
+      lockedObjectReferences = a;
+    }
+    
+    vm.notifyObjectUnlocked(this, ei);
+  }
+
+
+  @Override
+  public Object clone() {
+    try {
+      // threadData and top StackFrame are copy-on-write, so we should not have to clone them
+      // lockedObjects are state-volatile and restored explicitly after a backtrack
+      return super.clone();
+
+    } catch (CloneNotSupportedException cnsx) {
+      return null;
+    }
+  }
+
+  /**
+   * Returns the number of stack frames.
+   */
+  public int countStackFrames () {
+    return stackDepth;
+  }
+
+  /**
+   * get a stack snapshot that consists of an array of {mthId,pc} pairs.
+   * strip stackframes that enter instance methods of the exception object
+   */
+  public int[] getSnapshot (int xObjRef) {
+    StackFrame frame = top;
+    int n = stackDepth;
+    
+    if (xObjRef != MJIEnv.NULL){ // filter out exception method frames
+      for (;frame != null; frame = frame.getPrevious()){
+        if (frame.getThis() != xObjRef){
+          break;
+        }
+        n--;
+      }
+    }
+
+    int j=0;
+    int[] snap = new int[n*2];
+
+    for (; frame != null; frame = frame.getPrevious()){
+      snap[j++] = frame.getMethodInfo().getGlobalId();
+      snap[j++] = frame.getPC().getInstructionIndex();
+    }
+
+    return snap;
+  }
+
+  /**
+   * turn a snapshot into an JPF array of StackTraceElements, which means
+   * a lot of objects. Do this only on demand
+   */
+  public int createStackTraceElements (int[] snapshot) {
+    int n = snapshot.length/2;
+    int nVisible=0;
+    StackTraceElement[] list = new StackTraceElement[n];
+    for (int i=0, j=0; i<n; i++){
+      int methodId = snapshot[j++];
+      int pcOffset = snapshot[j++];
+      StackTraceElement ste = new StackTraceElement( methodId, pcOffset);
+      if (!ste.ignore){
+        list[nVisible++] = ste;
+      }
+    }
+
+    Heap heap = vm.getHeap();
+    ElementInfo eiArray = heap.newArray("Ljava/lang/StackTraceElement;", nVisible, this);
+    for (int i=0; i<nVisible; i++){
+      int eref = list[i].createJPFStackTraceElement();
+      eiArray.setReferenceElement( i, eref);
+    }
+
+    return eiArray.getObjectRef();
+  }
+
+  void print (PrintWriter pw, String s) {
+    if (pw != null){
+      pw.print(s);
+    } else {
+      vm.print(s);
+    }
+  }
+
+  public void printStackTrace (int objRef) {
+    printStackTrace(null, objRef);
+  }
+
+  public void printPendingExceptionOn (PrintWriter pw) {
+    if (pendingException != null) {
+      printStackTrace( pw, pendingException.getExceptionReference());
+    }
+  }
+
+  /**
+   * the reason why this is kind of duplicated (there is also a StackFrame.getPositionInfo)
+   * is that this might be working off a StackTraceElement[] that is created when the exception
+   * is created. At the time printStackTrace() is called, the StackFrames in question
+   * are most likely already be unwinded
+   */
+  public void printStackTrace (PrintWriter pw, int objRef) {
+    // 'env' usage is not ideal, since we don't know from what context we are called, and
+    // hence the MJIEnv calling context might not be set (no Method or ClassInfo)
+    // on the other hand, we don't want to re-implement all the MJIEnv accessor methods
+
+    print(pw, env.getClassInfo(objRef).getName());
+    int msgRef = env.getReferenceField(objRef,"detailMessage");
+    if (msgRef != MJIEnv.NULL) {
+      print(pw, ": ");
+      print(pw, env.getStringObject(msgRef));
+    }
+    print(pw, "\n");
+
+    // try the 'stackTrace' field first, it might have been set explicitly
+    int aRef = env.getReferenceField(objRef, "stackTrace"); // StackTrace[]
+    if (aRef != MJIEnv.NULL) {
+      int len = env.getArrayLength(aRef);
+      for (int i=0; i<len; i++) {
+        int steRef = env.getReferenceArrayElement(aRef, i);
+        if (steRef != MJIEnv.NULL){  // might be ignored (e.g. direct call)
+          StackTraceElement ste = new StackTraceElement(steRef);
+          ste.printOn( pw);
+        }
+      }
+
+    } else { // fall back to use the snapshot stored in the exception object
+      aRef = env.getReferenceField(objRef, "snapshot");
+      int[] snapshot = env.getIntArrayObject(aRef);
+      int len = snapshot.length/2;
+
+      for (int i=0, j=0; i<len; i++){
+        int methodId = snapshot[j++];
+        int pcOffset = snapshot[j++];
+        StackTraceElement ste = new StackTraceElement( methodId, pcOffset);
+        ste.printOn( pw);
+      }
+    }
+
+    int causeRef = env.getReferenceField(objRef, "cause");
+    if ((causeRef != objRef) && (causeRef != MJIEnv.NULL)){
+      print(pw, "Caused by: ");
+      printStackTrace(pw, causeRef);
+    }
+  }
+
+  class StackTraceElement {
+    String clsName, mthName, fileName;
+    int line;
+    boolean ignore;
+
+
+    StackTraceElement (int methodId, int pcOffset) {
+      if (methodId == MethodInfo.DIRECT_CALL) {
+        ignore = true;
+
+      } else {
+        MethodInfo mi = MethodInfo.getMethodInfo(methodId);
+        if (mi != null) {
+          clsName = mi.getClassName();
+          mthName = mi.getName();
+
+          fileName = mi.getStackTraceSource();          
+          if (pcOffset < 0){
+            // See ThreadStopTest.threadDeathWhileRunstart
+            // <2do> remove when RUNSTART is gone
+            pcOffset = 0;
+          }
+          line = mi.getLineNumber(mi.getInstruction(pcOffset));
+
+        } else { // this sounds like a bug
+          clsName = "?";
+          mthName = "?";
+          fileName = "?";
+          line = -1;
+        }
+      }
+    }
+
+    StackTraceElement (int sRef){
+      clsName = env.getStringObject(env.getReferenceField(sRef, "clsName"));
+      mthName = env.getStringObject(env.getReferenceField(sRef, "mthName"));
+      fileName = env.getStringObject(env.getReferenceField(sRef, "fileName"));
+      line = env.getIntField(sRef, "line");
+    }
+
+    int createJPFStackTraceElement() {
+      if (ignore) {
+        return MJIEnv.NULL;
+        
+      } else {
+        Heap heap = vm.getHeap();
+        ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.StackTraceElement");
+        ElementInfo ei = heap.newObject(ci, ThreadInfo.this);
+
+        ei.setReferenceField("clsName", heap.newString(clsName, ThreadInfo.this).getObjectRef());
+        ei.setReferenceField("mthName", heap.newString(mthName, ThreadInfo.this).getObjectRef());
+
+        String fname = fileName != null ? fileName : "Unknown Source";
+        ei.setReferenceField("fileName", heap.newString(fname, ThreadInfo.this).getObjectRef());
+                
+        ei.setIntField("line", line);
+
+        return ei.getObjectRef();
+      }
+    }
+
+    void printOn (PrintWriter pw){
+      if (!ignore){
+        // the usual behavior is to print only the filename, strip the path
+        if (fileName != null){
+          int idx = fileName.lastIndexOf(File.separatorChar);
+          if (idx >=0) {
+            fileName = fileName.substring(idx+1);
+          }
+        }
+
+        print(pw, "\tat ");
+        if (clsName != null){
+          print(pw, clsName);
+          print(pw, ".");
+        } else { // some synthetic methods don't belong to classes
+          print(pw, "[no class] ");
+        }
+        print(pw, mthName);
+
+        if (fileName != null){
+          print(pw, "(");
+          print(pw, fileName);
+          if (line >= 0){
+            print(pw, ":");
+            print(pw, Integer.toString(line));
+          }
+          print(pw, ")");
+        } else {
+          //print(pw, "<no source>");
+        }
+
+        print(pw, "\n");
+      }
+    }
+  }
+
+  /**
+   * this is a helper class to store listener generated exception requests that are checked before and after
+   * calling Instruction.execute(). This is a safe way to raise SUT exceptions from listener code without compromising
+   * consistency of executes() that are not prepared to cut short by means of re-execution or host VM exceptions
+   */
+  static class SUTExceptionRequest {
+    String xClsName;
+    String details;
+    
+    SUTExceptionRequest (String xClsName, String details){
+      this.xClsName = xClsName;
+      this.details = details;
+    }
+    
+    public String getExceptionClassName(){
+      return xClsName;
+    }
+    
+    public String getDetails(){
+      return details;
+    }
+  }
+  
+  public void requestSUTException (String exceptionClsName, String details){
+    pendingSUTExceptionRequest = new SUTExceptionRequest( exceptionClsName, details);
+    if (nextPc == null){ // this is pre-exec, skip the execute()
+      attributes |= ATTR_SKIP_INSN_EXEC;
+    }
+  }
+  
+  protected void processPendingSUTExceptionRequest (){
+    if (pendingSUTExceptionRequest != null){
+      // <2do> we could do more specific checks for ClassNotFoundExceptions here
+      nextPc = createAndThrowException( pendingSUTExceptionRequest.getExceptionClassName(), pendingSUTExceptionRequest.getDetails());
+      pendingSUTExceptionRequest = null;
+    }
+  }
+  
+  
+  /**
+   * <2do> pcm - this is only valid for java.* and our own Throwables that don't
+   * need ctor execution since we only initializeSharednessPolicy the Throwable fields. This method
+   * is here to avoid round trips in case of exceptions
+   */
+  int createException (ClassInfo ci, String details, int causeRef){
+    int[] snap = getSnapshot(MJIEnv.NULL);
+    return vm.getHeap().newSystemThrowable(ci, details, snap, causeRef, this, 0).getObjectRef();
+  }
+
+  /**
+   * Creates and throws an exception. This is what is used if the exception is
+   * thrown by the VM (or a listener)
+   */
+  public Instruction createAndThrowException (ClassInfo ci, String details) {
+    //if (ci.initializeClass(this)) {
+    //  return getPC();
+    //}
+    ci.initializeClassAtomic(this);
+
+    int objref = createException(ci,details, MJIEnv.NULL);
+    return throwException(objref);
+  }
+
+  /**
+   * Creates an exception and throws it.
+   */
+  public Instruction createAndThrowException (String cname) {
+    return createAndThrowException(cname, null);
+  }
+
+  public Instruction createAndThrowException (String cname, String details) {
+    try {
+      ClassInfo ci = null;
+      try {
+        ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cname);
+      } catch(ClassInfoException cie) {
+        // the non-system class loader couldn't find the class, 
+        if(cie.getExceptionClass().equals("java.lang.ClassNotFoundException") &&
+                        !ClassLoaderInfo.getCurrentClassLoader().isSystemClassLoader()) {
+          ci = ClassLoaderInfo.getSystemResolvedClassInfo(cname);
+        } else {
+          throw cie;
+        }
+      }
+      return createAndThrowException(ci, details);
+      
+    } catch (ClassInfoException cie){
+      if(!cname.equals(cie.getExceptionClass())) {
+        ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(cie.getExceptionClass());
+        return createAndThrowException(ci, cie.getMessage());
+      } else {
+        throw cie;
+      }
+    }
+  }
+
+  /**
+   * can be used by instructions to break long transitions (preferably on 
+   * backjumps so that state matching could terminate the search)
+   */
+  public boolean maxTransitionLengthExceeded(){
+    return executedInstructions >= maxTransitionLength;
+  }
+  
+  /**
+   * enter instructions until there is none left or somebody breaks
+   * the transition (e.g. by registering a CG)
+   * 
+   * this is the inner interpreter loop of JPF
+   */
+  protected void executeTransition (SystemState ss) throws JPFException {
+    Instruction pc;
+    outer:
+    while ((pc = getPC()) != null){
+      Instruction nextPc = null;
+
+      currentThread = this;
+      executedInstructions = 0;
+      pendingException = null;
+
+      if (isStopped()){
+        pc = throwStopException();
+        setPC(pc);
+      }
+
+      // this constitutes the main transition loop. It gobbles up
+      // insns until someone registered a ChoiceGenerator, there are no insns left,
+      // the transition was explicitly marked as ignored, or we have reached a
+      // max insn count and preempt the thread upon the next available backjump
+      while (pc != null) {
+        nextPc = executeInstruction();
+
+        if (ss.breakTransition()) {
+          if (ss.extendTransition()){
+            continue outer;
+            
+          } else {
+            if (executedInstructions == 0){ // a CG from a re-executed insn
+              if (isEmptyTransitionEnabled()){ // treat as a new state if empty transitions are enabled
+                ss.setForced(true);
+              }
+            }
+            return;
+          }
+
+        } else {        
+          pc = nextPc;
+        }
+      }
+    }
+  }
+
+
+  protected void resetTransientAttributes(){
+    attributes &= ~(ATTR_SKIP_INSN_EXEC | ATTR_SKIP_INSN_LOG | ATTR_ENABLE_EMPTY_TRANSITION);
+  }
+  
+  /**
+   * Execute next instruction.
+   */
+  public Instruction executeInstruction () {
+    Instruction pc = getPC();
+    SystemState ss = vm.getSystemState();
+
+    resetTransientAttributes();
+    nextPc = null;
+    
+    // note that we don't reset pendingSUTExceptionRequest since it could be set outside executeInstruction()
+    
+    if (log.isLoggable(Level.FINER)) {
+      log.fine( pc.getMethodInfo().getFullName() + " " + pc.getPosition() + " : " + pc);
+    }
+
+    // this is the pre-execution notification, during which a listener can perform
+    // on-the-fly instrumentation or even replace the instruction alltogether
+    vm.notifyExecuteInstruction(this, pc);
+
+    if ((pendingSUTExceptionRequest == null) && ((attributes & ATTR_SKIP_INSN_EXEC) == 0)){
+        try {
+          nextPc = pc.execute(this);
+        } catch (ClassInfoException cie) {
+          nextPc = this.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
+        }
+      }
+
+    // we also count the skipped ones
+    executedInstructions++;
+    
+    if ((attributes & ATTR_SKIP_INSN_LOG) == 0) {
+      ss.recordExecutionStep(pc);
+    }
+
+    // here we have our post exec bytecode exec observation point
+    vm.notifyInstructionExecuted(this, pc, nextPc);
+    
+    // since this is part of the inner execution loop, it is a convenient place to check for probes
+    vm.getSearch().checkAndResetProbeRequest();
+    
+    // clean up whatever might have been stored by enter
+    pc.cleanupTransients();
+
+    if (pendingSUTExceptionRequest != null){
+      processPendingSUTExceptionRequest();
+    }
+    
+    // set+return the next insn to enter if we did not return from the last stack frame.
+    // Note that 'nextPc' might have been set by a listener, and/or 'top' might have
+    // been changed by executing an invoke, return or throw (handler), or by
+    // pushing overlay calls on the stack
+    if (top != null) {
+      // <2do> this is where we would have to handle general insn repeat
+      setPC(nextPc);
+      return nextPc;
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * enter instruction hidden from any listeners, and do not
+   * record it in the path
+   */
+  public Instruction executeInstructionHidden () {
+    Instruction pc = getPC();
+    SystemState ss = vm.getSystemState();
+    KernelState ks = vm.getKernelState();
+
+    nextPc = null; // reset in case pc.execute() blows (this could be behind an exception firewall)
+
+    if (log.isLoggable(Level.FINE)) {
+      log.fine( pc.getMethodInfo().getFullName() + " " + pc.getPosition() + " : " + pc);
+    }
+
+    try {
+        nextPc = pc.execute(this);
+      } catch (ClassInfoException cie) {
+        nextPc = this.createAndThrowException(cie.getExceptionClass(), cie.getMessage());
+      }
+
+    // we also count the hidden ones since isFirstStepInsn depends on it
+    executedInstructions++;
+    
+    // since this is part of the inner execution loop, it is a convenient place  to check probe notifications
+    vm.getSearch().checkAndResetProbeRequest();
+    
+    // we did not return from the last frame stack
+    if (top != null) { // <2do> should probably bomb otherwise
+      setPC(nextPc);
+    }
+
+    return nextPc;
+  }
+
+  /**
+   * is this after calling Instruction.enter()
+   * used by instructions and listeners
+   */
+  public boolean isPostExec() {
+    return (nextPc != null);
+  }
+
+  public void reExecuteInstruction() {
+    nextPc = getPC();
+  }
+
+  public boolean willReExecuteInstruction() {
+    return (getPC() == nextPc);
+  }
+  
+  /**
+   * skip the next bytecode. To be used by listeners to on-the-fly replace
+   * instructions
+   */
+  public void skipInstruction (Instruction nextInsn) {
+    attributes |= ATTR_SKIP_INSN_EXEC;
+    
+    //assert nextInsn != null;
+    nextPc = nextInsn;
+  }
+
+  /**
+   * skip and continue with the following instruction. This is deprecated because
+   * callers should explicitly provide the next instruction (depending on the
+   * skipped insn)
+   */
+  @Deprecated
+  public void skipInstruction(){
+    skipInstruction(getPC().getNext());
+  }
+
+  public boolean isInstructionSkipped() {
+    return (attributes & ATTR_SKIP_INSN_EXEC) != 0;
+  }
+
+  public void skipInstructionLogging () {
+    attributes |= ATTR_SKIP_INSN_LOG;
+  }
+
+  /**
+   * explicitly set the next insn to enter. To be used by listeners that
+   * replace bytecode exec (during 'executeInstruction' notification
+   *
+   * Note this is dangerous because you have to make sure the operand stack is
+   * in a consistent state. This also will fail if someone already ordered
+   * reexecution of the current instruction
+   */
+  public boolean setNextPC (Instruction insn) {
+    if (nextPc == null){
+      // this is pre-execution, if we don't skip then the next insn.execute() is going
+      // to override what we set here
+      attributes |= ATTR_SKIP_INSN_EXEC;
+      nextPc = insn;
+      return true;
+      
+    } else {
+      if (top != null && nextPc != top.getPC()){ // this needs to be re-executed
+        nextPc = insn;   
+        return true;
+      }
+    }
+    
+    return false;
+  }
+
+  /**
+   * Executes a method call. Be aware that it executes the whole method as one atomic
+   * step. Arguments have to be already on the provided stack
+   *
+   * This only works for non-native methods, and does not allow any choice points,
+   * so you have to know very well what you are doing.
+   *
+   * Instructions executed by this method are still fully observable and stored in
+   * the path
+   */
+  public void executeMethodAtomic (StackFrame frame) {
+
+    pushFrame(frame);
+    int    depth = countStackFrames();
+    Instruction pc = frame.getPC();
+    SystemState ss = vm.getSystemState();
+
+    ss.incAtomic(); // to shut off avoidable context switches (MONITOR_ENTER and wait() can still block)
+
+    while (depth <= countStackFrames()) {
+      Instruction nextPC = executeInstruction();
+
+      if (ss.getNextChoiceGenerator() != null) {
+        // BANG - we can't have CG's here
+        // should be rather an ordinary exception
+        // createAndThrowException("java.lang.AssertionError", "choice point in sync executed method: " + frame);
+        throw new JPFException("choice point in atomic method execution: " + frame);
+      } else {
+        pc = nextPC;
+      }
+    }
+
+    vm.getSystemState().decAtomic();
+
+    nextPc = null;
+
+    // the frame was already removed by the RETURN insn of the frame's method
+  }
+
+  /**
+   * enter method atomically, but also hide it from listeners and do NOT add
+   * executed instructions to the path.
+   *
+   * this can be even more confusing than executeMethodAtomic(), since
+   * nothing prevents such a method from changing the program state, and we
+   * wouldn't know for what reason by looking at the trace
+   *
+   * this method should only be used if we have to enter test application code
+   * like hashCode() or equals() from native code, e.g. to silently check property
+   * violations
+   *
+   * executeMethodHidden also acts as an exception firewall, since we don't want
+   * any silently executed code fall back into the visible path (for
+   * no observable reason)
+   */
+  public void executeMethodHidden (StackFrame frame) {
+
+    pushFrame(frame);
+    
+    int depth = countStackFrames(); // this includes the DirectCallStackFrame
+    Instruction pc = frame.getPC();
+
+    vm.getSystemState().incAtomic(); // to shut off avoidable context switches (MONITOR_ENTER and wait() can still block)
+
+    while (depth <= countStackFrames()) {
+      Instruction nextPC = executeInstructionHidden();
+
+      if (pendingException != null) {
+
+      } else {
+        if (nextPC == pc) {
+          // BANG - we can't have CG's here
+          // should be rather an ordinary exception
+          // createAndThrowException("java.lang.AssertionError", "choice point in sync executed method: " + frame);
+          throw new JPFException("choice point in hidden method execution: " + frame);
+        } else {
+          pc = nextPC;
+        }
+      }
+    }
+
+    vm.getSystemState().decAtomic();
+
+    nextPc = null;
+
+    // the frame was already removed by the RETURN insn of the frame's method
+  }
+
+  public Heap getHeap () {
+    return vm.getHeap();
+  }
+
+  public ElementInfo getElementInfo (int objRef) {
+    Heap heap = vm.getHeap();
+    return heap.get(objRef);
+  }
+  
+  public ElementInfo getModifiableElementInfo (int ref) {
+    Heap heap = vm.getHeap();
+    return heap.getModifiable(ref);
+  }
+  
+  public ElementInfo getBlockedObject (MethodInfo mi, boolean isBeforeCall, boolean isModifiable) {
+    int         objref;
+    ElementInfo ei = null;
+
+    if (mi.isSynchronized()) {
+      if (mi.isStatic()) {
+        objref = mi.getClassInfo().getClassObjectRef();
+      } else {
+        // NOTE 'inMethod' doesn't work for natives, because getThis() pulls 'this' from the stack frame, 
+        // which we don't have (and don't need) for natives
+        objref = isBeforeCall ? getCalleeThis(mi) : getThis();
+      }
+
+      ei = (isModifiable) ? getModifiableElementInfo(objref) : getElementInfo(objref);
+
+      assert (ei != null) : ("inconsistent stack, no object or class ref: " +
+                               mi.getFullName() + " (" + objref +")");
+    }
+
+    return ei;
+  }
+
+  //--- convenience methods for call sites that don't have direct access to the VM
+  
+  public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg){
+    return vm.setNextChoiceGenerator(cg);
+  }
+  
+  public boolean hasNextChoiceGenerator(){
+    return vm.hasNextChoiceGenerator();
+  }
+
+  public void checkNextChoiceGeneratorSet (String msg){
+    if (!vm.hasNextChoiceGenerator()){
+      throw new JPFException(msg);
+    }
+  }
+  
+  //--- call processing
+  
+  /**
+   * note - this assumes the stackframe of the method to enter is already initialized and on top (pushed)
+   */
+  public void enter (){
+    MethodInfo mi = top.getMethodInfo();
+
+    if (!mi.isJPFExecutable()){
+      //printStackTrace();
+      throw new JPFException("method is not JPF executable: " + mi);
+    }
+
+    if (mi.isSynchronized()){
+      int oref = mi.isStatic() ?  mi.getClassInfo().getClassObjectRef() : top.getThis();
+      ElementInfo ei = getModifiableElementInfo( oref);
+      
+      ei.lock(this);
+    }
+
+    vm.notifyMethodEntered(this, mi);
+  }
+
+  /**
+   * note - this assumes the stackframe is still on top (not yet popped)
+   * 
+   * return true if any threads became unblocked due to a return from a sync method
+   */
+  public boolean leave(){
+    boolean didUnblock = false;
+    MethodInfo mi = top.getMethodInfo();
+    
+    // <2do> - that's not really enough, we might have suspicious bytecode that fails
+    // to release locks acquired by monitor_enter (e.g. by not having a handler that
+    // monitor_exits & re-throws). That's probably shifted into the bytecode verifier
+    // in the future (i.e. outside JPF), but maybe we should add an explicit test here
+    // and report an error if the code does asymmetric locking (according to the specs,
+    // VMs are allowed to silently fix this, so it might run on some and fail on others)
+    
+    if (mi.isSynchronized()) {
+      int oref = mi.isStatic() ?  mi.getClassInfo().getClassObjectRef() : top.getThis();
+      ElementInfo ei = getElementInfo( oref);
+      if (ei.isLocked()){
+        ei = ei.getModifiableInstance();
+        didUnblock = ei.unlock(this);
+      }
+    }
+
+    vm.notifyMethodExited(this, mi);
+    return didUnblock;
+  }
+
+  
+  /**
+   * this should only be called from the top half of the last DIRECTCALLRETURN of
+   * a thread.
+   * @return true - if the thread is done, false - if instruction has to be re-executed
+   */
+  public boolean exit(){
+    int objref = getThreadObjectRef();
+    ElementInfo ei = getModifiableElementInfo(objref); // we are going to modify it no matter what
+    SystemState ss = vm.getSystemState();
+    Scheduler scheduler = getScheduler();
+
+    enableEmptyTransition();
+    
+    // if this is the last non-daemon and there are only daemons left (which
+    // would be killed by our termination) we have to give them a chance to
+    // run BEFORE we terminate, to catch errors in those daemons we might have
+    // triggered in our last transition. Even if a daemon has a proper CG
+    // on the trigger that would expose the error subsequently, it would not be
+    // scheduled anymore but hard terminated. This is even true if the trigger
+    // is the last operation in the daemon since a host VM might preempt
+    // on every instruction, not just CG insns (see .test.mc.DaemonTest)
+    if (vm.getThreadList().hasOnlyMatchingOtherThan(this, vm.getDaemonRunnablePredicate())) {
+      if (scheduler.setsRescheduleCG(this, "daemonTermination")) {
+        return false;
+      }
+    }
+    
+    // beware - this notifies all waiters for this thread (e.g. in a join())
+    // hence it has to be able to acquire the lock
+    if (!ei.canLock(this)) {
+      // if we can't acquire the lock, it means there needs to be another live thread,
+      // but it might not be runnable (deadlock) and we don't want to mask that error
+      
+      // block first, so that we don't get this thread in the list of CGs
+      ei.block(this);
+      if (!scheduler.setsBlockedThreadCG(this, ei)){
+        throw new JPFException("blocking thread termination without transition break");            
+      }    
+      return false; // come back once we can obtain the lock to notify our waiters
+    }
+      
+    // we have to be able to acquire the group lock since we are going to remove
+    // the thread from the group
+    int grpRef = getThreadGroupRef();
+    ElementInfo eiGrp = getModifiableElementInfo(grpRef);
+    if (eiGrp != null){
+      if (!eiGrp.canLock(this)){
+        eiGrp.block(this);
+        if (!scheduler.setsBlockedThreadCG(this, eiGrp)){
+          throw new JPFException("blocking thread termination on group without transition break");            
+        }    
+        return false; // come back once we can obtain the lock to update the group
+      }
+    }
+
+    // Ok, we can get the lock, time to die
+    
+    // this simulates the ThreadGroup.remove(), which would cause a lot
+    // of states if done in bytecode. Since we don't have a ThreadGroup peer
+    // we keep all this code in ThreadInfo.
+    // This might cause the ThreadGroup to notify waiters, which has to
+    // happen before the notification on the thread object
+    eiGrp.lock(this);
+    cleanupThreadGroup(grpRef, objref);
+    eiGrp.unlock(this);
+    
+    
+    // notify waiters on thread termination
+    if (!holdsLock(ei)) {
+      // we only need to increase the lockcount if we don't own the lock yet,
+      // as is the case for synchronized run() in anonymous threads (otherwise
+      // we have a lockcount > 1 and hence do not unlock upon return)
+      ei.lock(this);
+    }
+
+    ei.notifiesAll(); // watch out, this might change the runnable count
+    ei.unlock(this);
+
+    setState(State.TERMINATED);
+
+    // we don't unregister this thread yet, this happens when the corresponding
+    // thread object is collected
+    ss.clearAtomic();
+    cleanupThreadObject(ei);
+    vm.activateGC();  // stack is gone, so reachability might change
+
+    // give the thread tracking policy a chance to remove this thread from
+    // object/class thread sets
+    scheduler.cleanupThreadTermination(this);
+
+    if (vm.getThreadList().hasAnyMatchingOtherThan(this, getRunnableNonDaemonPredicate())) {
+      if (!scheduler.setsTerminationCG(this)){
+        throw new JPFException("thread termination without transition break");
+      }
+    }
+
+    popFrame(); // we need to do this *after* setting the CG (so that we still have a CG insn)
+
+    return true;
+  }
+
+  Predicate<ThreadInfo> getRunnableNonDaemonPredicate() {
+    return new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (ti.isRunnable() && !ti.isDaemon());
+      }
+    };
+  }
+  
+  /**
+   * this is called upon ThreadInfo.exit() and corresponds to the private Thread.exit()
+   */
+  void cleanupThreadObject (ElementInfo ei) {
+    // ideally, this should be done by calling Thread.exit(), but this
+    // does a ThreadGroup.remove(), which does a lot of sync stuff on the shared
+    // ThreadGroup object, which might create lots of states. So we just nullify
+    // the Thread fields and remove it from the ThreadGroup from here
+    int grpRef = ei.getReferenceField("group");
+    cleanupThreadGroup(grpRef, ei.getObjectRef());
+
+    ei.setReferenceField("group", MJIEnv.NULL);    
+    ei.setReferenceField("threadLocals", MJIEnv.NULL);
+    
+    ei.setReferenceField("uncaughtExceptionHandler", MJIEnv.NULL);
+  }
+
+  /**
+   * this is called upon ThreadInfo.exit() and corresponds to ThreadGroup.remove(t), which is called from Thread.exit()
+   * 
+   * ideally this should be in the ThreadGroup peer, but we don't want to reference concrete peers from core (which is the
+   * lowest layer).
+   * Since we already depend on ThreadGroup fields during VM initialization we just keep all Thread/ThreadGroup
+   * related methods here
+   */
+  void cleanupThreadGroup (int grpRef, int threadRef) {
+    if (grpRef != MJIEnv.NULL) {
+      ElementInfo eiGrp = getModifiableElementInfo(grpRef);
+      int threadsRef = eiGrp.getReferenceField("threads");
+      if (threadsRef != MJIEnv.NULL) {
+        ElementInfo eiThreads = getModifiableElementInfo(threadsRef);
+        if (eiThreads.isArray()) {
+          int nthreads = eiGrp.getIntField("nthreads");
+
+          for (int i=0; i<nthreads; i++) {
+            int tref = eiThreads.getReferenceElement(i);
+
+            if (tref == threadRef) { // compact the threads array
+              int n1 = nthreads-1;
+              for (int j=i; j<n1; j++) {
+                eiThreads.setReferenceElement(j, eiThreads.getReferenceElement(j+1));
+              }
+              eiThreads.setReferenceElement(n1, MJIEnv.NULL);
+
+              eiGrp.setIntField("nthreads", n1);
+              if (n1 == 0) {
+                eiGrp.lock(this);
+                eiGrp.notifiesAll();
+                eiGrp.unlock(this);
+              }
+
+              // <2do> we should probably also check if we have to set it destroyed
+              return;
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * this is called by the VM upon initialization of the main thread. At this point, we have a tiMain and a java.lang.Thread
+   * object, but there is no ThreadGroup yet
+   * 
+   * This method is here to keep all Thread/ThreadGroup field dependencies in one place. The downside of not keeping this in
+   * VM is that we can't override in order to have specialized ThreadInfos, but there is no factory for them anyways
+   */
+  protected void createMainThreadObject (SystemClassLoaderInfo sysCl){
+    //--- now create & initializeSharednessPolicy all the related JPF objects
+    Heap heap = getHeap();
+
+    ClassInfo ciThread = sysCl.threadClassInfo;
+    ElementInfo eiThread = heap.newObject( ciThread, this);
+    objRef = eiThread.getObjectRef();
+     
+    ElementInfo eiName = heap.newString(MAIN_NAME, this);
+    int nameRef = eiName.getObjectRef();
+    eiThread.setReferenceField("name", nameRef);
+    
+    ElementInfo eiGroup = createMainThreadGroup(sysCl);
+    eiThread.setReferenceField("group", eiGroup.getObjectRef());
+    
+    eiThread.setIntField("priority", Thread.NORM_PRIORITY);
+
+    ClassInfo ciPermit = sysCl.getResolvedClassInfo("java.lang.Thread$Permit");
+    ElementInfo eiPermit = heap.newObject( ciPermit, this);
+    eiPermit.setBooleanField("blockPark", true);
+    eiThread.setReferenceField("permit", eiPermit.getObjectRef());
+
+    addToThreadGroup(eiGroup);
+    
+    addId( objRef, id);
+
+    //--- set the thread running
+    setState(ThreadInfo.State.RUNNING);
+  }
+  
+
+  /**
+   * this creates and inits the main ThreadGroup object, which we have to do explicitly since
+   * we can't execute bytecode yet
+   */
+  protected ElementInfo createMainThreadGroup (SystemClassLoaderInfo sysCl) {
+    Heap heap = getHeap();
+    
+    ClassInfo ciGroup = sysCl.getResolvedClassInfo("java.lang.ThreadGroup");
+    ElementInfo eiThreadGrp = heap.newObject( ciGroup, this);
+
+    ElementInfo eiGrpName = heap.newString("main", this);
+    eiThreadGrp.setReferenceField("name", eiGrpName.getObjectRef());
+
+    eiThreadGrp.setIntField("maxPriority", java.lang.Thread.MAX_PRIORITY);
+
+    // 'threads' and 'nthreads' will be set later from createMainThreadObject
+
+    return eiThreadGrp;
+  }
+
+  /**
+   * this is used for all thread objects, not just main 
+   */
+  protected void addToThreadGroup (ElementInfo eiGroup){
+    FieldInfo finThreads = eiGroup.getFieldInfo("nthreads");
+    int nThreads = eiGroup.getIntField(finThreads);
+    
+    if (eiGroup.getBooleanField("destroyed")){
+      env.throwException("java.lang.IllegalThreadStateException");
+      
+    } else {
+      FieldInfo fiThreads = eiGroup.getFieldInfo("threads");
+      int threadsRef = eiGroup.getReferenceField(fiThreads);
+      
+      if (threadsRef == MJIEnv.NULL){
+        threadsRef = env.newObjectArray("Ljava/lang/Thread;", 1);
+        env.setReferenceArrayElement(threadsRef, 0, objRef);
+        eiGroup.setReferenceField(fiThreads, threadsRef);
+        
+      } else {
+        int newThreadsRef = env.newObjectArray("Ljava/lang/Thread;", nThreads+1);
+        ElementInfo eiNewThreads = env.getElementInfo(newThreadsRef);        
+        ElementInfo eiThreads = env.getElementInfo(threadsRef);
+        
+        for (int i=0; i<nThreads; i++){
+          int tr = eiThreads.getReferenceElement(i);
+          eiNewThreads.setReferenceElement(i, tr);
+        }
+        
+        eiNewThreads.setReferenceElement(nThreads, objRef);
+        eiGroup.setReferenceField(fiThreads, newThreadsRef);
+      }
+      
+      eiGroup.setIntField(finThreads, nThreads+1);
+      
+      /** <2do> we don't model this yet
+      FieldInfo finUnstartedThreads = eiGroup.getFieldInfo("nUnstartedThreads");
+      int nUnstarted = eiGroup.getIntField(finUnstartedThreads);
+      eiGroup.setIntField(finUnstartedThreads, nUnstarted-1);
+      **/
+    }    
+  }
+  
+  
+  public void hash (HashData hd) {
+    threadData.hash(hd);
+
+    for (StackFrame f = top; f != null; f = f.getPrevious()){
+      f.hash(hd);
+    }
+  }
+
+  public void interrupt () {
+    ElementInfo eiThread = getModifiableElementInfo(getThreadObjectRef());
+
+    State status = getState();
+
+    switch (status) {
+    case RUNNING:
+    case BLOCKED:
+    case UNBLOCKED:
+    case NOTIFIED:
+    case TIMEDOUT:
+      // just set interrupt flag
+      eiThread.setBooleanField("interrupted", true);
+      break;
+
+    case WAITING:
+    case TIMEOUT_WAITING:
+      eiThread.setBooleanField("interrupted", true);
+      setState(State.INTERRUPTED);
+
+      // since this is potentially called w/o owning the wait lock, we
+      // have to check if the waiter goes directly to UNBLOCKED
+      ElementInfo eiLock = getElementInfo(lockRef);
+      if (eiLock.canLock(this)) {
+        resetLockRef();
+        setState(State.UNBLOCKED);
+        
+        // we cannot yet remove this thread from the Monitor lock contender list
+        // since it still has to re-acquire the lock before becoming runnable again
+        
+        // NOTE: this can lead to attempts to reenter the same thread to the 
+        // lock contender list if the interrupt handler of the interrupted thread
+        // tries to wait/block/park again
+        //eiLock.setMonitorWithoutLocked(this);
+      }
+      
+      break;
+
+    case NEW:
+    case TERMINATED:
+      // ignore
+      break;
+
+    default:
+    }
+  }
+
+  /**
+   * mark all objects during gc phase1 which are reachable from this threads
+   * root set (Thread object, Runnable, stack)
+   * @aspects: gc
+   */
+  void markRoots (Heap heap) {
+    
+    // 1. mark the Thread object itself
+    heap.markThreadRoot(objRef, id);
+
+    // 2. and its runnable
+    if (targetRef != MJIEnv.NULL) {
+      heap.markThreadRoot(targetRef,id);
+    }
+
+    // 3. if we have a pending exception that wasn't handled, make sure the exception object is not collected before we match states
+    if (pendingException != null){
+      heap.markThreadRoot(pendingException.getExceptionReference(), id);
+    }
+    
+    // 4. now all references on the stack
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      frame.markThreadRoots(heap, id);
+    }
+  }
+
+
+  /**
+   * replace the top frame - this is a dangerous method that should only
+   * be used from Restoreres and to restore operators and locals in post-execution notifications
+   * to their pre-execution contents
+   */
+  public void setTopFrame (StackFrame frame) {
+    top = frame;
+
+    // since we have swapped the top frame, the stackDepth might have changed
+    int n = 0;
+    for (StackFrame f = frame; f != null; f = f.getPrevious()){
+      n++;
+    }
+    stackDepth = n;
+  }
+
+  /**
+   * Adds a new stack frame for a new called method.
+   */
+  public void pushFrame (StackFrame frame) {
+
+    frame.setPrevious(top);
+
+    top = frame;
+    stackDepth++;
+
+    // a new frame cannot have been stored yet, so we don't need to clone on the next mod
+    // note this depends on not pushing a frame in the top half of a CG method
+    markTfChanged(top);
+
+    returnedDirectCall = null;
+  }
+
+  /**
+   * Removes a stack frame
+   */
+  public void popFrame() {
+    StackFrame frame = top;
+
+    //--- do our housekeeping
+    if (frame.hasAnyRef()) {
+      vm.getSystemState().activateGC();
+    }
+
+    // there always is one since we start all threads through directcalls
+    top = frame.getPrevious();
+    stackDepth--;
+  }
+
+  public StackFrame popAndGetModifiableTopFrame() {
+    popFrame();
+
+    if (top.isFrozen()) {
+      top = top.clone();
+    }
+    
+    return top;
+  }
+  
+  public StackFrame popAndGetTopFrame(){
+    popFrame();
+    return top;
+  }
+  
+  /**
+   * removing DirectCallStackFrames is a bit different (only happens from
+   * DIRECTCALLRETURN insns)
+   */
+  public StackFrame popDirectCallFrame() {
+    //assert top instanceof DirectCallStackFrame;
+
+    returnedDirectCall = (DirectCallStackFrame) top;
+
+    if (top.hasFrameAttr( UncaughtHandlerAttr.class)){
+      return popUncaughtHandlerFrame();
+    }
+    
+    top = top.getPrevious();
+    stackDepth--;
+    
+    return top;
+  }
+
+  
+  public boolean hasReturnedFromDirectCall () {
+    // this is reset each time we push a new frame
+    return (returnedDirectCall != null);
+  }
+
+  public boolean hasReturnedFromDirectCall(String directCallId){
+    return (returnedDirectCall != null &&
+            returnedDirectCall.getMethodName().equals(directCallId));
+  }
+
+  public DirectCallStackFrame getReturnedDirectCall () {
+    return returnedDirectCall;
+  }
+
+
+  public String getStateDescription () {
+    StringBuilder sb = new StringBuilder("thread ");
+    sb.append(getThreadObjectClassInfo().getName());
+    sb.append(":{id:");
+    sb.append(id);
+    sb.append(',');
+    sb.append(threadData.getFieldValues());
+    sb.append('}');
+    
+    return sb.toString();
+  }
+
+  public ClassInfo getThreadObjectClassInfo() {
+    return getThreadObject().getClassInfo();
+  }
+  
+  /**
+   * Prints the content of the stack
+   */
+  public void printStackContent () {
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      frame.printStackContent();
+    }
+  }
+
+  /**
+   * Prints current stacktrace information
+   */
+  public void printStackTrace () {
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()){
+      frame.printStackTrace();
+    }
+  }
+
+  protected boolean haltOnThrow (String exceptionClassName){
+    if ((haltOnThrow != null) && (haltOnThrow.matchesAny(exceptionClassName))){
+      return true;
+    }
+
+    return false;
+  }
+
+  protected Instruction throwStopException (){
+    // <2do> maybe we should do a little sanity check first
+    ElementInfo ei = getModifiableThreadObject();
+
+    int xRef = ei.getReferenceField("stopException");
+    ei.setReferenceField("stopException", MJIEnv.NULL);
+    attributes &= ~ATTR_SET_STOPPED;  // otherwise we would throw again if thread gets still executed
+
+    Instruction insn = getPC();
+    if (insn instanceof EXECUTENATIVE){
+      // we only get here if there was a CG in a native method and we might
+      // have to reacquire a lock to go on
+
+      // <2do> it would be better if we could avoid to enter the native method
+      // since it might have side effects like overwriting the exception or
+      // doing roundtrips in its bottom half, but we don't know which lock that
+      // is (lockRef might be already reset)
+
+      env.throwException(xRef);
+      return insn;
+    }
+
+    return throwException(xRef);
+  }
+  
+  /**
+   * this is basically a side-effect free version of throwException to determine if a given
+   * exception will be handled.
+   */
+  public HandlerContext getHandlerContextFor (ClassInfo ciException){
+    ExceptionHandler matchingHandler = null; // the matching handler we found (if any)
+    
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
+      // that means we have to turn the exception into an InvocationTargetException
+      if (frame.isReflection()) {
+        ciException = ClassInfo.getInitializedSystemClassInfo("java.lang.reflect.InvocationTargetException", this);
+      }
+
+      matchingHandler = frame.getHandlerFor( ciException);
+      if (matchingHandler != null){
+        return new HandlerContext( this, ciException, frame, matchingHandler);
+      }
+    }
+    
+    if (!ignoreUncaughtHandlers && !isUncaughtHandlerOnStack()) {
+      int uchRef;
+      if ((uchRef = getInstanceUncaughtHandler()) != MJIEnv.NULL) {
+        return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.INSTANCE, uchRef);
+      }
+
+      int grpRef = getThreadGroupRef();
+      if ((uchRef = getThreadGroupUncaughtHandler(grpRef)) != MJIEnv.NULL) {
+        return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.GROUP, uchRef);
+      }
+
+      if ((uchRef = getGlobalUncaughtHandler()) != MJIEnv.NULL) {
+        return new HandlerContext( this, ciException, HandlerContext.UncaughtHandlerType.GLOBAL, uchRef);
+      }
+    }
+    
+    return null;
+  }
+  
+  /**
+   * unwind stack frames until we find a matching handler for the exception object
+   */
+  public Instruction throwException (int exceptionObjRef) {
+    Heap heap = vm.getHeap();
+    ElementInfo eiException = heap.get(exceptionObjRef);
+    ClassInfo ciException = eiException.getClassInfo();
+    String exceptionName = ciException.getName();
+    StackFrame handlerFrame = null; // the stackframe that has a matching handler (if any)
+    ExceptionHandler matchingHandler = null; // the matching handler we found (if any)
+
+    // first, give the VM a chance to intercept (we do this before changing anything)
+    Instruction insn = vm.handleException(this, exceptionObjRef);
+    if (insn != null){
+      return insn;
+    }
+
+    // we don't have to store the stacktrace explicitly anymore, since that is now
+    // done in the Throwable ctor (more specifically the native fillInStackTrace)
+    pendingException = new ExceptionInfo(this, eiException);
+
+    vm.notifyExceptionThrown(this, eiException);
+
+    if (haltOnThrow(exceptionName)) {
+      // shortcut - we don't try to find a handler for this one but bail immediately
+      //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
+      throw new UncaughtException(this, exceptionObjRef);
+    }
+
+    // check if we find a matching handler, and if we do store it. Leave the
+    // stack untouched so that listeners can still inspect it
+    for (StackFrame frame = top; (frame != null) && (handlerFrame == null); frame = frame.getPrevious()) {
+      // that means we have to turn the exception into an InvocationTargetException
+      if (frame.isReflection()) {
+        // make sure it's in the startup class set since we are not able to re-execute here
+        ciException = ClassInfo.getInitializedSystemClassInfo("java.lang.reflect.InvocationTargetException", this);
+        exceptionObjRef  = createException(ciException, exceptionName, exceptionObjRef);
+        exceptionName = ciException.getName();
+        eiException = heap.get(exceptionObjRef);
+        pendingException = new ExceptionInfo(this, eiException);
+      }
+
+      matchingHandler = frame.getHandlerFor( ciException);
+      if (matchingHandler != null){
+        handlerFrame = frame;
+        break;
+      }
+
+      if ((handlerFrame == null) && frame.isFirewall()) {
+        // this method should not let exceptionHandlers pass into lower level stack frames
+        // (e.g. for <clinit>, or hidden direct calls)
+        // <2do> if this is a <clinit>, we should probably turn into an
+        // ExceptionInInitializerError first
+        unwindTo(frame);
+        //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
+        throw new UncaughtException(this, exceptionObjRef);
+      }
+    }
+
+    if (handlerFrame == null) {
+      // we still have to check if there is a Thread.UncaughtExceptionHandler in effect,
+      // and if we already enter within one, in which case we don't reenter it
+      if (!ignoreUncaughtHandlers && !isUncaughtHandlerOnStack()) {
+        // we use a direct call instead of exception handlers within the run()/main()
+        // direct call methods because we want to preserve the whole stack in case
+        // we treat returned (report-only) handlers as NoUncaughtExceptionProperty
+        // violations (passUncaughtHandler=false)
+        insn = callUncaughtHandler(pendingException);
+        if (insn != null) {
+          // we only do this if there is a UncaughtHandler other than the standard
+          // ThreadGroup, hence we have to check for the return value. If there is
+          // only ThreadGroup.uncaughtException(), we put the system out of its misery
+          return insn;
+        }
+      }
+
+      // there was no overridden uncaughtHandler, or we already executed it
+      if ("java.lang.ThreadDeath".equals(exceptionName)) { // gracefully shut down
+        unwindToFirstFrame();
+        pendingException = null;
+        return top.getPC().getNext(); // the final DIRECTCALLRETURN
+
+      } else { // we have a NoUncaughtPropertyViolation
+        //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
+        throw new UncaughtException(this, exceptionObjRef);
+      }
+
+    } else { // we found a matching handler
+      
+      unwindTo(handlerFrame);
+
+      // according to the VM spec, before transferring control to the handler we have
+      // to reset the operand stack to contain only the exception reference
+      // (4.9.2 - "4. merge the state of the operand stack..")
+      handlerFrame = getModifiableTopFrame();
+      handlerFrame.setExceptionReference(exceptionObjRef);
+
+      // jump to the exception handler and set pc so that listeners can see it
+      int handlerOffset = matchingHandler.getHandler();
+      insn = handlerFrame.getMethodInfo().getInstructionAt(handlerOffset);
+      handlerFrame.setPC(insn);
+
+      // notify before we reset the pendingException
+      vm.notifyExceptionHandled(this);
+
+      pendingException = null; // handled, no need to keep it
+
+      return insn;
+    }
+  }
+
+  /**
+   * is there any UncaughHandler in effect for this thread?
+   * NOTE - this doesn't check if we are already executing one (i.e. it would still handle an exception)
+   * or if uncaughtHandlers are enabled within JPF
+   */
+  public boolean hasUncaughtHandler (){
+    if (getInstanceUncaughtHandler() != MJIEnv.NULL){
+      return true;
+    }
+    
+    int grpRef = getThreadGroupRef();
+    if (getThreadGroupUncaughtHandler(grpRef) != MJIEnv.NULL){
+      return true;
+    }
+    
+    if (getGlobalUncaughtHandler() != MJIEnv.NULL){
+      return true;
+    }
+    
+    return false;
+  }
+  
+  /**
+   * this explicitly models the standard ThreadGroup.uncaughtException(), but we want
+   * to save us a roundtrip if that's the only handler we got. If we would use
+   * a handler block in the run()/main() direct call stubs that just delegate to
+   * the standard ThreadGroup.uncaughtException(), we would have trouble mapping
+   * this to NoUncaughtExceptionProperty violations (which is just a normal
+   * printStackTrace() in there).
+   */
+  protected Instruction callUncaughtHandler (ExceptionInfo xi){
+    Instruction insn = null;
+    
+    // 1. check if this thread has its own uncaughtExceptionHandler set. If not,
+    // hand it over to ThreadGroup.uncaughtException()
+    int  hRef = getInstanceUncaughtHandler();
+    if (hRef != MJIEnv.NULL){
+      insn = callUncaughtHandler(xi, hRef, "[threadUncaughtHandler]");
+      
+    } else {
+      // 2. check if any of the ThreadGroup chain has an overridden uncaughtException
+      int grpRef = getThreadGroupRef();
+      hRef = getThreadGroupUncaughtHandler(grpRef);
+      
+      if (hRef != MJIEnv.NULL){
+        insn = callUncaughtHandler(xi, hRef, "[threadGroupUncaughtHandler]");
+      
+      } else {
+        // 3. as a last measure, check if there is a global handler 
+        hRef = getGlobalUncaughtHandler();
+        if (hRef != MJIEnv.NULL){
+          insn = callUncaughtHandler(xi, hRef, "[globalUncaughtHandler]");
+        }    
+      }
+    }
+    
+    return insn;
+  }
+  
+  protected boolean isUncaughtHandlerOnStack(){
+    for (StackFrame frame = top; frame != null; frame = frame.getPrevious()) {
+      if (frame.hasFrameAttr(UncaughtHandlerAttr.class)){
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  protected int getInstanceUncaughtHandler (){
+    ElementInfo ei = getElementInfo(objRef);
+    int handlerRef = ei.getReferenceField("uncaughtExceptionHandler");
+    return handlerRef;
+  }
+  
+  protected int getThreadGroupRef() {
+    ElementInfo ei = getElementInfo(objRef);
+    int groupRef = ei.getReferenceField("group");
+    return groupRef;
+  }
+  
+  protected int getThreadGroupUncaughtHandler (int grpRef){
+
+    // get the first overridden uncaughtException() implementation in the group chain
+    while (grpRef != MJIEnv.NULL){
+      ElementInfo eiGrp = getElementInfo(grpRef);
+      ClassInfo ciGrp = eiGrp.getClassInfo();
+      MethodInfo miHandler = ciGrp.getMethod("uncaughtException(Ljava/lang/Thread;Ljava/lang/Throwable;)V", true);
+      ClassInfo ciHandler = miHandler.getClassInfo();
+      if (!ciHandler.getName().equals("java.lang.ThreadGroup")) {
+        return eiGrp.getObjectRef();
+      }
+
+      grpRef = eiGrp.getReferenceField("parent");
+    }
+    
+    // no overridden uncaughtHandler found
+    return MJIEnv.NULL;
+  }
+  
+  protected int getGlobalUncaughtHandler(){
+    ElementInfo ei = getElementInfo(objRef);
+    ClassInfo ci = ei.getClassInfo();
+    FieldInfo fi = ci.getStaticField("defaultUncaughtExceptionHandler");
+    
+    // the field is in our java.lang.Thread, but the concrete thread object class might differ
+    ClassInfo fci = fi.getClassInfo();
+    return fci.getStaticElementInfo().getReferenceField(fi);
+  }
+  
+  /**
+   * using an attribute to mark DirectCallStackFrames executing uncaughtHandlers is not
+   * ideal, but the case is so exotic that we don't want another concrete StackFrame subclass that
+   * would have to be created through the ClassInfo factory. Apart from retrieving the 
+   * ExceptionInfo this is just a normal DirectCallStackFrame.
+   * We could directly use ExceptionInfo, but it seems more advisable to have a dedicated,
+   * private type. This could also store execution state
+   */
+  class UncaughtHandlerAttr implements SystemAttribute {
+    ExceptionInfo xInfo;
+    
+    UncaughtHandlerAttr (ExceptionInfo xInfo){
+      this.xInfo = xInfo;
+    }
+    
+    ExceptionInfo getExceptionInfo(){
+      return xInfo;
+    }
+  }
+  
+  protected Instruction callUncaughtHandler (ExceptionInfo xi, int handlerRef, String id){
+    ElementInfo eiHandler = getElementInfo(handlerRef);
+    ClassInfo ciHandler = eiHandler.getClassInfo();
+    MethodInfo miHandler = ciHandler.getMethod("uncaughtException(Ljava/lang/Thread;Ljava/lang/Throwable;)V", true);
+
+    // we have to clear this here in case there is a CG while executing the uncaughtHandler
+    pendingException = null;
+    
+    DirectCallStackFrame frame = miHandler.createDirectCallStackFrame(this, 0);
+    int argOffset = frame.setReferenceArgument( 0, handlerRef, null);
+    argOffset = frame.setReferenceArgument( argOffset, objRef, null);
+    frame.setReferenceArgument( argOffset, xi.getExceptionReference(), null);
+    
+    UncaughtHandlerAttr uchContext = new UncaughtHandlerAttr( xi);
+    frame.setFrameAttr( uchContext);
+    
+    pushFrame(frame);
+    return frame.getPC();
+  }
+  
+  protected StackFrame popUncaughtHandlerFrame(){    
+    // we return from an overridden uncaughtException() direct call, but
+    // its debatable if this counts as 'handled'. For handlers that just do
+    // reporting this is probably false and we want JPF to report the defect.
+    // If this is a fail-safe handler that tries to clean up so that other threads can
+    // take over, we want to be able to go on and properly shut down the 
+    // thread without property violation
+    
+    if (passUncaughtHandler) {
+      // gracefully shutdown this thread
+      unwindToFirstFrame(); // this will take care of notifying
+      
+      getModifiableTopFrame().advancePC();
+      assert top.getPC() instanceof ReturnInstruction : "topframe PC not a ReturnInstruction: " + top.getPC();
+      return top;
+
+    } else {
+      // treat this still as an NoUncaughtExceptionProperty violation
+      UncaughtHandlerAttr ctx = returnedDirectCall.getFrameAttr(UncaughtHandlerAttr.class);
+      pendingException = ctx.getExceptionInfo();
+      //NoUncaughtExceptionsProperty.setExceptionInfo(pendingException);
+      throw new UncaughtException(this, pendingException.getExceptionReference());
+    }
+  }
+
+  
+  protected void unwindTo (StackFrame newTopFrame){
+    for (StackFrame frame = top; (frame != null) && (frame != newTopFrame); frame = frame.getPrevious()) {
+      leave(); // that takes care of releasing locks
+      vm.notifyExceptionBailout(this); // notify before we pop the frame
+      popFrame();
+    }
+  }
+
+  protected StackFrame unwindToFirstFrame(){
+    StackFrame frame;
+
+    for (frame = top; frame.getPrevious() != null; frame = frame.getPrevious()) {
+      leave(); // that takes care of releasing locks
+      vm.notifyExceptionBailout(this); // notify before we pop the frame
+      popFrame();
+    }
+
+    return frame;
+  }
+
+  public ExceptionInfo getPendingException () {
+    return pendingException;
+  }
+
+  /**
+   * watch out - just clearing it might cause an infinite loop
+   * if we don't drop frames and/or advance the pc
+   */
+  public void clearPendingException () {
+    //NoUncaughtExceptionsProperty.setExceptionInfo(null);
+    pendingException = null;
+  }
+
+  /**
+   * Returns a clone of the thread data. To be called every time we change some ThreadData field
+   * (which unfortunately includes lock counts, hence this should be changed)
+   */
+  protected ThreadData threadDataClone () {
+    if ((attributes & ATTR_DATA_CHANGED) != 0) {
+      // already cloned, so we don't have to clone
+    } else {
+      // reset, so that next storage request would recompute tdIndex
+      markTdChanged();
+      vm.kernelStateChanged();
+
+      threadData = threadData.clone();
+    }
+
+    return threadData;
+  }
+
+  public void restoreThreadData(ThreadData td) {
+    threadData = td;
+  }
+
+
+  /**
+   * this is a generic request to reschedule that is not based on instruction type
+   * Note that we check for a mandatory CG, i.e. the policy has to return a CG to make sure
+   * there is a transition break. We still go through the policy object for selection
+   * of scheduling choices though
+   * 
+   */
+  public boolean reschedule (String reason){
+    return getScheduler().setsRescheduleCG(this, reason);
+  }
+  
+  /**
+   * this is a version that unconditionally breaks the current transition
+   * without really adding choices. It only goes on with the same thread
+   * (to avoid state explosion). Since we require a break, and there is no
+   * choice (current thread is supposed to continue), there is no point 
+   * going through the SyncPolicy
+   *
+   * if the current transition is already marked as ignored, this method does nothing
+   */
+  public boolean breakTransition(String reason) {
+    SystemState ss = vm.getSystemState();
+
+    // no need to set a CG if this transition is already marked as ignored
+    // (which will also break executeTransition)
+    BreakGenerator cg = new BreakGenerator(reason, this, false);
+    return ss.setNextChoiceGenerator(cg); // this breaks the transition
+  }
+  
+  /**
+   * this breaks the current transition with a CG that forces an end state (i.e.
+   * has no choices)
+   * this only takes effect if the current transition is not already marked
+   * as ignored
+   */
+  public boolean breakTransition(boolean isTerminator) {
+    SystemState ss = vm.getSystemState();
+
+    if (!ss.isIgnored()){
+      BreakGenerator cg = new BreakGenerator( "breakTransition", this, isTerminator);
+      return ss.setNextChoiceGenerator(cg); // this breaks the transition
+    }
+    
+    return false;
+  }
+
+  public boolean hasOtherRunnables () {
+    return vm.getThreadList().hasAnyMatchingOtherThan(this, vm.getRunnablePredicate());
+  }
+  
+  protected void markUnchanged() {
+    attributes &= ~ATTR_ANY_CHANGED;
+  }
+
+  protected void markTfChanged(StackFrame frame) {
+    attributes |= ATTR_STACK_CHANGED;
+    vm.kernelStateChanged();
+  }
+
+  protected void markTdChanged() {
+    attributes |= ATTR_DATA_CHANGED;
+    vm.kernelStateChanged();
+  }
+
+  public StackFrame getCallerStackFrame() {
+    if (top != null){
+      return top.getPrevious();
+    } else {
+      return null;
+    }
+  }
+
+  public int mixinExecutionStateHash(int h) {
+    for (StackFrame frame = top; frame != null; frame = frame.prev) {
+      if (!frame.isNative()) {
+        h = frame.mixinExecutionStateHash(h);
+      }
+    }
+    
+    return h;
+  }
+  
+  public boolean hasDataChanged() {
+    return (attributes & ATTR_DATA_CHANGED) != 0;
+  }
+
+  public boolean hasStackChanged() {
+    return (attributes & ATTR_STACK_CHANGED) != 0;
+  }
+
+  public boolean hasChanged() {
+    return (attributes & ATTR_ANY_CHANGED) != 0;
+  }
+
+  /**
+   * Returns a clone of the top stack frame.
+   */
+  public StackFrame getModifiableTopFrame () {
+    if (top.isFrozen()) {
+      top = top.clone();
+      markTfChanged(top);
+    }
+    return top;
+  }
+
+  /**
+   * Returns the top stack frame.
+   */
+  public StackFrame getTopFrame () {
+    return top;
+  }
+
+  /**
+   * this replaces all frames up from 'frame' to 'top' with modifiable ones.
+   * 
+   * NOTE - you can't use this AFTER getModifiableTopFrame() since it changes top. Because
+   * it is inherently unsafe, it should be used with care and you have to make sure nothing
+   * else has stored top references, or respective references are updated after this call.
+   * 
+   * NOTE also that we assume there is no frozen frame on top of an unfrozen one
+   * <2do> that should probably be reported as an error since it is a stack consistency violation
+   */
+  public StackFrame getModifiableFrame (StackFrame frame){
+    StackFrame newTop = null;
+    StackFrame last = null;
+    boolean done = false;
+    
+    for (StackFrame f = top; f != null; f = f.getPrevious()){
+      done = (f == frame);
+      
+      if (f.isFrozen()){
+        f = f.clone();
+        if (newTop == null){
+          newTop = f;
+        } else {
+          last.setPrevious(f);
+        }
+        last = f;
+        
+      }
+      
+      if (done){ // done
+        if (newTop != null){
+          top = newTop;
+          markTfChanged(top);
+        }
+        return f;
+      }
+    }
+    
+    return null; // it wasn't on the callstack
+  }
+  
+  /**
+   * note that we don't provide a modifiable version of this. All modifications
+   * should only happen in the executing (top) frame
+   */
+  public StackFrame getStackFrameExecuting (Instruction insn, int offset){
+    int n = offset;
+    StackFrame frame = top;
+
+    for (; (n > 0) && frame != null; frame = frame.getPrevious()){
+      n--;
+    }
+
+    for(; frame != null; frame = frame.getPrevious()){
+      if (frame.getPC() == insn){
+        return frame;
+      }
+    }
+
+    return null;
+  }
+
+  @Override
+  public String toString() {
+    return "ThreadInfo [name=" + getName() +
+            ",id=" + id +
+            ",state=" + getStateName() +
+            // ",obj=" + Integer.toHexString(getThreadObjectRef()) +
+            ']';
+  }
+
+  void setDaemon (boolean isDaemon) {
+    threadDataClone().isDaemon = isDaemon;
+  }
+
+  public boolean isDaemon () {
+    return threadData.isDaemon;
+  }
+
+  public MJIEnv getMJIEnv () {
+    return env;
+  }
+  
+  void setName (String newName) {
+    threadDataClone().name = newName;
+
+    // see 'setPriority()', only that it's more serious here, because the
+    // java.lang.Thread name is stored as a char[]
+  }
+
+  public void setPriority (int newPrio) {
+    if (threadData.priority != newPrio) {
+      threadDataClone().priority = newPrio;
+
+      // note that we don't update the java.lang.Thread object, but
+      // use our threadData value (which works because the object
+      // values are just used directly from the Thread ctors (from where we pull
+      // it out in our ThreadInfo ctor), and henceforth only via our intercepted
+      // native getters
+    }
+  }
+
+  public int getPriority () {
+    return threadData.priority;
+  }
+
+  /**
+   * Comparison for sorting based on index.
+   */
+  @Override
+  public int compareTo (ThreadInfo that) {
+    return this.id - that.id;
+  }
+  
+  
+  /**
+   * only for debugging purposes
+   */
+  public void checkConsistency(boolean isStore){
+    checkAssertion(threadData != null, "no thread data");
+    
+    // if the thread is runnable, it can't be blocked
+    if (isRunnable()){
+      checkAssertion(lockRef == MJIEnv.NULL, "runnable thread with non-null lockRef: " + lockRef) ;
+    }
+    
+    // every thread that has been started and is not terminated has to have a stackframe with a next pc
+    if (!isTerminated() && !isNew()){
+      checkAssertion( stackDepth > 0, "empty stack " + getState());
+      checkAssertion( top != null, "no top frame");
+      checkAssertion( top.getPC() != null, "no top PC");
+    }
+    
+    // if we are timedout, the top pc has to be either on a native Object.wait() or Unsafe.park()
+    if (isTimedOut()){
+      Instruction insn = top.getPC();
+      checkAssertion( insn instanceof EXECUTENATIVE, "thread timedout outside of native method");
+      
+      // this is a bit dangerous in case we introduce new timeout points
+      MethodInfo mi = ((EXECUTENATIVE)insn).getExecutedMethod();
+      String mname = mi.getUniqueName();
+      checkAssertion( mname.equals("wait(J") || mname.equals("park(ZJ"), "timedout thread outside timeout method: " + mi.getFullName());
+    }
+  
+    List<ElementInfo> lockedObjects = getLockedObjects();
+    
+    if (lockRef != MJIEnv.NULL){
+      // object we are blocked on has to exist
+      ElementInfo ei = this.getElementInfo(lockRef);
+      checkAssertion( ei != null, "thread locked on non-existing object: " + lockRef);
+      
+      // we have to be in the lockedThreads list of that objects monitor
+      checkAssertion( ei.isLocking(this), "thread blocked on non-locking object: " + ei);
+        
+      // can't be blocked on a lock we own (but could be in waiting before giving it up)
+      if (!isWaiting() && lockedObjectReferences.length > 0){
+        for (ElementInfo lei : lockedObjects){
+            checkAssertion( lei.getObjectRef() != lockRef, "non-waiting thread blocked on owned lock: " + lei);
+        }
+      }
+      
+      // the state has to be BLOCKED, NOTIFIED, WAITING or TIMEOUT_WAITING
+      checkAssertion( isWaiting() || isBlockedOrNotified(), "locked thread not waiting, blocked or notified");
+      
+    } else { // no lockRef set
+      checkAssertion( !isWaiting() && !isBlockedOrNotified(), "non-locked thread is waiting, blocked or notified");
+    }
+    
+    // if we have locked objects, we have to be the locking thread of each of them
+    if (lockedObjects != null && !lockedObjects.isEmpty()){
+      for (ElementInfo ei : lockedObjects){
+        ThreadInfo lti = ei.getLockingThread();
+        if (lti != null){
+          checkAssertion(lti == this, "not the locking thread for locked object: " + ei + " owned by " + lti);
+        } else {
+          // can happen for a nested monitor lockout
+        }
+      }
+    }
+
+  }
+  
+  protected void checkAssertion(boolean cond, String failMsg){
+    if (!cond){
+      System.out.println("!!!!!! failed thread consistency: "  + this + ": " + failMsg);
+      vm.dumpThreadStates();
+      assert false;
+    }
+  }
+  
+  public boolean isSystemThread() {
+    return false;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThreadInfoSet.java b/src/main/gov/nasa/jpf/vm/ThreadInfoSet.java
new file mode 100644 (file)
index 0000000..c9b71a2
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface to abstract the referencing set of threadinfos per object/class
+ * Used to detect shared objects/classes 
+ * Instances are created through a configured factory (SharedObjectPolicy)
+ * 
+ * We abstract the container so that the way we identify threads is not exposed
+ * to the client, and implementations can use either ThreadInfo references or
+ * global ids.
+ */
+public interface ThreadInfoSet extends Cloneable {
+
+  /**
+   * @return true if the thread wasn't in the set yet and was added
+   */
+  ThreadInfoSet add (ThreadInfo ti);
+  
+  ThreadInfoSet remove (ThreadInfo ti);
+  
+  boolean contains (ThreadInfo ti);
+  
+  boolean isShared (ThreadInfo ti, ElementInfo ei);
+  
+  
+  boolean hasMultipleLiveThreads ();
+  boolean hasMultipleRunnableThreads ();
+  
+  Memento<ThreadInfoSet> getMemento();
+  
+  int size();
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThreadList.java b/src/main/gov/nasa/jpf/vm/ThreadList.java
new file mode 100644 (file)
index 0000000..f262001
--- /dev/null
@@ -0,0 +1,617 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.HashData;
+import gov.nasa.jpf.util.Predicate;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.NoSuchElementException;
+
+/**
+ * Contains the list of all ThreadInfos for live java.lang.Thread objects
+ * 
+ * We add a thread upon creation (within the ThreadInfo ctor), and remove it
+ * when the corresponding java.lang.Thread object gets recycled by JPF. This means
+ * that:
+ *   * the thread list can contain terminated threads
+ *   * terminated and recycled threads are (eventually) removed from the list
+ *   * the list can shrink along a given path
+ *   * thread ids don't have to correspond with storing order !!
+ *   * thread ids can be re-used
+ *
+ * Per default, thread ids are re-used in order to be packed (which is required to efficiently
+ * keep track of referencing threads in ElementInfo reftids). If there is a need
+ * to avoid recycled thread ids, set 'vm.reuse_tid=false'.
+ * 
+ * NOTE - this ThreadList implementation doubles up as a thread object -> ThreadInfo
+ * map, which is for instance heavily used by the JPF_java_lang_Thread peer.
+ * 
+ * This implies that ThreadList is still not fully re-organized in case something
+ * keeps terminated thread objects alive. We could avoid this by having a separate
+ * map for live threads<->ThreadInfos, but this would also have to be a backrackable
+ * container that is highly redundant to ThreadList (the only difference being
+ * that terminated threads could be removed from ThreadList).
+ * 
+ */
+public class ThreadList implements Cloneable, Iterable<ThreadInfo>, Restorable<ThreadList> {
+
+  public static class Count {
+    public final int alive;
+    public final int runnableNonDaemons;
+    public final int runnableDaemons;
+    public final int blocked;
+    
+    Count (int alive, int runnableNonDaemons, int runnableDaemons, int blocked){
+      this.alive = alive;
+      this.runnableNonDaemons = runnableNonDaemons;
+      this.runnableDaemons = runnableDaemons;
+      this.blocked = blocked;
+    }
+  }
+  
+  protected boolean reuseTid;
+  
+  // ThreadInfos for all created but not terminated threads
+  protected ThreadInfo[] threads;
+  
+  // the highest ID created so far along this path
+  protected int maxTid;
+
+
+  static class TListMemento implements Memento<ThreadList> {
+    // note that we don't clone/deepcopy ThreadInfos
+    Memento<ThreadInfo>[] tiMementos;
+    int maxTid;
+
+    TListMemento(ThreadList tl) {
+      ThreadInfo[] threads = tl.threads;
+      int len = threads.length;
+
+      maxTid = tl.maxTid;
+      tiMementos = new Memento[len];
+      for (int i=0; i<len; i++){
+        ThreadInfo ti = threads[i];
+        Memento<ThreadInfo> m = null;
+
+        if (!ti.hasChanged()){
+          m = ti.cachedMemento;
+        }
+        if (m == null){
+          m = ti.getMemento();
+          ti.cachedMemento = m;
+        }
+        tiMementos[i] = m;
+      }
+    }
+
+    @Override
+       public ThreadList restore(ThreadList tl){
+      int len = tiMementos.length;
+      ThreadInfo[] threads = new ThreadInfo[len];
+      for (int i=0; i<len; i++){
+        Memento<ThreadInfo> m = tiMementos[i];
+        ThreadInfo ti = m.restore(null);
+        ti.cachedMemento = m;
+        threads[i] = ti;
+        ti.tlIdx = i;
+      }
+      tl.threads = threads;
+      tl.maxTid = maxTid;
+
+      return tl;
+    }
+  }
+
+
+  protected ThreadList() {
+    // nothing here
+  }
+
+  /**
+   * Creates a new empty thread list.
+   */
+  public ThreadList (Config config, KernelState ks) {
+    threads = new ThreadInfo[0];
+    
+    reuseTid = config.getBoolean("vm.reuse_tid", false);
+  }
+
+  @Override
+  public Memento<ThreadList> getMemento(MementoFactory factory) {
+    return factory.getMemento(this);
+  }
+  public Memento<ThreadList> getMemento(){
+    return new TListMemento(this);
+  }
+
+  @Override
+  public Object clone() {
+    ThreadList other = new ThreadList();
+    other.threads = new ThreadInfo[threads.length];
+
+    for (int i=0; i<threads.length; i++) {
+      other.threads[i] = (ThreadInfo) threads[i].clone();
+    }
+
+    return other;
+  }
+
+  /**
+   * add a new ThreadInfo if it isn't already in the list.
+   * Note: the returned id is NOT our internal storage index
+   * 
+   * @return (path specific) Thread id
+   */
+  public int add (ThreadInfo ti) {
+    int n = threads.length;
+
+    BitSet ids = new BitSet();   
+    for (int i=0; i<n; i++) {
+      ThreadInfo t = threads[i];
+      if (t == ti) {
+        return t.getId();
+      }
+      
+      ids.set( t.getId());
+    }
+
+    // append it
+    ThreadInfo[] newThreads = new ThreadInfo[n+1];
+    System.arraycopy(threads, 0, newThreads, 0, n);
+    
+    newThreads[n] = ti;
+    ti.tlIdx = n;
+    
+    threads = newThreads;
+    
+    if (reuseTid){
+      return ids.nextClearBit(0);
+    } else {
+      return maxTid++;
+    }
+  }
+  
+  public boolean remove (ThreadInfo ti){
+    int n = threads.length;
+    
+    for (int i=0; i<n; i++) {
+      if (ti == threads[i]){
+        int n1 = n-1;
+        ThreadInfo[] newThreads = new ThreadInfo[n1];
+        if (i>0){
+          System.arraycopy(threads, 0, newThreads, 0, i);
+        }
+        if (i<n1){
+          System.arraycopy(threads, i+1, newThreads, i, (n1-i));
+          
+          // update the tlIdx values
+          for (int j=i; j<n1; j++){
+            ThreadInfo t = threads[j];
+            if (t != null){
+              t.tlIdx = j;
+            }
+          }
+        }
+        
+        threads = newThreads;        
+        return true;
+      }
+    }
+    
+    return false;
+  }
+
+  /**
+   * Returns the array of threads.
+   */
+  public ThreadInfo[] getThreads() {
+    return threads.clone();
+  }
+
+  public void hash (HashData hd) {
+    for (int i=0; i<threads.length; i++){
+      threads[i].hash(hd);
+    }
+  }
+  
+  public ThreadInfo getThreadInfoForId (int tid){
+    for (int i=0; i<threads.length; i++){
+      ThreadInfo ti = threads[i];
+      if (ti.getId() == tid){
+        return ti;
+      }
+    }
+    
+    return null;
+  }
+
+  public ThreadInfo getThreadInfoForObjRef (int objRef){
+    for (int i=0; i<threads.length; i++){
+      ThreadInfo ti = threads[i];
+      if (ti.getThreadObjectRef() == objRef){
+        return ti;
+      }
+    }
+    
+    return null;
+  }
+  
+  public boolean contains (ThreadInfo ti){
+    int idx = ti.tlIdx;
+    
+    if (idx < threads.length){
+      return threads[idx] == ti;
+    }
+    
+    return false;
+  }
+  
+  /**
+   * Returns the length of the list.
+   */
+  public int length () {
+    return threads.length;
+  }
+
+  /**
+   * Replaces the array of ThreadInfos.
+   */
+  public void setAll(ThreadInfo[] threads) {
+    this.threads = threads;
+  }
+
+  public ThreadInfo locate (int objref) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if (threads[i].getThreadObjectRef() == objref) {
+        return threads[i];
+      }
+    }
+
+    return null;
+  }
+
+  public void markRoots (Heap heap) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if (threads[i].isAlive()) {
+        threads[i].markRoots(heap);
+      }
+    }
+  }
+  
+  public boolean hasProcessTimeoutRunnables (ApplicationContext appCtx){
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      if (ti.isTimeoutRunnable() && ti.getApplicationContext() == appCtx) {
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  public ThreadInfo[] getProcessTimeoutRunnables (ApplicationContext appCtx){
+    ArrayList<ThreadInfo> list = new ArrayList<ThreadInfo>();
+    
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      if (ti.isTimeoutRunnable() && ti.getApplicationContext() == appCtx) {
+        list.add(ti);
+      }
+    }
+    
+    return list.toArray( new ThreadInfo[list.size()]);
+  }
+  
+  public boolean hasLiveThreads(){
+    for (int i = 0; i < threads.length; i++) {
+      if (threads[i].isAlive()) {
+        return true;
+      }
+    }
+    return false;    
+  }
+  
+  public boolean hasTimeoutRunnables (){
+    for (int i = 0; i < threads.length; i++) {
+      if (threads[i].isRunnable()) {
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  public boolean hasUnblockedThreads(){
+    for (int i = 0; i < threads.length; i++) {
+      if (threads[i].isUnblocked()) {
+        return true;
+      }
+    }
+    return false;    
+  }
+
+  public ThreadInfo[] getTimeoutRunnables (){
+    ArrayList<ThreadInfo> list = new ArrayList<ThreadInfo>();
+    
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      if (ti.isTimeoutRunnable()) {
+        list.add(ti);
+      }
+    }
+    
+    return list.toArray( new ThreadInfo[list.size()]);
+  }
+
+  
+
+  public boolean hasAnyMatching(Predicate<ThreadInfo> predicate) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if (predicate.isTrue(threads[i])) {
+        return true;
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean hasAnyMatchingOtherThan(ThreadInfo ti, Predicate<ThreadInfo> predicate) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if(ti != threads[i]) {
+        if (predicate.isTrue(threads[i])) {
+          return true;
+        }
+      }
+    }
+    
+    return false;
+  }
+  
+  public boolean hasOnlyMatching(Predicate<ThreadInfo> predicate) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if (!predicate.isTrue(threads[i])) {
+        return false;
+      }
+    }
+    
+    return true;
+  }
+  
+  public boolean hasOnlyMatchingOtherThan(ThreadInfo ti, Predicate<ThreadInfo> predicate) {
+    int n=0;
+    for (int i = 0, l = threads.length; i < l; i++) {
+      if(ti != threads[i]) {
+        if (!predicate.isTrue(threads[i])) {
+          return false;
+        } else {
+          n++;
+        }
+      }
+    }
+    
+    return (n>0);
+  }
+  
+  public ThreadInfo[] getAllMatching(Predicate<ThreadInfo> predicate) {
+    List<ThreadInfo> list = new ArrayList<ThreadInfo>();
+    
+    int n = 0;
+    for (int i = 0, l = threads.length; i < l; i++) {
+      ThreadInfo ti = threads[i];
+      if (predicate.isTrue(ti)) {
+        list.add(ti);
+        n++;
+      }
+    }
+    
+    return list.toArray(new ThreadInfo[n]);
+  }
+
+  public ThreadInfo[] getAllMatchingWith(final ThreadInfo ti, Predicate<ThreadInfo> predicate) {
+    List<ThreadInfo> list = new ArrayList<ThreadInfo>();
+    
+    int n = 0;
+    for (int i = 0, l = threads.length; i < l; i++) {
+      ThreadInfo t = threads[i];
+      if (predicate.isTrue(t) || (ti==t)) {
+        list.add(t);
+        n++;
+      }
+    }
+    
+    return list.toArray(new ThreadInfo[n]);
+  }
+  
+  public ThreadInfo[] getAllMatchingWithout(final ThreadInfo ti, Predicate<ThreadInfo> predicate) {
+    List<ThreadInfo> list = new ArrayList<ThreadInfo>();
+    
+    int n = 0;
+    for (int i = 0, l = threads.length; i < l; i++) {
+      ThreadInfo t = threads[i];
+      if (predicate.isTrue(t) && (ti != t)) {
+        list.add(t);
+        n++;
+      }
+    }
+    
+    return list.toArray(new ThreadInfo[n]);
+  }
+  
+  public int getMatchingCount(Predicate<ThreadInfo> predicate) {
+    int n = 0;
+    for (int i = 0, l = threads.length; i < l; i++) {
+      ThreadInfo ti = threads[i];
+      if (predicate.isTrue(ti)) {
+        n++;
+      }
+    }
+    
+    return n;
+  }
+  
+  public ThreadInfo getFirstMatching(Predicate<ThreadInfo> predicate) {
+    for (int i = 0, l = threads.length; i < l; i++) {
+      ThreadInfo t = threads[i];
+      if (predicate.isTrue(t)) {
+        return t;
+      }
+    }
+    
+    return null;
+  }
+  
+  public Count getCountWithout (ThreadInfo tiExclude){
+    int alive=0, runnableNonDaemons=0, runnableDaemons=0, blocked=0;
+    
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+  
+      if (ti != tiExclude){
+        if (ti.isAlive()) {
+          alive++;
+
+          if (ti.isRunnable()) {
+            if (ti.isDaemon()) {
+              runnableDaemons++;
+            } else {
+              runnableNonDaemons++;
+            }
+          } else {
+            blocked++;
+          }
+        }
+      }
+    }
+    
+    return new Count(alive, runnableNonDaemons, runnableDaemons, blocked);
+  }
+
+  public Count getCount(){
+    return getCountWithout(null);
+  }
+
+  public void dump () {
+    int i=0;
+    for (ThreadInfo t : threads) {
+      System.err.println("[" + i++ + "] " + t);
+    }
+  }
+
+  @Override
+  public Iterator<ThreadInfo> iterator() {
+    return new Iterator<ThreadInfo>() {
+      int i = 0;
+
+      @Override
+       public boolean hasNext() {
+        return threads != null && threads.length>0 && i<threads.length;
+      }
+
+      @Override
+       public ThreadInfo next() {
+        if (threads != null && threads.length>0 && i<threads.length){
+          return threads[i++];
+        } else {
+          throw new NoSuchElementException();
+        }
+      }
+
+      @Override
+       public void remove() {
+        throw new UnsupportedOperationException("Iterator<ThreadInfo>.remove()");
+      }
+    };
+  }
+
+  
+  class CanonicalLiveIterator implements Iterator<ThreadInfo> {
+    
+    int nextGid = -1;
+    int nextIdx = -1;
+    
+    CanonicalLiveIterator(){
+      setNext();
+    }
+    
+    // <2do> not overly efficient, but we assume small thread lists anyways
+    void setNext (){
+      int lastGid = nextGid;
+      int nextGid = Integer.MAX_VALUE;
+      int nextIdx = -1;
+      
+      for (int i=0; i<threads.length; i++){
+        ThreadInfo ti = threads[i];
+        if (ti.isAlive()){
+          int gid = ti.getGlobalId();
+          if ((gid > lastGid) && (gid < nextGid)){
+            nextGid = gid;
+            nextIdx = i;
+          }
+        }
+      }
+      
+      CanonicalLiveIterator.this.nextGid = nextGid;
+      CanonicalLiveIterator.this.nextIdx = nextIdx;
+    }
+    
+    @Override
+       public boolean hasNext() {
+      return (nextIdx >= 0);
+    }
+
+    @Override
+       public ThreadInfo next() {
+      if (nextIdx >= 0){
+        ThreadInfo tiNext = threads[nextIdx];
+        setNext();
+        return tiNext;
+        
+      } else {
+        throw new NoSuchElementException();
+      }
+    }
+
+    @Override
+       public void remove() {
+      throw new UnsupportedOperationException("Iterator<ThreadInfo>.remove()");
+    }
+  }
+  
+  /**
+   * an iterator for a canonical order over all live threads
+   */
+  public Iterator<ThreadInfo> canonicalLiveIterator(){
+    return new CanonicalLiveIterator();     
+  }
+  
+  
+  /**
+   * only for debugging purposes, this is expensive
+   */
+  public void checkConsistency(boolean isStore) {
+    for (int i = 0; i < threads.length; i++) {
+      ThreadInfo ti = threads[i];
+      
+      ti.checkConsistency(isStore);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThresholdFieldLockInfo.java b/src/main/gov/nasa/jpf/vm/ThresholdFieldLockInfo.java
new file mode 100644 (file)
index 0000000..25f917e
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+/**
+ * a FieldLockInfo that assumes lock protection after n accesses with
+ * non-empty lock sets
+ */
+public abstract class ThresholdFieldLockInfo extends FieldLockInfo implements Cloneable {
+  protected int remainingChecks;
+
+  protected ThresholdFieldLockInfo(int remainingChecks) {
+    this.remainingChecks = remainingChecks;
+  }
+
+  @Override
+  public boolean isProtected() {
+    // otherwise this would have turned into a EmptyFieldLockInfo
+    return (remainingChecks == 0);
+  }
+
+  protected void checkFailedLockAssumption(ThreadInfo ti, ElementInfo ei, FieldInfo fi) {
+    if (remainingChecks == 0) {
+      // with no locks remaining this would have been demoted to an
+      // EmptyFieldLockInfo by now
+      lockAssumptionFailed(ti, ei, fi);
+    }
+  }
+  
+  /**
+   * this implements a path-local FieldLockInfo that are never mutated
+   * this has to be overridden for search global FieldLockInfos
+   */
+  protected FieldLockInfo getInstance (int nRemaining){
+    try {
+      ThresholdFieldLockInfo fli = (ThresholdFieldLockInfo)clone();
+      fli.remainingChecks = nRemaining;
+      return fli;
+              
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("clone of ThresholdFieldLockInfo failed: " + this);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/ThrowsAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/ThrowsAnnotationInfo.java
new file mode 100644 (file)
index 0000000..4c1ddd4
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for throws elements
+ */
+public class ThrowsAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int throwsIndex;
+  
+  public ThrowsAnnotationInfo (AnnotationInfo base,  int targetType, short[] typePath, int throwsIndex)  {
+    super( base, targetType, typePath);
+    
+    this.throwsIndex = throwsIndex;
+  }
+  
+  public int getThrowsIndex(){
+    return throwsIndex;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/TidSet.java b/src/main/gov/nasa/jpf/vm/TidSet.java
new file mode 100644 (file)
index 0000000..d9f6b9f
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.UnsortedArrayIntSet;
+
+/**
+ * set that stores threads via (search global) thread ids. Used to detect shared objects/classes,
+ * created by configured SharedObjectPolicy factory
+ * 
+ * Note - this class modified contents of instances, i.e. it does destructive updates
+ * and hence has state carry-over between paths
+ */
+public class TidSet extends UnsortedArrayIntSet implements ThreadInfoSet, Memento<ThreadInfoSet> {
+  
+  protected VM vm;
+  
+  public TidSet (ThreadInfo ti){
+    vm = ti.getVM();
+    
+    add( ti.getId());
+  }  
+  
+  //--- set update
+  
+  @Override
+  public ThreadInfoSet add (ThreadInfo ti) {
+    add( ti.getId());
+    return this;
+  }
+  
+  @Override
+  public ThreadInfoSet remove (ThreadInfo ti) {
+    remove( ti.getId());
+    return this;
+  }
+  
+  
+  //--- set query
+  
+  @Override
+  public boolean contains (ThreadInfo ti) {
+    return contains( ti.getId());
+  }
+  
+  @Override
+  public boolean isShared (ThreadInfo ti, ElementInfo ei){
+    return hasMultipleLiveThreads();
+  }
+  
+  @Override
+  public boolean hasMultipleLiveThreads(){
+    if (size == 0){
+      return false;
+      
+    } else {
+      boolean alreadyHadOne = false;
+      ThreadList tl = vm.getThreadList();
+      
+      for (int i=0; i<size; i++){
+        ThreadInfo ti = tl.getThreadInfoForId(elements[i]);
+        if (ti != null && !ti.isTerminated()){
+          if (alreadyHadOne){
+            return true;
+          }
+          alreadyHadOne = true;
+        }
+      }
+      
+      return false;
+    }
+  }
+
+  @Override
+  public boolean hasMultipleRunnableThreads(){
+    if (size == 0){
+      return false;
+      
+    } else {
+      boolean alreadyHadOne = false;
+      ThreadList tl = vm.getThreadList();
+      
+      for (int i=0; i<size; i++){
+        ThreadInfo ti = tl.getThreadInfoForId(elements[i]);
+        if (ti != null && ti.isRunnable()){
+          if (alreadyHadOne){
+            return true;
+          }
+          alreadyHadOne = true;
+        }
+      }
+      
+      return false;
+    }
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder();
+    sb.append(getClass().getName());
+    sb.append('{');
+    for (int i = 0; i<size; i++) {
+      if (i>0) {
+        sb.append(',');
+      }
+      sb.append(elements[i]);
+    }
+    sb.append('}');
+    
+    return sb.toString();
+  }
+
+  
+  //--- state management (TidSet instance per default are their own mementos)
+  
+  @Override
+  public Memento<ThreadInfoSet> getMemento(){
+    return this;
+  }
+
+  @Override
+  public ThreadInfoSet restore(ThreadInfoSet inSitu) {
+    return this;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/TimeModel.java b/src/main/gov/nasa/jpf/vm/TimeModel.java
new file mode 100644 (file)
index 0000000..da55622
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * interface that encapsulates the mechanism to obtain values for
+ * 
+ *   System.getCurrentTimeMillis()
+ *   System.nanoTime()
+ * 
+ * calls. Implementors should guarantee the invariant that time values are
+ * strictly increasing along any given path, but don't have to backtrack
+ * time values in order to achieve uniform time increments along all paths.
+ * 
+ * Note that implementations have to avoid creating state leaks, i.e.
+ * the respective time value storage should not contribute to the state space
+ * hashing. If it has to be backtrackable, it either has to be stored on the 
+ * native side, or marked as @FilterField
+ */
+public interface TimeModel {
+  
+  public long currentTimeMillis();
+  public long nanoTime();
+}
diff --git a/src/main/gov/nasa/jpf/vm/Transition.java b/src/main/gov/nasa/jpf/vm/Transition.java
new file mode 100644 (file)
index 0000000..04991b7
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.Iterator;
+
+/**
+ * concrete type to store execution paths. TrailInfo corresponds to Transition,
+ * i.e. all instructions executed in the context of a vm.forward() leading
+ * into a new state
+ */
+public class Transition implements Iterable<Step>, Cloneable {
+
+  ChoiceGenerator<?> cg;
+  ThreadInfo ti;
+
+  private Step   first, last;
+  int nSteps;
+
+  private Object annotation;
+  String         output;
+
+  private int stateId = StateSet.UNKNOWN_ID;
+
+  public Transition (ChoiceGenerator<?> cg, ThreadInfo ti) {
+    this.cg = cg;
+    this.ti = ti;
+  }
+
+  @Override
+  public Object clone() {
+    try {
+      Transition t = (Transition)super.clone();
+      
+      // the deep copy references
+      t.cg = cg.clone();
+      t.ti = (ThreadInfo)ti.clone();
+      
+      return t;
+      
+    } catch (CloneNotSupportedException cnsx){
+      return null; // cannot happen
+    } 
+  }
+  
+  public String getLabel () {
+    if (last != null) {
+      return last.getLineString();
+    } else {
+      return "?";
+    }
+  }
+
+  public int getStateId() {
+    return(stateId);
+  }
+
+  public void setStateId(int id) {
+    stateId = id;
+  }
+
+  public void setOutput (String s) {
+    output = s;
+  }
+
+  public void setAnnotation (Object o) {
+    annotation = o;
+  }
+
+  public Object getAnnotation () {
+    return annotation;
+  }
+
+  public String getOutput () {
+    return output;
+  }
+
+  // don't use this for step iteration - this is very inefficient
+  public Step getStep (int index) {
+    Step s = first;
+    for (int i=0; s != null && i < index; i++) s = s.next;
+    return s;
+  }
+
+  public Step getLastStep () {
+    return last;
+  }
+
+  public int getStepCount () {
+    return nSteps;
+  }
+
+  public ThreadInfo getThreadInfo() {
+    return ti;
+  }
+
+  public int getThreadIndex () {
+    return ti.getId();
+  }
+
+  public ChoiceGenerator<?> getChoiceGenerator() {
+    return cg;
+  }
+
+  public ChoiceGenerator<?>[] getChoiceGeneratorCascade(){
+    return cg.getCascade();
+  }
+
+  public void incStepCount() {
+    nSteps++;
+  }
+
+  void addStep (Step step) {
+    if (first == null) {
+      first = step;
+      last = step;
+    } else {
+      last.next = step;
+      last = step;
+    }
+    nSteps++;
+  }
+
+  public class StepIterator implements Iterator<Step> {
+    Step cur;
+
+    @Override
+       public boolean hasNext () {
+      return (cur != last);
+    }
+
+    @Override
+       public Step next () {
+      if (cur == null) {
+        cur = first;
+      } else {
+        if (cur != last) {
+          cur = cur.next;
+        } else {
+          return null;
+        }
+      }
+      return cur;
+    }
+
+    @Override
+       public void remove () {
+      if (cur == null) {
+        first = first.next;
+      } else {
+        Step s;
+        for (s = first; s.next != cur; s = s.next);
+        s.next = cur.next;
+        cur = cur.next;
+      }
+    }
+  }
+
+  @Override
+  public Iterator<Step> iterator () {
+    return new StepIterator();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/TypeAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/TypeAnnotationInfo.java
new file mode 100644 (file)
index 0000000..3aa7618
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for classes, enums and interfaces
+ * 
+ * Note that we need a separate type than AbstractTypeAnnotationInfo since the
+ * type is used for queries (InfoObjects can have any number of type annotations
+ * with different targets)
+ */
+public class TypeAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  public TypeAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath)  {
+    super( base, targetType, typePath);
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/TypeParameterAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/TypeParameterAnnotationInfo.java
new file mode 100644 (file)
index 0000000..d1344e7
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for type parameters
+ */
+public class TypeParameterAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int typeIndex;
+  
+  public TypeParameterAnnotationInfo (AnnotationInfo base, 
+                                      int targetType, short[] typePath, int typeIndex) {
+    super( base, targetType, typePath);
+    
+    this.typeIndex = typeIndex;
+  }
+  
+  public int getTypeIndex(){
+    return typeIndex;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/TypeParameterBoundAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/TypeParameterBoundAnnotationInfo.java
new file mode 100644 (file)
index 0000000..fd3cfc9
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for type parameter bounds, such as
+ *   <T extends ClassA & TypeB & TypeC>
+ * 
+ * note that boundIndex == 0 always has to be a class
+ */
+public class TypeParameterBoundAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected int typeIndex;
+  protected int boundIndex;
+  
+  public TypeParameterBoundAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath, int typeIndex, int boundIndex)  {
+    super( base, targetType, typePath);
+    
+    this.typeIndex = typeIndex;
+  }
+  
+  public int getTypeIndex(){
+    return typeIndex;
+  }
+
+  public int getBoundIndex(){
+    return boundIndex;
+  }
+
+
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/Types.java b/src/main/gov/nasa/jpf/vm/Types.java
new file mode 100644 (file)
index 0000000..fb4add6
--- /dev/null
@@ -0,0 +1,1156 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+
+/**
+ * various type mangling/demangling routines
+ *
+ * This reflects the general type id mess in Java. We support the following:
+ *
+ *  builtin type: byte - T_BOOLEAN and the like
+ *  type name: String - according to JLS 6.7 ("int", "x.Y[]")
+ *  type signature: String - like JNI ("I", "[Lx/Y;")
+ *  type classname: String - e.g. "int", "[I", "x.Y", "[Lx.Y;"
+ */
+public class Types {
+
+  // these have the same values as the BCEL Constants since we don't want to break compiled code
+  public static final byte T_NONE      = 0; // illegal type
+  
+  public static final byte T_BOOLEAN   = 4;
+  public static final byte T_BYTE      = 8;
+  public static final byte T_CHAR      = 5;
+  public static final byte T_SHORT     = 9;
+  public static final byte T_INT       = 10;
+  public static final byte T_LONG      = 11;
+  public static final byte T_FLOAT     = 6;
+  public static final byte T_DOUBLE    = 7;
+  public static final byte T_REFERENCE = 14;
+  public static final byte T_ARRAY     = 13;  // <2do> do we need this in addition to T_REFERENCE?
+  public static final byte T_VOID      = 12;
+
+  
+  public static byte[] getArgumentTypes (String signature) {
+    int i;
+    int j;
+    int nArgs;
+
+    for (i = 1, nArgs = 0; signature.charAt(i) != ')'; nArgs++) {
+      i += getTypeLength(signature, i);
+    }
+
+    byte[] args = new byte[nArgs];
+
+    for (i = 1, j = 0; j < nArgs; j++) {
+      int    end = i + getTypeLength(signature, i);
+      String arg = signature.substring(i, end);
+      i = end;
+
+      args[j] = getBuiltinTypeFromSignature(arg);
+    }
+
+    return args;
+  }
+
+  public static String[] getArgumentTypeNames (String signature) {
+    int len = signature.length();
+
+    if ((len > 1) && (signature.charAt(1) == ')')) {
+      return new String[0]; // 'no args' shortcut
+    }
+    
+    ArrayList<String> a = new ArrayList<String>();
+
+    for (int i = 1; signature.charAt(i) != ')';) {
+      int end = i + getTypeLength(signature,i);
+      String arg = signature.substring(i, end);
+      i = end;
+
+      a.add(getTypeName(arg));
+    }
+
+    String[] typeNames = new String[a.size()];
+    a.toArray(typeNames);
+    
+    return typeNames;
+  }
+  
+  public static String dequalify (String typeName){
+    int idx = typeName.lastIndexOf('.');
+    if (idx > 0) {
+      return typeName.substring(idx + 1);
+    } else {
+      return typeName;
+    }    
+  }
+  
+  public static String getDequalifiedMethodSignature (String mName){
+    int idx = mName.indexOf('(');
+    String sig = mName.substring(idx);
+    
+    return mName.substring(0, idx) + getDequalifiedArgumentSignature(sig);
+  }
+  
+  public static String getDequalifiedArgumentSignature (String sig){
+    String[] argTypes = getArgumentTypeNames(sig);
+    StringBuilder sb = new StringBuilder();
+    sb.append('(');
+    for (int i=0; i<argTypes.length; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      sb.append(dequalify(argTypes[i]));
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+  
+  public static String getDequalifiedTypeName (String sig){
+    String tn = getTypeName(sig);
+    return dequalify(tn);
+  }
+  
+  public static String getArgumentSignature (String[] typeNames, boolean qualified){
+    StringBuilder sb = new StringBuilder();
+    sb.append('(');
+    for (int i=0; i<typeNames.length; i++){
+      if (i>0){
+        sb.append(',');
+      }
+      
+      String tn = getTypeName(typeNames[i]);
+      if (!qualified){
+        int idx = tn.lastIndexOf('.');
+        if (idx >0){
+          tn = tn.substring(idx+1);
+        }
+      }
+      
+      sb.append( tn);
+    }
+    sb.append(')');
+    return sb.toString();
+  }
+  
+  /**
+   * get size in stack slots (ints), excluding this
+   */
+  public static int getArgumentsSize (String sig) {
+    int  n = 0;
+    for (int i = 1; sig.charAt(i) != ')'; i++) {
+      switch (sig.charAt(i)) {
+      case 'L':
+        do i++; while (sig.charAt(i) != ';');
+        n++;
+        break;
+      case '[':
+        do i++; while (sig.charAt(i) == '[');
+        if (sig.charAt(i) == 'L') {
+          do i++; while (sig.charAt(i) != ';');
+        }
+        n++;
+        break;
+      case 'J':
+      case 'D':
+        // the two-slot types
+        n += 2;
+        break;
+      default:
+        // just one slot entry
+        n++;
+      }
+    }
+    return n;
+  }
+
+  public static String getArrayElementType (String type) {
+    if (type.charAt(0) != '[') {
+      throw new JPFException("not an array type: " + type);
+    }
+
+    return type.substring(1);
+  }
+
+  public static String getComponentTerminal (String type) {
+    if (type.charAt(0) != '[') {
+      throw new JPFException("not an array type: " + type);
+    }
+
+    if(isReferenceSignature(type)) {
+      return type.substring(type.indexOf('L') + 1 , type.indexOf(';'));
+    } else {
+      return type.substring(type.lastIndexOf('[') + 1);
+    }
+  }
+
+  public static byte getBuiltinTypeFromSignature (String signature) {
+    switch (signature.charAt(0)) {
+    case 'B':
+      return T_BYTE;
+
+    case 'C':
+      return T_CHAR;
+
+    case 'D':
+      return T_DOUBLE;
+
+    case 'F':
+      return T_FLOAT;
+
+    case 'I':
+      return T_INT;
+
+    case 'J':
+      return T_LONG;
+
+    case 'L':
+      return T_REFERENCE;
+
+    case 'S':
+      return T_SHORT;
+
+    case 'V':
+      return T_VOID;
+
+    case 'Z':
+      return T_BOOLEAN;
+
+    case '[':
+      return T_ARRAY;
+    }
+
+    throw new JPFException("invalid type string: " + signature);
+  }
+
+  /**
+   * get the argument type part of the signature out of a
+   * JNI mangled method name.
+   * Note this is not the complete signature, since we don't have a
+   * return type (which is superfluous since it's not overloading,
+   * but unfortunately part of the signature in the class file)
+   */
+  public static String getJNISignature (String mangledName) {
+    int    i = mangledName.indexOf("__");
+    String sig = null;
+
+    if (i > 0) {
+      int k = 0;      
+      int r = mangledName.indexOf("__", i+2); // maybe there is a return type part
+      boolean gotReturnType = false;
+      int len = mangledName.length();
+      char[] buf = new char[len + 2];
+
+      buf[k++] = '(';
+
+      for (i += 2; i < len; i++) {
+
+        if (i == r) { // here comes the return type part (that's not JNI, only MJI
+          if ((i + 2) < len) {
+            i++;
+            buf[k++] = ')';
+            gotReturnType = true;
+            continue;
+          } else {
+            break;
+          }
+        }
+        
+        char c = mangledName.charAt(i);
+        if (c == '_') {
+          i++;
+
+          if (i < len) {
+            c = mangledName.charAt(i);
+
+            switch (c) {
+            case '1':
+              buf[k++] = '_';
+
+              break;
+
+            case '2':
+              buf[k++] = ';';
+
+              break;
+
+            case '3':
+              buf[k++] = '[';
+
+              break;
+
+            default:
+              buf[k++] = '/';
+              buf[k++] = c;
+            }
+          } else {
+            buf[k++] = '/';
+          }
+        } else {
+          buf[k++] = c;
+        }
+      }
+
+      if (!gotReturnType) {
+        // if there was no return type spec, assume 'void'
+        buf[k++] = ')';
+        buf[k++] = 'V';
+      }
+        
+      sig = new String(buf, 0, k);
+    }
+
+    // Hmm, maybe we should return "()V" instead of null, but that seems a bit too assuming
+    return sig;
+  }
+
+  public static String getJNIMangledMethodName (Method m) {
+    String      name = m.getName();
+    Class<?>[]    pt = m.getParameterTypes();
+    StringBuilder  s = new StringBuilder(name.length() + (pt.length * 16));
+
+    s.append(name);
+    s.append("__");
+
+    // <2do> not very efficient, but we don't care for now
+    for (int i = 0; i < pt.length; i++) {
+      s.append(getJNITypeCode(pt[i].getName()));
+    }
+
+    // the return type part, which is not in JNI, but is required for
+    // handling covariant return types
+    Class<?> rt = m.getReturnType();
+    s.append("__");
+    s.append(getJNITypeCode(rt.getName()));
+    
+    return s.toString();
+  }
+
+  public static String getJNIMangledMethodName (String cls, String name,
+                                                String signature) {
+    StringBuilder s = new StringBuilder(signature.length() + 10);
+    int           i;
+    char          c;
+    int           slen = signature.length();
+    
+    if (cls != null) {
+      s.append(cls.replace('.', '_'));
+    }
+
+    s.append(name);
+    s.append("__");
+
+    // as defined in the JNI specs
+    for (i = 1; i<slen; i++) {
+      c = signature.charAt(i);
+      switch (c) {
+      case '/':
+        s.append('_');
+        break;
+
+      case '_':
+        s.append("_1");
+        break;
+
+      case ';':
+        s.append("_2");
+        break;
+
+      case '[':
+        s.append("_3");
+        break;
+
+      case ')':
+        // the return type part - note this is not JNI, but saves us a lot of trouble with
+        // the covariant return types of Java 1.5        
+        s.append("__");
+        break;
+        
+      default:
+        s.append(c);
+      }
+    }
+
+    return s.toString();
+  }
+
+  /**
+   * return the name part of a JNI mangled method name (which is of
+   * course not completely safe - you should only use it if you know
+   * this is a JNI name)
+   */
+  public static String getJNIMethodName (String mangledName) {
+    // note that's the first '__' group, which marks the beginning of the arg types
+    int i = mangledName.indexOf("__");
+
+    if (i > 0) {
+      return mangledName.substring(0, i);
+    } else {
+      return mangledName;
+    }
+  }
+
+  /**
+   * type is supposed to be Class.getName conforming, i.e.
+   * 
+   * int    -> int
+   * int[]  -> [I
+   * String -> java.lang.String
+   * String[] -> [Ljava.lang.String;
+   * String[][] -> [[Ljava.lang.String;
+   * 
+   * <2do> this is really not very efficient
+   */
+  public static String getJNITypeCode (String type) {
+    StringBuilder sb = new StringBuilder(32);
+    int l = type.length() - 1;
+    int i;
+
+    // Class.getName arrays "[...type"
+    for ( i=0; type.charAt(i) == '['; i++){
+      sb.append("_3");
+    }
+    
+    // conventional arrays "type[]..."
+    for (; type.charAt(l) == ']'; l -= 2) {
+      sb.append("_3");
+    }
+
+    type = type.substring(i, l + 1);
+
+    if (type.equals("int") || type.equals("I")) {
+      sb.append('I');
+    } else if (type.equals("long") || type.equals("J")) {
+      sb.append('J');
+    } else if (type.equals("boolean") || type.equals("Z")) {
+      sb.append('Z');
+    } else if (type.equals("char") || type.equals("C")) {
+      sb.append('C');
+    } else if (type.equals("byte")  || type.equals("B")) {
+      sb.append('B');
+    } else if (type.equals("short") || type.equals("S")) {
+      sb.append('S');
+    } else if (type.equals("double") || type.equals("D")) {
+      sb.append('D');
+    } else if (type.equals("float") || type.equals("F")) {
+      sb.append('F');
+    } else if (type.equals("void") || type.equals("V")) {  // for return types
+      sb.append('V');
+    } else { // reference type
+      if (type.charAt(0) != 'L'){
+        sb.append('L');
+      }
+
+      l = type.length();
+      for (i=0; i < l; i++) {
+        char c = type.charAt(i);
+
+        switch (c) {
+        case '.':
+          sb.append('_');
+          break;
+
+        case '_':
+          sb.append("_1");
+          break;
+          
+        case ';':
+          break;
+          
+        default:
+          sb.append(c);
+        }
+      }
+
+      sb.append("_2");
+      
+    }
+
+    return sb.toString();
+  }
+
+  // this includes the return type part
+  public static int getNumberOfStackSlots (String signature, boolean isStatic) {
+    int nArgSlots = 0;
+    int n = isStatic ? 0 : 1;
+    int sigLen = signature.length();
+
+    for (int i = 1; i < sigLen; i++) {
+      switch (signature.charAt(i)) {
+      case ')' : // end of arg part, but there still might be a return type
+        nArgSlots = n;
+        n = 0;
+        break;
+      case 'L':   // reference = 1 slot
+        i = signature.indexOf(';', i);
+        n++;
+        break;
+      case '[':
+        do i++; while (signature.charAt(i) == '[');
+        if (signature.charAt(i) == 'L') {
+          i = signature.indexOf(';', i);
+        }
+        n++;
+        break;
+      case 'J':
+      case 'D':
+        n+=2;
+        break;
+      default:
+        n++;
+      }
+    }
+    
+    return Math.max(n, nArgSlots);
+  }
+  
+  public static int getNumberOfArguments (String signature) {
+    int  i,n;
+    int sigLen = signature.length();
+
+    for (i = 1, n = 0; i<sigLen; n++) {
+      switch (signature.charAt(i)) {
+      case ')' :
+        return n;
+      case 'L':
+        do i++; while (signature.charAt(i) != ';');
+        break;
+
+      case '[':
+        do i++; while (signature.charAt(i) == '[');
+        if (signature.charAt(i) == 'L') {
+          do i++; while (signature.charAt(i) != ';');
+        }
+        break;
+
+      default:
+        // just a single type char
+      }
+
+      i++;
+    }
+
+    assert (false) : "malformed signature: " + signature;
+    return n; // that would be a malformed signature
+  }
+
+  public static boolean isReferenceSignature(String signature){
+    return signature.charAt(signature.length()-1) == ';';
+  }
+
+  public static boolean isReference (String type) {
+    int t = getBuiltinTypeFromSignature(type);
+
+    return (t == T_ARRAY) || (t == T_REFERENCE);
+  }
+
+  public static boolean isArray (String type) {
+    int t = getBuiltinTypeFromSignature(type);
+
+    return (t == T_ARRAY);
+  }
+
+  public static byte getReturnBuiltinType (String signature) {
+    int i = signature.indexOf(')');
+
+    return getBuiltinTypeFromSignature(signature.substring(i + 1));
+  }
+
+  public static String getReturnTypeSignature(String signature){
+    int i = signature.indexOf(')');
+    return signature.substring(i + 1);
+  }
+
+  public static String getReturnTypeName (String signature){
+    int i = signature.indexOf(')');
+    return getTypeName(signature.substring(i+1));
+  }
+  
+  public static String getTypeSignature (String type, boolean asDotNotation) {
+    String  t = null;
+    int arrayDim = 0;
+    
+    type = asDotNotation ? type.replace('/', '.') : type.replace('.', '/');
+    
+    if ((type.charAt(0) == '[') || (type.endsWith(";"))) {  // [[[L...;
+      t = type;
+      
+    } else {
+      
+      while (type.endsWith("[]")) { // type[][][]
+        type = type.substring(0, type.length() - 2);
+        arrayDim++;
+      }
+      
+      if (type.equals("byte")) {
+        t = "B";
+      } else if (type.equals("char")) {
+        t = "C";
+      } else if (type.equals("short")) {
+        t = "S";
+      } else if (type.equals("int")) {
+        t = "I";
+      } else if (type.equals("float")) {
+        t = "F";
+      } else if (type.equals("long")) {
+        t = "J";
+      } else if (type.equals("double")) {
+        t = "D";
+      } else if (type.equals("boolean")) {
+        t = "Z";
+      } else if (type.equals("void")) {
+        t = "V";
+      } else if (type.endsWith(";")) {
+        t = type;
+        
+      } else {
+        t = "L" + type + ';';
+      }
+      
+      while (arrayDim-- > 0) {
+        t = "[" + t;
+      }
+    }
+
+    return t;
+  }
+
+  public static byte getBuiltinType(String typeName){
+      if (typeName.equals("byte")) {
+        return T_BYTE;
+      } else if (typeName.equals("char")) {
+        return T_CHAR;
+      } else if (typeName.equals("short")) {
+        return T_SHORT;
+      } else if (typeName.equals("int")) {
+        return T_INT;
+      } else if (typeName.equals("float")) {
+        return T_FLOAT;
+      } else if (typeName.equals("long")) {
+        return T_LONG;
+      } else if (typeName.equals("double")) {
+        return T_DOUBLE;
+      } else if (typeName.equals("boolean")) {
+        return T_BOOLEAN;
+      } else if (typeName.equals("void")) {
+        return T_VOID;
+      } else {
+        if (typeName.charAt(typeName.length()-1) == ']'){
+          return T_ARRAY;
+        } else {
+          return T_REFERENCE;
+        }
+      }
+  }
+
+  public static String getBoxedType (byte type) {
+         switch (type) {
+         case Types.T_BOOLEAN:
+                 return "Boolean";
+         case Types.T_BYTE:
+                 return "Byte";
+         case Types.T_CHAR:
+                 return "Character";
+         case Types.T_SHORT:
+                 return "Short";
+         case Types.T_INT:
+                 return "Integer";
+         case Types.T_LONG:
+                 return "Long";
+         case Types.T_FLOAT:
+                 return "Float";
+         case Types.T_DOUBLE:
+                 return "Double";
+         default:
+                 return null;
+         }
+  }
+  
+  public static byte getUnboxedType (String typeName){
+    if (typeName.startsWith("java.lang.")){
+      typeName = typeName.substring(10);
+      if (typeName.equals("Boolean")){
+        return T_BOOLEAN;
+      } else if (typeName.equals("Byte")){
+        return T_BYTE;
+      } else if (typeName.equals("Character")){
+        return T_CHAR;
+      } else if (typeName.equals("Short")){
+        return T_SHORT;
+      } else if (typeName.equals("Integer")){
+        return T_INT;
+      } else if (typeName.equals("Long")){
+        return T_LONG;
+      } else if (typeName.equals("Float")){
+        return T_FLOAT;
+      } else if (typeName.equals("Double")){
+        return T_DOUBLE;
+      }
+    }
+    
+    // everything else can't be a box type
+    if (typeName.charAt(0) == '[' || typeName.charAt(typeName.length()-1) == ']'){
+      return T_ARRAY;
+    } else {
+      return T_REFERENCE;
+    }
+  }
+  
+  public static String getClassNameFromSignature (String signature){
+    if (signature.charAt(signature.length()-1) == ';'){ // reference
+      return signature.replace('/', '.');
+
+    } else { // builtin
+      switch (signature.charAt(0)){
+        case 'Z': return "boolean";
+        case 'B': return "byte";
+        case 'C': return "char";
+        case 'S': return "short";
+        case 'I': return "int";
+        case 'J': return "long";
+        case 'F': return "float";
+        case 'D': return "double";
+        default:
+          throw new JPFException("illegal type signature: " + signature);
+      }
+    }
+  }
+
+  /**
+   * get the canonical representation of a type name, which happens to be
+   *  (1) the name of the builtin type (e.g. "int")
+   *  (2) the normal dot name for ordinary classes (e.g. "java.lang.String")
+   *  (3) the coded dot name for arrays (e.g. "[B", "[[C", or "[Ljava.lang.String;")
+   *  
+   * not sure if we need to support internal class names (which use '/'
+   * instead of '.' as separators
+   *  
+   * no idea what's the logic behind this, but let's implement it
+   */
+  public static String getClassNameFromTypeName (String typeName) {
+    typeName = typeName.replace('/','.');
+    int n = typeName.length()-1;
+    
+    if (typeName.charAt(0) == '['){ // the "[<type>" notation
+      if (typeName.charAt(1) == 'L'){
+        if (typeName.charAt(n) != ';'){
+          typeName = typeName + ';';
+        }
+      }
+      
+      return typeName;
+    }
+    
+    int i=typeName.indexOf('[');
+    if (i>0){ // the sort of "<type>[]"
+      StringBuilder sb = new StringBuilder();
+      sb.append('[');
+      for (int j=i; (j=typeName.indexOf('[',j+1)) >0;){
+        sb.append('[');
+      }
+      
+      typeName = typeName.substring(0,i);
+      if (isBasicType(typeName)){
+        sb.append( getTypeSignature(typeName, true));
+      } else {
+        sb.append('L');
+        sb.append(typeName);
+        sb.append(';');
+      }
+      
+      return sb.toString();
+    }
+    
+    if (typeName.charAt(n) == ';') {
+      return typeName.substring(1,n);
+    }
+    
+    return typeName;
+  }
+
+  
+  public static boolean isTypeCode (String t) {
+    char c = t.charAt(0);
+
+    if (c == '[') {
+      return true;
+    }
+
+    if ((t.length() == 1) &&
+            ((c == 'B') || (c == 'I') || (c == 'S') || (c == 'C') ||
+              (c == 'F') || (c == 'J') || (c == 'D') || (c == 'Z'))) {
+      return true;
+    }
+
+    if (t.endsWith(";")) {
+      return true;
+    }
+
+    return false;
+  }
+
+  public static boolean isBasicType (String typeName){
+    return ("boolean".equals(typeName) ||
+        "byte".equals(typeName) ||
+        "char".equals(typeName) ||
+        "int".equals(typeName) ||
+        "long".equals(typeName) ||
+        "double".equals(typeName) ||
+        "short".equals(typeName) ||
+        "float".equals(typeName));
+  }
+
+  public static byte getTypeCode (String signature){
+    char c = signature.charAt(0);
+
+    switch (c) {
+      case 'B':
+        return T_BYTE;
+
+      case 'C':
+        return T_CHAR;
+
+      case 'D':
+        return T_DOUBLE;
+
+      case 'F':
+        return T_FLOAT;
+
+      case 'I':
+        return T_INT;
+
+      case 'J':
+        return T_LONG;
+
+      case 'L':
+        return T_REFERENCE;
+
+      case 'S':
+        return T_SHORT;
+
+      case 'V':
+        return T_VOID;
+
+      case 'Z':
+        return T_BOOLEAN;
+
+      case '[':
+        return T_ARRAY;
+
+      default:
+        throw new JPFException("unknow typecode: " + signature);
+    }
+  }
+  
+  /**
+   * return the qualified signature name according to JLS 6.7 (e.g. "int", "x.Y[]")
+   */
+  public static String getTypeName (String signature) {
+    int  len = signature.length();
+    char c = signature.charAt(0);
+
+    if (len == 1) {
+      switch (c) {
+      case 'B':
+        return "byte";
+
+      case 'C':
+        return "char";
+
+      case 'D':
+        return "double";
+
+      case 'F':
+        return "float";
+
+      case 'I':
+        return "int";
+
+      case 'J':
+        return "long";
+
+      case 'S':
+        return "short";
+
+      case 'V':
+        return "void";
+
+      case 'Z':
+        return "boolean";
+      }
+    }
+
+    if (c == '[') {
+      return getTypeName(signature.substring(1)) + "[]";
+    }
+
+    int len1 = len-1;
+    if (signature.charAt(len1) == ';') {
+      return signature.substring(1, len1).replace('/', '.');
+    }
+
+    throw new JPFException("invalid type string: " + signature);
+  }
+
+  /** thoses are according to the arrayType codes of the newarray JVMS definition */
+  public static String getElementDescriptorOfType (int arrayType){
+    switch (arrayType){
+      case 4: return "Z";
+      case 5: return "C";
+      case 6: return "F";
+      case 7: return "D";
+      case 8: return "B";
+      case 9: return "S";
+      case 10: return "I";
+      case 11: return "J";
+    }
+    return null;
+  }
+
+  /**
+   * what would be the info size in bytes, not words
+   * (we ignore 64bit machines for now)
+   */
+  public static int getTypeSizeInBytes (String signature) {
+    switch (signature.charAt(0)) {
+      case 'V':
+        return 0;
+        
+      case 'Z': // that's a stretch, but we assume boolean uses the smallest addressable size
+      case 'B':
+        return 1;
+        
+      case 'S':
+      case 'C':
+        return 2;
+        
+      case 'L':
+      case '[':
+      case 'F':
+      case 'I':
+        return 4;
+        
+      case 'D':
+      case 'J':
+        return 8;
+    }
+
+    throw new JPFException("invalid type string: " + signature);
+  }
+  
+  public static int getTypeSize (String signature) {
+    switch (signature.charAt(0)) {
+    case 'V':
+      return 0;
+
+    case 'B':
+    case 'C':
+    case 'F':
+    case 'I':
+    case 'L':
+    case 'S':
+    case 'Z':
+    case '[':
+      return 1;
+
+    case 'D':
+    case 'J':
+      return 2;
+    }
+
+    throw new JPFException("invalid type string: " + signature);
+  }
+
+  public static int getTypeSize (byte typeCategory){
+    if (typeCategory == T_LONG || typeCategory == T_DOUBLE){
+      return 2;
+    } else {
+      return 1;
+    }
+  }
+  
+  public static String asTypeName (String type) {
+    if (type.startsWith("[") || type.endsWith(";")) {
+      return getTypeName(type);
+    }
+
+    return type;
+  }
+
+  public static int booleanToInt (boolean b) {
+    return b ? 1 : 0;
+  }
+
+  public static long doubleToLong (double d) {
+    return Double.doubleToLongBits(d);
+  }
+
+  public static int floatToInt (float f) {
+    return Float.floatToIntBits(f);
+  }
+
+  public static int hiDouble (double d) {
+    return hiLong(Double.doubleToLongBits(d));
+  }
+
+  public static int hiLong (long l) {
+    return (int) (l >> 32);
+  }
+
+  public static boolean instanceOf (String type, String ofType) {
+    int bType = getBuiltinTypeFromSignature(type);
+
+    if ((bType == T_ARRAY) && ofType.equals("Ljava.lang.Object;")) {
+      return true;
+    }
+
+    int bOfType = getBuiltinTypeFromSignature(ofType);
+
+    if (bType != bOfType) {
+      return false;
+    }
+
+    switch (bType) {
+    case T_ARRAY:
+      return instanceOf(type.substring(1), ofType.substring(1));
+
+    case T_REFERENCE:
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(getTypeName(type));
+      return ci.isInstanceOf(getTypeName(ofType));
+
+    default:
+      return true;
+    }
+  }
+
+  public static boolean intToBoolean (int i) {
+    return i != 0;
+  }
+
+  public static float intToFloat (int i) {
+    return Float.intBitsToFloat(i);
+  }
+
+  public static double intsToDouble (int l, int h) {
+    long bits = ((long) h << 32) | (/*(long)*/ l & 0xFFFFFFFFL);
+    return Double.longBitsToDouble(bits);
+  }
+
+  public static long intsToLong (int l, int h) {
+    return ((long) h << 32) | (/*(long)*/ l & 0xFFFFFFFFL);
+  }
+
+  public static int loDouble (double d) {
+    return loLong(Double.doubleToLongBits(d));
+  }
+
+  public static int loLong (long l) {
+    return (int) (l & 0xFFFFFFFFL);
+  }
+
+  public static double longToDouble (long l) {
+    return Double.longBitsToDouble(l);
+  }
+
+  private static int getTypeLength (String signature, int idx) {
+    switch (signature.charAt(idx)) {
+    case 'B':
+    case 'C':
+    case 'D':
+    case 'F':
+    case 'I':
+    case 'J':
+    case 'S':
+    case 'V':
+    case 'Z':
+      return 1;
+
+    case '[':
+      return 1 + getTypeLength(signature, idx + 1);
+
+    case 'L':
+
+      int semicolon = signature.indexOf(';', idx);
+
+      if (semicolon == -1) {
+        throw new JPFException("invalid type signature: " +
+                                         signature);
+      }
+
+      return semicolon - idx + 1;
+    }
+
+    throw new JPFException("invalid type signature");
+  }
+
+  /**
+   * return the JPF internal representation of a method signature that is given
+   * in dot-notation (like javap),
+   *
+   *  e.g.  "int foo(int[],java.lang.String)" -> "foo([ILjava/lang/String;)I"
+   *
+   */
+  public static String getSignatureName (String methodDecl) {
+
+    StringBuffer sb = new StringBuffer(128);
+    String retType = null;
+
+    int i = methodDecl.indexOf('(');
+    if (i>0){
+
+      //--- name and return type
+      String[] a = methodDecl.substring(0, i).split(" ");
+      if (a.length > 0){
+        sb.append(a[a.length-1]);
+
+        if (a.length > 1){
+          retType = getTypeSignature(a[a.length-2], false);
+        }
+      }
+
+      //--- argument types
+      int j = methodDecl.lastIndexOf(')');
+      if (j > 0){
+        sb.append('(');
+        for (String type : methodDecl.substring(i+1,j).split(",")){
+          if (!type.isEmpty()){
+            type = type.trim();
+            if (!type.isEmpty()){
+              sb.append( getTypeSignature(type,false));
+            }
+          }
+        }
+        sb.append(')');
+
+        if (retType != null){
+          sb.append(retType);
+        }
+
+        return sb.toString();
+      }
+    }
+
+    throw new JPFException("invalid method declaration: " + methodDecl);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/UncaughtException.java b/src/main/gov/nasa/jpf/vm/UncaughtException.java
new file mode 100644 (file)
index 0000000..9a0ee12
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.Printable;
+
+import java.io.PrintWriter;
+
+
+/**
+ * represents the case of an unhandled exception detected by JPF
+ *
+ * This is a "controlflow exception", but I finally made my peace with it since
+ * UncaughtExceptions can be thrown from various places, including the VM (<clinit>, finalizer)
+ * and we can't rely on that all these locations can check for pc == null. Even if they would,
+ * at this point there is nothing to do anymore, get to the NoUncaughtProperty reporting
+ * as quickly as possible, since chances are we would be even obfuscating the problem
+ */
+@SuppressWarnings("serial")
+public class UncaughtException extends RuntimeException implements Printable {
+
+  ThreadInfo thread;
+  int xObjRef;          // the exception object reference (that went uncaught)
+
+  String     xClsName;
+  String     details;
+
+  //ArrayList  stackTrace; // unused -pcd
+
+  public UncaughtException (ThreadInfo ti, int objRef) {
+    thread = ti;
+    xObjRef = objRef;
+    
+    ElementInfo ei = ti.getElementInfo(xObjRef);
+    xClsName = ei.getClassInfo().getName();
+    details = ei.getStringField("detailMessage");
+  }
+  
+  public String getRawMessage () {
+    return xClsName;
+  }
+  
+  @Override
+  public String getMessage () {
+    String s = "uncaught exception in thread " + thread.getName() +
+              " #" + thread.getId() + " : "
+              + xClsName;
+    
+    if (details != null) {
+      s += " : \"" + details + "\"";
+    }
+    
+    return s;
+  }
+
+  @Override
+  public void printOn (PrintWriter pw) {
+    pw.print("uncaught exception in thread ");
+    pw.print( thread.getName());
+    pw.print(" #");
+    pw.print(thread.getId());
+    pw.print(" : ");
+
+    thread.printStackTrace(pw, xObjRef);
+    pw.flush();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/VM.java b/src/main/gov/nasa/jpf/vm/VM.java
new file mode 100644 (file)
index 0000000..560a979
--- /dev/null
@@ -0,0 +1,2053 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.JPFListenerException;
+import gov.nasa.jpf.jvm.ClassFile;
+import gov.nasa.jpf.vm.FinalizerThreadInfo;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.IntTable;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.Misc;
+import gov.nasa.jpf.util.Predicate;
+
+import java.io.PrintWriter;
+import java.nio.ByteOrder;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * This class represents the virtual machine. The virtual machine is able to
+ * move backward and forward one transition at a time.
+ */
+public abstract class VM {
+
+  /**
+   * this is a debugging aid to control compilation of expensive consistency checks
+   * (we don't control these with class-wise assertion enabling since we do use
+   * unconditional assertions for mandatory consistency checks)
+   */
+  public static final boolean CHECK_CONSISTENCY = false;
+  
+  protected static final String[] EMPTY_ARGS = new String[0];
+  
+  protected static JPFLogger log = JPF.getLogger("vm");
+
+  /**
+   * our execution context
+   */
+  protected JPF jpf;
+
+  /**
+   * The number of errors saved so far.
+   * Used to generate the name of the error trail file.
+   */
+  protected static int error_id;
+
+  /**
+   * <2do> - this is a hack to be removed once there are no static references
+   * anymore
+   */
+  protected static VM vm;
+
+  static {
+    initStaticFields();
+  }
+
+  protected SystemState ss;
+  
+  protected FunctionObjectFactory funcObjFactory = new FunctionObjectFactory();
+
+  // <2do> - if you are confused about the various pieces of state and its
+  // storage/backtrack structures, I'm with you. It's mainly an attempt to
+  // separate non-policy VM state (objects), policy VM state (Scheduler)
+  // and general JPF execution state, with special support for stack oriented
+  // state restoration (backtracking).
+  // this needs to be cleaned up and the principle reinstated
+
+
+  protected Path path;  /** execution path to current state */
+  protected StringBuilder out;  /** buffer to store output along path execution */
+
+  /**
+   * various caches for VMListener state acquisition. NOTE - these are only
+   * valid during notification
+   *
+   * <2do> get rid of the 'lasts' in favor of queries on the insn, the executing
+   * thread, and the VM. This is superfluous work to for every notification
+   * (even if there are no listeners using it) that can easily lead to inconsistencies
+   */
+  protected Transition      lastTrailInfo;
+
+  protected boolean isTraceReplay; // can be set by listeners to indicate this is a replay
+
+  /** the repository we use to find out if we already have seen a state */
+  protected StateSet stateSet;
+
+  /** this was the last stateId - note this is also used for stateless model checking */
+  protected int newStateId;
+
+  /** the structure responsible for storing and restoring backtrack info */
+  protected Backtracker backtracker;
+
+  /** optional serializer/restorer to support backtracker */
+  protected StateRestorer<?> restorer;
+
+  /** optional serializer to support stateSet */
+  protected StateSerializer serializer;
+
+  /** potential execution listeners. We keep them in a simple array to avoid
+   creating objects on each notification */
+  protected VMListener[] listeners = new VMListener[0];
+
+  /** did we get a new transition */
+  protected boolean transitionOccurred;
+
+  /** how we model execution time */
+  protected TimeModel timeModel;
+  
+  /** ThreadChoiceGenerator management related to data races and shared objects */
+  protected Scheduler scheduler;
+  
+  
+  protected Config config; // that's for the options we use only once
+
+  // VM options we use frequently
+  protected boolean runGc;
+  protected boolean treeOutput;
+  protected boolean pathOutput;
+  protected boolean indentOutput;
+  protected boolean processFinalizers;
+  
+  // <2do> there are probably many places where this should be used
+  protected boolean isBigEndian;
+
+  protected boolean initialized;
+
+  //thread filters
+  protected Predicate<ThreadInfo> userliveNonDaemonPredicate;
+  protected Predicate<ThreadInfo> timedoutRunnablePredicate;
+  protected Predicate<ThreadInfo> alivePredicate;
+  protected Predicate<ThreadInfo> userTimedoutRunnablePredicate;
+
+  // a list of actions to be run post GC. This is a bit redundant to VMListener,
+  // but in addition to avoid the per-instruction execution overhead of a VMListener
+  // we want a (internal) mechanism that is on-demand only, i.e. processed
+  // actions are removed from the list
+  protected ArrayList<Runnable> postGcActions = new ArrayList<Runnable>();
+  
+  /**
+   * be prepared this might throw JPFConfigExceptions
+   */
+  public VM (JPF jpf, Config conf) {
+    this.jpf = jpf; // so that we know who instantiated us
+
+    // <2do> that's really a bad hack and should be removed once we
+    // have cleaned up the reference chains
+    vm = this;
+
+    config = conf;
+
+    runGc = config.getBoolean("vm.gc", true);
+
+    treeOutput = config.getBoolean("vm.tree_output", true);
+    // we have to defer setting pathOutput until we have a reporter registered
+    indentOutput = config.getBoolean("vm.indent_output",false);
+
+    processFinalizers = config.getBoolean("vm.process_finalizers", false);
+    
+    isBigEndian = getPlatformEndianness(config);
+    initialized = false;
+    
+    initTimeModel(config);
+
+    initSubsystems(config);
+    initFields(config);
+    
+    // set predicates used to query from threadlist
+    userliveNonDaemonPredicate = new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (!ti.isDaemon() && !ti.isTerminated() && !ti.isSystemThread());
+      }
+    };
+
+    timedoutRunnablePredicate = new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (ti.isTimeoutRunnable());
+      }
+    };
+    
+    userTimedoutRunnablePredicate = new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (ti.isTimeoutRunnable() && !ti.isSystemThread());
+      }
+    };
+    
+    alivePredicate = new Predicate<ThreadInfo>() {
+      @Override
+       public boolean isTrue (ThreadInfo ti) {
+        return (ti.isAlive());
+      }
+    };
+  }
+
+  /**
+   * just here for unit test mockups, don't use as implicit base ctor in
+   * VM derived classes
+   */
+  protected VM (){}
+
+  public JPF getJPF() {
+    return jpf;
+  }
+
+  public void initFields (Config config) {
+    path = new Path("fix-this!");
+    out = null;
+
+    ss = new SystemState(config, this);
+
+    stateSet = config.getInstance("vm.storage.class", StateSet.class);
+    if (stateSet != null) stateSet.attach(this);
+    backtracker = config.getEssentialInstance("vm.backtracker.class", Backtracker.class);
+    backtracker.attach(this);
+
+    scheduler = config.getEssentialInstance("vm.scheduler.class", Scheduler.class);
+    
+    newStateId = -1;
+  }
+
+  protected void initSubsystems (Config config) {
+    ClassLoaderInfo.init(config);
+    ClassInfo.init(config);
+    ThreadInfo.init(config);
+    ElementInfo.init(config);
+    MethodInfo.init(config);
+    NativePeer.init(config);
+    ChoiceGeneratorBase.init(config);
+    
+    // peer classes get initialized upon NativePeer creation
+  }
+
+  protected void initTimeModel (Config config){
+    Class<?>[] argTypes = { VM.class, Config.class };
+    Object[] args = { this, config };
+    timeModel = config.getEssentialInstance("vm.time.class", TimeModel.class, argTypes, args);
+  }
+  
+  /**
+   * called after the JPF run is finished. Shouldn't be public, but is called by JPF
+   */
+  public void cleanUp(){
+    // nothing yet
+  }
+  
+  protected boolean getPlatformEndianness (Config config){
+    String endianness = config.getString("vm.endian");
+    if (endianness == null) {
+      return ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN;
+    } else if (endianness.equalsIgnoreCase("big")) {
+      return true;
+    } else if (endianness.equalsIgnoreCase("little")) {
+      return false;
+    } else {
+      config.exception("illegal vm.endian value: " + endianness);
+      return false; // doesn't matter
+    }
+  }
+  
+  public boolean isBigEndianPlatform(){
+    return isBigEndian;
+  }
+
+  public boolean finalizersEnabled() {
+    return processFinalizers;
+  }
+  
+  public boolean isInitialized() {
+    return initialized;
+  }
+  
+  public boolean isSingleProcess() {
+    return true;
+  }
+
+  /**
+   * do we see our model classes? Some of them cannot be used from the standard CLASSPATH, because they
+   * are tightly coupled with the JPF core (e.g. java.lang.Class, java.lang.Thread,
+   * java.lang.StackTraceElement etc.)
+   * Our strategy here is kind of lame - we just look into java.lang.Class if we find the 'uniqueId' field
+   * (that's a true '42')
+   */
+  static boolean checkSystemClassCompatibility (SystemClassLoaderInfo systemLoader) {
+    ClassInfo ci = systemLoader.getClassClassInfo();
+    return ci.checkIfValidClassClassInfo();
+  }
+
+  static boolean isValidClassName (String clsName) {
+    if ( !clsName.matches("[a-zA-Z_$][a-zA-Z_$0-9.]*")) {
+      return false;
+    }
+
+    // well, those two could be part of valid class names, but
+    // in all likeliness somebody specified a filename instead of
+    // a classname
+    if (clsName.endsWith(".java")) {
+      return false;
+    }
+    if (clsName.endsWith(".class")) {
+      return false;
+    }
+
+    return true;
+  }
+
+  //--- ThreadInfo factory methods
+  protected ThreadInfo createMainThreadInfo (int id, ApplicationContext appCtx){
+    ThreadInfo tiMain = new ThreadInfo( this, id, appCtx);
+    ThreadInfo.currentThread = tiMain; // we still need this for listeners that process startup class loading events
+    registerThread(tiMain);
+    
+    return tiMain;
+  }
+  
+  protected ThreadInfo createThreadInfo (int objRef, int groupRef, int runnableRef, int nameRef){
+    ThreadInfo tiCurrent = ThreadInfo.getCurrentThread();
+    ThreadInfo tiNew = new ThreadInfo( this, objRef, groupRef, runnableRef, nameRef, tiCurrent);
+
+    // note that we have to register here so that subsequent native peer calls can use the objRef
+    // to lookup the ThreadInfo. This is a bit premature since the thread is not runnable yet,
+    // but chances are it will be started soon, so we don't waste another data structure to do the mapping
+    registerThread( tiNew);
+    
+    return tiNew;
+  }
+
+  // created if the option "vm.process_finalizers" is set to true
+  protected ThreadInfo createFinalizerThreadInfo (int id, ApplicationContext appCtx){
+    FinalizerThreadInfo finalizerTi = new FinalizerThreadInfo( this, appCtx, id);
+    registerThread(finalizerTi);
+    
+    return finalizerTi;
+  }
+  
+  /**
+   * the minimal set of system classes we need for initialization
+   */
+  protected List<String> getStartupSystemClassNames() {
+    ArrayList<String> startupClasses = new ArrayList<String>(64);
+
+    // bare essentials
+    startupClasses.add("java.lang.Object");
+    startupClasses.add("java.lang.Class");
+    startupClasses.add("java.lang.ClassLoader");
+
+    // the builtin types (and their arrays)
+    startupClasses.add("boolean");
+    startupClasses.add("[Z");
+    startupClasses.add("byte");
+    startupClasses.add("[B");
+    startupClasses.add("char");
+    startupClasses.add("[C");
+    startupClasses.add("short");
+    startupClasses.add("[S");
+    startupClasses.add("int");
+    startupClasses.add("[I");
+    startupClasses.add("long");
+    startupClasses.add("[J");
+    startupClasses.add("float");
+    startupClasses.add("[F");
+    startupClasses.add("double");
+    startupClasses.add("[D");
+    startupClasses.add("void");
+
+    // the box types
+    startupClasses.add("java.lang.Boolean");
+    startupClasses.add("java.lang.Character");
+    startupClasses.add("java.lang.Short");
+    startupClasses.add("java.lang.Integer");
+    startupClasses.add("java.lang.Long");
+    startupClasses.add("java.lang.Float");
+    startupClasses.add("java.lang.Double");
+    startupClasses.add("java.lang.Byte");
+
+    // the cache for box types
+    startupClasses.add("gov.nasa.jpf.BoxObjectCaches");
+
+    // standard system classes
+    startupClasses.add("java.lang.String");
+    startupClasses.add("java.lang.Thread");
+    startupClasses.add("java.lang.ThreadGroup");
+    startupClasses.add("java.lang.Thread$State");
+    startupClasses.add("java.lang.Thread$Permit");
+    startupClasses.add("java.io.PrintStream");
+    startupClasses.add("java.io.InputStream");
+    startupClasses.add("java.lang.System");
+    startupClasses.add("java.lang.ref.Reference");
+    startupClasses.add("java.lang.ref.WeakReference");
+    startupClasses.add("java.lang.Enum");
+    startupClasses.add("gov.nasa.jpf.FinalizerThread");
+
+    // we could be more fancy and use wildcard patterns and the current classpath
+    // to specify extra classes, but this could be VERY expensive. Projected use
+    // is mostly to avoid static init of single classes during the search
+    String[] extraStartupClasses = config.getStringArray("vm.extra_startup_classes");
+    if (extraStartupClasses != null) {      
+      for (String extraCls : extraStartupClasses) {
+        startupClasses.add(extraCls);
+      }
+    }
+
+    // the main class has to be handled separately since it might be VM specific
+
+    return startupClasses;
+  }
+
+  /**
+   * return a list of ClassInfos for essential system types
+   * 
+   * If system classes are not found, or are not valid JPF model classes, we throw
+   * a JPFConfigException and exit
+   * 
+   * returned ClassInfos are not yet registered in Statics and don't have class objects
+   */
+  protected List<ClassInfo> getStartupSystemClassInfos (SystemClassLoaderInfo sysCl, ThreadInfo tiMain){
+    LinkedList<ClassInfo> list = new LinkedList<ClassInfo>();
+    
+    try {
+      for (String clsName : getStartupSystemClassNames()) {
+        ClassInfo ci = sysCl.getResolvedClassInfo(clsName);
+        ci.registerStartupClass( tiMain, list); // takes care of superclasses and interfaces
+      }
+    } catch (ClassInfoException e){
+      e.printStackTrace();
+      throw new JPFConfigException("cannot load system class " + e.getFailedClass());
+    } 
+    
+    return list;
+  }
+  
+  /**
+   * this adds the application main class and its supers to the list of startup classes 
+   */
+  protected ClassInfo getMainClassInfo (SystemClassLoaderInfo sysCl, String mainClassName, ThreadInfo tiMain, List<ClassInfo> list){
+    try {
+      ClassInfo ciMain = sysCl.getResolvedClassInfo(mainClassName);
+      ciMain.registerStartupClass(tiMain, list); // this might add a couple more
+      
+      return ciMain;
+      
+    } catch (ClassInfoException e){
+      throw new JPFConfigException("cannot load application class " + e.getFailedClass());
+    }
+  }
+  
+  /*
+   * these are called when creating ApplicationContexts and can be overridden by concrete VM types 
+   */
+  protected SystemClassLoaderInfo createSystemClassLoaderInfo (int appId) {
+    Class<?>[] argTypes = { VM.class, int.class };
+   
+    Object[] args = { this, Integer.valueOf(appId)};
+    SystemClassLoaderInfo sysCli = config.getEssentialInstance("vm.classloader.class", SystemClassLoaderInfo.class, argTypes, args);
+    return sysCli;
+  }
+  
+  protected void createSystemClassLoaderObject (SystemClassLoaderInfo sysCl, ThreadInfo tiMain) {
+    Heap heap = getHeap();
+
+    // create ClassLoader object for the ClassLoader type defined by this SystemClassLoaderInfo
+    // NOTE - this requires the SystemClassLoaderInfo cache to be initialized
+    ClassInfo ciCl = sysCl.getClassLoaderClassInfo();
+    ElementInfo ei = heap.newObject( ciCl, tiMain);
+    //ei.setReferenceField("parent", MJIEnv.NULL);
+    heap.registerPinDown(ei.getObjectRef());
+
+    sysCl.setClassLoaderObject(ei);
+  }  
+  
+  protected void pushMainEntryArgs (MethodInfo miMain, String[] args, ThreadInfo tiMain, DirectCallStackFrame frame){
+    String sig = miMain.getSignature();
+    Heap heap = getHeap();
+    
+    if (sig.contains("([Ljava/lang/String;)")){
+      ElementInfo eiArgs = heap.newArray("Ljava/lang/String;", args.length, tiMain);
+      for (int i = 0; i < args.length; i++) {
+        ElementInfo eiArg = heap.newString(args[i], tiMain);
+        eiArgs.setReferenceElement(i, eiArg.getObjectRef());
+      }
+      frame.setReferenceArgument( 0, eiArgs.getObjectRef(), null);
+
+    } else if (sig.contains("(Ljava/lang/String;)")){
+      if (args.length > 1){
+        ElementInfo eiArg = heap.newString(args[0], tiMain);
+        frame.setReferenceArgument( 0, eiArg.getObjectRef(), null);
+      } else {
+        frame.setReferenceArgument( 0, MJIEnv.NULL, null);
+      }
+      
+    } else if (!sig.contains("()")){
+      throw new JPFException("unsupported main entry signature: " + miMain.getFullName());
+    }
+  }
+  
+  protected void pushMainEntry (MethodInfo miMain, String[] args, ThreadInfo tiMain) {
+    DirectCallStackFrame frame = miMain.createDirectCallStackFrame(tiMain, 0);
+    pushMainEntryArgs( miMain, args, tiMain, frame);    
+    tiMain.pushFrame(frame);
+  }
+
+  protected MethodInfo getMainEntryMethodInfo (String mthName, ClassInfo ciMain) {
+    MethodInfo miMain = ciMain.getMethod(mthName, true);
+
+    //--- do some sanity checks if this is a valid entry method
+    if (miMain == null || !miMain.isStatic()) {
+      throw new JPFConfigException("no static entry method: " + ciMain.getName() + '.' + mthName);
+    }
+    
+    return miMain;
+  }
+  
+  protected void pushClinits (List<ClassInfo> startupClasses, ThreadInfo tiMain) {
+    for (ClassInfo ci : startupClasses){
+      MethodInfo mi = ci.getMethod("<clinit>()V", false);
+      if (mi != null) {
+        DirectCallStackFrame frame = mi.createDirectCallStackFrame(tiMain, 0);
+        tiMain.pushFrame(frame);
+      } else {
+        ci.setInitialized();
+      }      
+    }
+  }
+  
+  /**
+   * this is the main initialization point that sets up startup objects threads and callstacks.
+   * If this returns false VM initialization cannot proceed and JPF will terminate
+   */
+  public abstract boolean initialize ();
+  
+  /**
+   * create and initialize the main thread for the given ApplicationContext.
+   * This is called from VM.initialize() implementations, the caller has to handle exceptions that should be reported
+   * differently (JPFConfigException, ClassInfoException)
+   */
+  protected ThreadInfo initializeMainThread (ApplicationContext appCtx, int tid){
+    SystemClassLoaderInfo sysCl = appCtx.sysCl;
+    
+    ThreadInfo tiMain = createMainThreadInfo(tid, appCtx);
+    List<ClassInfo> startupClasses = getStartupSystemClassInfos(sysCl, tiMain);
+    ClassInfo ciMain = getMainClassInfo(sysCl, appCtx.mainClassName, tiMain, startupClasses);
+
+    if (!checkSystemClassCompatibility( sysCl)){
+      throw new JPFConfigException("non-JPF system classes, check classpath");
+    }
+    
+    // create essential objects (we can't call ctors yet)
+    createSystemClassLoaderObject(sysCl, tiMain);
+    for (ClassInfo ci : startupClasses) {
+      ci.createAndLinkStartupClassObject(tiMain);
+    }
+    tiMain.createMainThreadObject(sysCl);
+    registerThread(tiMain);
+    
+    // note that StackFrames have to be pushed in reverse order
+    MethodInfo miMain = getMainEntryMethodInfo(appCtx.mainEntry, ciMain);
+    appCtx.setEntryMethod(miMain);
+    pushMainEntry(miMain, appCtx.args, tiMain);
+    Collections.reverse(startupClasses);
+    pushClinits(startupClasses, tiMain);
+
+    registerThreadListCleanup(sysCl.getThreadClassInfo());
+
+    return tiMain;
+  }
+  
+  protected void initializeFinalizerThread (ApplicationContext appCtx, int tid) {
+    if(processFinalizers) {
+      ApplicationContext app = getCurrentApplicationContext();
+      FinalizerThreadInfo finalizerTi = app.getFinalizerThread();
+    
+      finalizerTi = (FinalizerThreadInfo) createFinalizerThreadInfo(tid, app);
+      finalizerTi.createFinalizerThreadObject(app.getSystemClassLoader());
+    
+      appCtx.setFinalizerThread(finalizerTi);
+    }
+  }
+  
+  protected void registerThreadListCleanup (ClassInfo ciThread){
+    assert ciThread != null : "java.lang.Thread not loaded yet";
+    
+    ciThread.addReleaseAction( new ReleaseAction(){
+      @Override
+       public void release (ElementInfo ei) {
+        ThreadList tl = getThreadList();
+        int objRef = ei.getObjectRef();
+        ThreadInfo ti = tl.getThreadInfoForObjRef(objRef);
+        if (tl.remove(ti)){        
+          vm.getKernelState().changed();    
+        }
+      }
+    });
+  }
+  
+
+  /**
+   * override this if the concrete VM needs a special root CG
+   */
+  protected void setRootCG(){
+    scheduler.setRootCG();
+  }
+  
+  protected void initSystemState (ThreadInfo mainThread){
+    ss.setStartThread(mainThread);
+
+    ss.recordSteps(hasToRecordSteps());
+
+    if (!pathOutput) { // don't override if explicitly requested
+      pathOutput = hasToRecordPathOutput();
+    }
+
+    setRootCG(); // this has to be guaranteed to register a CG
+    if (!hasNextChoiceGenerator()){
+      throw new JPFException("scheduler failed to set ROOT choice generator: " + scheduler);
+    }
+    
+    transitionOccurred = true;
+  }
+  
+  public void addPostGcAction (Runnable r){
+    postGcActions.add(r);
+  }
+  
+  /**
+   * to be called from the Heap after GC is completed (i.e. only live objects remain)
+   */
+  public void processPostGcActions(){
+    if (!postGcActions.isEmpty()){
+      for (Runnable r : postGcActions){
+        r.run();
+      }
+      
+      postGcActions.clear();
+    }
+  }
+  
+  public void addListener (VMListener newListener) {
+    log.info("VMListener added: ", newListener);
+    listeners = Misc.appendElement(listeners, newListener);
+  }
+
+  public boolean hasListenerOfType (Class<?> listenerCls) {
+    return Misc.hasElementOfType(listeners, listenerCls);
+  }
+
+  public <T> T getNextListenerOfType(Class<T> type, T prev){
+    return Misc.getNextElementOfType(listeners, type, prev);
+  }
+  
+  public void removeListener (VMListener removeListener) {
+    listeners = Misc.removeElement(listeners, removeListener);
+  }
+
+  public void setTraceReplay (boolean isReplay) {
+    isTraceReplay = isReplay;
+  }
+
+  public boolean isTraceReplay() {
+    return isTraceReplay;
+  }
+
+  public boolean hasToRecordSteps() {
+    // we have to record if there either is a reporter that has
+    // a 'trace' topic, or there is an explicit request
+    return jpf.getReporter().hasToReportTrace()
+             || config.getBoolean("vm.store_steps");
+  }
+
+  public void recordSteps( boolean cond) {
+    // <2do> not ideal - it might be already too late when this is called
+
+    config.setProperty("vm.store_steps", cond ? "true" : "false");
+
+    if (ss != null){
+      ss.recordSteps(cond);
+    }
+  }
+
+  public boolean hasToRecordPathOutput() {
+    if (config.getBoolean("vm.path_output")){ // explicitly requested
+      return true;
+    } else {
+      return jpf.getReporter().hasToReportOutput(); // implicilty required
+    }
+  }
+  
+  //--- VM listener notifications
+  
+  /*
+   * while some of these can be called from various places, the calls that happen from within Instruction.execute() should
+   * happen right before return since listeners might do things such as ThreadInfo.createAndThrowException(..), i.e. cause
+   * side effects that would violate consistency requirements of successive operations (e.g. by assuming we are still executing
+   * in the same StackFrame - after throwing an exception)
+   */
+  
+  protected void notifyVMInitialized () {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].vmInitialized(this);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during vmInitialized() notification", t);
+    }    
+  }
+  
+  protected void notifyChoiceGeneratorRegistered (ChoiceGenerator<?>cg, ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].choiceGeneratorRegistered(this, cg, ti, ti.getPC());
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during choiceGeneratorRegistered() notification", t);
+    }
+  }
+
+  protected void notifyChoiceGeneratorSet (ChoiceGenerator<?>cg) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].choiceGeneratorSet(this, cg);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during choiceGeneratorSet() notification", t);
+    }
+  }
+
+  protected void notifyChoiceGeneratorAdvanced (ChoiceGenerator<?>cg) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].choiceGeneratorAdvanced(this, cg);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during choiceGeneratorAdvanced() notification", t);
+    }
+  }
+
+  protected void notifyChoiceGeneratorProcessed (ChoiceGenerator<?>cg) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].choiceGeneratorProcessed(this, cg);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during choiceGeneratorProcessed() notification", t);
+    }
+  }
+
+  protected void notifyExecuteInstruction (ThreadInfo ti, Instruction insn) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].executeInstruction(this, ti, insn);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during executeInstruction() notification", t);
+    }
+  }
+
+  protected void notifyInstructionExecuted (ThreadInfo ti, Instruction insn, Instruction nextInsn) {
+    try {
+      //listener.instructionExecuted(this);
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].instructionExecuted(this, ti, nextInsn, insn);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during instructionExecuted() notification", t);
+    }
+  }
+
+  protected void notifyThreadStarted (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadStarted(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadStarted() notification", t);
+    }
+  }
+
+  // NOTE: the supplied ThreadInfo does NOT have to be the running thread, as this
+  // notification can occur as a result of a lock operation in the current thread
+  protected void notifyThreadBlocked (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadBlocked(this, ti, ti.getLockObject());
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadBlocked() notification", t);
+    }
+  }
+
+  protected void notifyThreadWaiting (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadWaiting(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadWaiting() notification", t);
+    }
+  }
+
+  protected void notifyThreadNotified (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadNotified(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadNotified() notification", t);
+    }
+  }
+
+  protected void notifyThreadInterrupted (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadInterrupted(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadInterrupted() notification", t);
+    }
+  }
+
+  protected void notifyThreadTerminated (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadTerminated(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadTerminated() notification", t);
+    }
+  }
+
+  protected void notifyThreadScheduled (ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].threadScheduled(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during threadScheduled() notification", t);
+    }
+  }
+  
+  protected void notifyLoadClass (ClassFile cf){
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].loadClass(this, cf);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during classLoaded() notification", t);
+    }    
+  }
+
+  protected void notifyClassLoaded(ClassInfo ci) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].classLoaded(this, ci);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during classLoaded() notification", t);
+    }
+  }
+
+  protected void notifyObjectCreated(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectCreated(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectCreated() notification", t);
+    }
+  }
+
+  protected void notifyObjectReleased(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectReleased(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectReleased() notification", t);
+    }
+  }
+
+  protected void notifyObjectLocked(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectLocked(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectLocked() notification", t);
+    }
+  }
+
+  protected void notifyObjectUnlocked(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectUnlocked(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectUnlocked() notification", t);
+    }
+  }
+
+  protected void notifyObjectWait(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectWait(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectWait() notification", t);
+    }
+  }
+
+   protected void notifyObjectExposed(ThreadInfo ti, ElementInfo eiShared, ElementInfo eiExposed) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectExposed(this, ti, eiShared, eiExposed);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectExposed() notification", t);
+    }
+  }
+
+   protected void notifyObjectShared(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectShared(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectShared() notification", t);
+    }
+  }
+  
+  protected void notifyObjectNotifies(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectNotify(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectNotifies() notification", t);
+    }
+  }
+
+  protected void notifyObjectNotifiesAll(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].objectNotifyAll(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during objectNotifiesAll() notification", t);
+    }
+  }
+
+  protected void notifyGCBegin() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].gcBegin(this);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during gcBegin() notification", t);
+    }
+  }
+
+  protected void notifyGCEnd() {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].gcEnd(this);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during gcEnd() notification", t);
+    }
+  }
+
+  protected void notifyExceptionThrown(ThreadInfo ti, ElementInfo ei) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].exceptionThrown(this, ti, ei);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during exceptionThrown() notification", t);
+    }
+  }
+
+  protected void notifyExceptionBailout(ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].exceptionBailout(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during exceptionBailout() notification", t);
+    }
+  }
+
+  protected void notifyExceptionHandled(ThreadInfo ti) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].exceptionHandled(this, ti);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during exceptionHandled() notification", t);
+    }
+  }
+
+  protected void notifyMethodEntered(ThreadInfo ti, MethodInfo mi) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].methodEntered(this, ti, mi);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during methodEntered() notification", t);
+    }
+  }
+
+  protected void notifyMethodExited(ThreadInfo ti, MethodInfo mi) {
+    try {
+      for (int i = 0; i < listeners.length; i++) {
+        listeners[i].methodExited(this, ti, mi);
+      }
+    } catch (UncaughtException x) {
+      throw x;
+    } catch (JPF.ExitException x) {
+      throw x;
+    } catch (Throwable t) {
+      throw new JPFListenerException("exception during methodExited() notification", t);
+    }
+  }
+
+  // VMListener acquisition
+  public String getThreadName () {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+
+    return ti.getName();
+  }
+
+  // VMListener acquisition
+  public Instruction getInstruction () {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    return ti.getPC();
+  }
+
+  /**
+   * note this is gone after backtracking or starting the next exception
+   */
+  public ExceptionInfo getPendingException () {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+
+    if (ti != null){
+      return ti.getPendingException();
+    } else {
+      return null;
+    }
+  }
+
+  public Step getLastStep () {
+    Transition trail = ss.getTrail();
+    if (trail != null) {
+      return trail.getLastStep();
+    }
+
+    return null;
+  }
+
+  public Transition getLastTransition () {
+    if (path.size() == 0) {
+      return null;
+    }
+    return path.get(path.size() - 1);
+  }
+
+  public ClassInfo getClassInfo (int objref) {
+    if (objref != MJIEnv.NULL) {
+      return getElementInfo(objref).getClassInfo();
+    } else {
+      return null;
+    }
+  }
+
+  /**
+   * NOTE: only use this locally, since the path is getting modified by the VM
+   *
+   * The path only contains all states when queried from a stateAdvanced() notification.
+   * If this is called from an instructionExecuted() (or other VMListener), and you need
+   * the ongoing transition in it, you have to call updatePath() first
+   */
+  public Path getPath () {
+    return path;
+  }
+
+  /**
+   * this is the ongoing transition. Note that it is not yet stored in the path
+   * if this is called from a VMListener notification
+   */
+  public Transition getCurrentTransition() {
+    return ss.getTrail();
+  }
+
+  /**
+   * use that one if you have to store the path for subsequent use
+   *
+   * NOTE: without a prior call to updatePath(), this does NOT contain the
+   * ongoing transition. See getPath() for usage from a VMListener
+   */
+  public Path getClonedPath () {
+    return path.clone();
+  }
+
+  public int getPathLength () {
+    return path.size();
+  }
+
+  public ThreadList getThreadList () {
+    return getKernelState().getThreadList();
+  }
+  
+  public ClassLoaderList getClassLoaderList() {
+    return getKernelState().getClassLoaderList();
+  }
+
+  
+  /**
+   * Bundles up the state of the system for export
+   */
+  public RestorableVMState getRestorableState () {
+    return new RestorableVMState(this);
+  }
+
+  /**
+   * Gets the system state.
+   */
+  public SystemState getSystemState () {
+    return ss;
+  }
+
+  public KernelState getKernelState () {
+    return ss.getKernelState();
+  }
+
+  public void kernelStateChanged(){
+    ss.getKernelState().changed();
+  }
+  
+  public Config getConfig() {
+    return config;
+  }
+
+  public Backtracker getBacktracker() {
+    return backtracker;
+  }
+
+  @SuppressWarnings("unchecked")
+  public <T> StateRestorer<T> getRestorer() {
+    if (restorer == null) {
+      if (serializer instanceof StateRestorer) {
+        restorer = (StateRestorer<?>) serializer;
+      } else if (stateSet instanceof StateRestorer) {
+        restorer = (StateRestorer<?>) stateSet;
+      } else {
+        // config read only if serializer is not also a restorer
+        restorer = config.getInstance("vm.restorer.class", StateRestorer.class);
+      }
+      restorer.attach(this);
+    }
+
+    return (StateRestorer<T>) restorer;
+  }
+
+  public StateSerializer getSerializer() {
+    if (serializer == null) {
+      serializer = config.getEssentialInstance("vm.serializer.class",
+                                      StateSerializer.class);
+      serializer.attach(this);
+    }
+    return serializer;
+  }
+
+  public void setSerializer (StateSerializer newSerializer){
+    serializer = newSerializer;
+    serializer.attach(this);
+  }
+  
+  /**
+   * Returns the stateSet if states are being matched.
+   */
+  public StateSet getStateSet() {
+    return stateSet;
+  }
+
+  public Scheduler getScheduler(){
+    return scheduler;
+  }
+  
+  public FunctionObjectFactory getFunctionObjectFacotry() {
+    return funcObjFactory;
+  }
+  
+  /**
+   * return the last registered SystemState's ChoiceGenerator object
+   * NOTE: there might be more than one ChoiceGenerator associated with the
+   * current transition (ChoiceGenerators can be cascaded)
+   */
+  public ChoiceGenerator<?> getChoiceGenerator () {
+    return ss.getChoiceGenerator();
+  }
+
+  public ChoiceGenerator<?> getNextChoiceGenerator() {
+    return ss.getNextChoiceGenerator();
+  }
+  
+  public boolean hasNextChoiceGenerator(){
+    return (ss.getNextChoiceGenerator() != null);
+  }
+
+  public boolean setNextChoiceGenerator (ChoiceGenerator<?> cg){
+    return ss.setNextChoiceGenerator(cg);
+  }
+  
+  public void setMandatoryNextChoiceGenerator (ChoiceGenerator<?> cg, String failMsg){
+    ss.setMandatoryNextChoiceGenerator(cg, failMsg);
+  }
+  
+  /**
+   * return the latest registered ChoiceGenerator used in this transition
+   * that matches the provided 'id' and is of 'cgType'.
+   * 
+   * This should be the main getter for clients that are cascade aware
+   */
+  public <T extends ChoiceGenerator<?>> T getCurrentChoiceGenerator (String id, Class<T> cgType) {
+    return ss.getCurrentChoiceGenerator(id,cgType);
+  }
+
+  /**
+   * returns all ChoiceGenerators in current path
+   */
+  public ChoiceGenerator<?>[] getChoiceGenerators() {
+    return ss.getChoiceGenerators();
+  }
+
+  public <T extends ChoiceGenerator<?>> T[] getChoiceGeneratorsOfType (Class<T> cgType) {
+    return ss.getChoiceGeneratorsOfType(cgType);
+  }
+
+  public <T extends ChoiceGenerator<?>> T getLastChoiceGeneratorOfType (Class<T> cgType){
+    return ss.getLastChoiceGeneratorOfType(cgType);
+  }
+
+  public ChoiceGenerator<?> getLastChoiceGeneratorInThread (ThreadInfo ti){
+    return ss.getLastChoiceGeneratorInThread(ti);
+  }
+  
+  public void print (String s) {
+    if (treeOutput) {
+      System.out.print(s);
+    }
+
+    if (pathOutput) {
+      appendOutput(s);
+    }
+  }
+
+  public void println (String s) {
+    if (treeOutput) {
+      if (indentOutput){
+        StringBuilder indent = new StringBuilder();
+        int i;
+        for (i = 0;i<=path.size();i++) {
+          indent.append('|').append(i);
+        }
+        indent.append("|").append(s);
+        System.out.println(indent);
+      }
+      else {
+        System.out.println(s);
+      }
+    }
+
+    if (pathOutput) {
+      appendOutput(s);
+      appendOutput('\n');
+    }
+  }
+
+  public void print (boolean b) {
+    if (treeOutput) {
+      System.out.print(b);
+    }
+
+    if (pathOutput) {
+      appendOutput(Boolean.toString(b));
+    }
+  }
+
+  public void print (char c) {
+    if (treeOutput) {
+      System.out.print(c);
+    }
+
+    if (pathOutput) {
+      appendOutput(c);
+    }
+  }
+
+  public void print (int i) {
+    if (treeOutput) {
+      System.out.print(i);
+    }
+
+    if (pathOutput) {
+      appendOutput(Integer.toString(i));
+    }
+  }
+
+  public void print (long l) {
+    if (treeOutput) {
+      System.out.print(l);
+    }
+
+    if (pathOutput) {
+      appendOutput(Long.toString(l));
+    }
+  }
+
+  public void print (double d) {
+    if (treeOutput) {
+      System.out.print(d);
+    }
+
+    if (pathOutput) {
+      appendOutput(Double.toString(d));
+    }
+  }
+
+  public void print (float f) {
+    if (treeOutput) {
+      System.out.print(f);
+    }
+
+    if (pathOutput) {
+      appendOutput(Float.toString(f));
+    }
+  }
+
+  public void println () {
+    if (treeOutput) {
+      System.out.println();
+    }
+
+    if (pathOutput) {
+      appendOutput('\n');
+    }
+  }
+
+
+  void appendOutput (String s) {
+    if (out == null) {
+      out = new StringBuilder();
+    }
+    out.append(s);
+  }
+
+  void appendOutput (char c) {
+    if (out == null) {
+      out = new StringBuilder();
+    }
+    out.append(c);
+  }
+
+  /**
+   * get the pending output (not yet stored in the path)
+   */
+  public String getPendingOutput() {
+    if (out != null && out.length() > 0){
+      return out.toString();
+    } else {
+      return null;
+    }
+  }
+  
+  /**
+   * this is here so that we can intercept it in subclassed VMs
+   */
+  public Instruction handleException (ThreadInfo ti, int xObjRef){
+    ti = null;        // Get rid of IDE warning
+    xObjRef = 0;
+    return null;
+  }
+
+  public void storeTrace (String fileName, String comment, boolean verbose) {
+    ChoicePoint.storeTrace(fileName, getSUTName(), comment,
+                           ss.getChoiceGenerators(), verbose);
+  }
+
+  public void storePathOutput () {
+    pathOutput = true;
+  }
+
+  private void printCG (ChoiceGenerator<?> cg, int n){
+    ChoiceGenerator cgPrev = cg.getPreviousChoiceGenerator();
+    if (cgPrev != null){
+      printCG( cgPrev, --n);
+    }
+    
+    System.out.printf("[%d] ", n);
+    System.out.println(cg);
+  } 
+  
+  // for debugging purposes
+  public void printChoiceGeneratorStack(){
+    ChoiceGenerator<?> cg = getChoiceGenerator();
+    if (cg != null){
+      int n = cg.getNumberOfParents();
+      printCG(cg, n);
+    }
+  }
+  
+  public ThreadInfo[] getLiveThreads () {
+    return getThreadList().getThreads();
+  }
+  
+  /**
+   * print call stacks of all live threads
+   * this is also used for debugging purposes, so we can't move it to the Reporter system
+   * (it's also using a bit too many internals for that)
+   */
+  public void printLiveThreadStatus (PrintWriter pw) {
+    int nThreads = ss.getThreadCount();
+    ThreadInfo[] threads = getThreadList().getThreads();
+    int n=0;
+
+    for (int i = 0; i < nThreads; i++) {
+      ThreadInfo ti = threads[i];
+
+      if (ti.getStackDepth() > 0){
+        n++;
+        //pw.print("Thread: ");
+        //pw.print(tiMain.getName());
+        pw.println(ti.getStateDescription());
+
+        List<ElementInfo> locks = ti.getLockedObjects();
+        if (!locks.isEmpty()) {
+          pw.print("  owned locks:");
+          boolean first = true;
+          for (ElementInfo e : locks) {
+            if (first) {
+              first = false;
+            } else {
+              pw.print(",");
+            }
+            pw.print(e);
+          }
+          pw.println();
+        }
+
+        ElementInfo ei = ti.getLockObject();
+        if (ei != null) {
+          if (ti.getState() == ThreadInfo.State.WAITING) {
+            pw.print( "  waiting on: ");
+          } else {
+            pw.print( "  blocked on: ");
+          }
+          pw.println(ei);
+        }
+
+        pw.println("  call stack:");
+        for (StackFrame frame : ti){
+          if (!frame.isDirectCallFrame()) {
+            pw.print("\tat ");
+            pw.println(frame.getStackTraceInfo());
+          }
+        }
+
+        pw.println();
+      }
+    }
+
+    if (n==0) {
+      pw.println("no live threads");
+    }
+  }
+
+  // just a debugging aid
+  public void dumpThreadStates () {
+    java.io.PrintWriter pw = new java.io.PrintWriter(System.out, true);
+    printLiveThreadStatus(pw);
+    pw.flush();
+  }
+
+  /**
+   * Moves one step backward. This method and forward() are the main methods
+   * used by the search object.
+   * Note this is called with the state that caused the backtrack still being on
+   * the stack, so we have to remove that one first (i.e. popping two states
+   * and restoring the second one)
+   */
+  public boolean backtrack () {
+    transitionOccurred = false;
+
+    boolean success = backtracker.backtrack();
+    if (success) {
+      if (CHECK_CONSISTENCY) checkConsistency(false);
+      
+      // restore the path
+      path.removeLast();
+      lastTrailInfo = path.getLast();
+
+      return true;
+      
+    } else {
+      return false;
+    }
+  }
+
+  /**
+   * store the current SystemState's Trail in our path, after updating it
+   * with whatever annotations the VM wants to add.
+   * This is supposed to be called after each transition we want to keep
+   */
+  public void updatePath () {
+    Transition t = ss.getTrail();
+    Transition tLast = path.getLast();
+
+    // NOTE: don't add the transition twice, this is public and might get called
+    // from listeners, so the transition object might get changed
+
+    if (tLast != t) {
+      // <2do> we should probably store the output directly in the TrailInfo,
+      // but this might not be our only annotation in the future
+
+      // did we have output during the last transition? If yes, add it
+      if ((out != null) && (out.length() > 0)) {
+        t.setOutput( out.toString());
+        out.setLength(0);
+      }
+
+      path.add(t);
+    }
+  }
+
+  /**
+   * advance the program state
+   *
+   * forward() and backtrack() are the two primary interfaces towards the Search
+   * driver. note that the caller still has to check if there is a next state,
+   * and if the executed instruction sequence led into a new or already visited state
+   *
+   * @return 'true' if there was an un-executed sequence out of the current state,
+   * 'false' if it was completely explored
+   *
+   */
+  public boolean forward () {
+
+    // the reason we split up CG initialization and transition execution
+    // is that program state storage is not required if the CG initialization
+    // does not produce a new choice since we have to backtrack in that case
+    // anyways. This can be caused by complete enumeration of CGs and/or by
+    // CG listener intervention (i.e. not just after backtracking). For a large
+    // number of matched or end states and ignored transitions this can be a
+    // huge saving.
+    // The downside is that CG notifications are NOT allowed anymore to change the
+    // KernelState (modify fields or thread states) since those changes would
+    // happen before storing the KernelState, and hence would make backtracking
+    // inconsistent. This is advisable anyways since all program state changes
+    // should take place during transitions, but the real snag is that this
+    // cannot be easily enforced.
+
+    // actually, it hasn't occurred yet, but will
+    transitionOccurred = ss.initializeNextTransition(this);
+    
+    if (transitionOccurred){
+      if (CHECK_CONSISTENCY) {
+        checkConsistency(true); // don't push an inconsistent state
+      }
+
+      backtracker.pushKernelState();
+
+      // cache this before we enter (and increment) the next insn(s)
+      lastTrailInfo = path.getLast();
+
+      try {
+        ss.executeNextTransition(vm);
+
+      } catch (UncaughtException e) {
+        // we don't pass this up since it means there were insns executed and we are
+        // in a consistent state
+      } // every other exception goes upwards
+
+      backtracker.pushSystemState();
+      updatePath();
+
+      if (!isIgnoredState()) {
+        // if this is ignored we are going to backtrack anyways
+        // matching states out of ignored transitions is also not a good idea
+        // because this transition is usually incomplete
+
+        if (runGc && !hasPendingException()) {
+          if(ss.gcIfNeeded()) {
+            processFinalizers();
+          }
+        }
+
+        if (stateSet != null) {
+          newStateId = stateSet.size();
+          int id = stateSet.addCurrent();
+          ss.setId(id);
+
+        } else { // this is 'state-less' model checking, i.e. we don't match states
+          ss.setId(++newStateId); // but we still should have states numbered in case listeners use the id
+        }
+      }
+      
+      return true;
+
+    } else {
+
+      return false;  // no transition occurred
+    }
+  }
+
+  /**
+   * Prints the current stack trace. Just for debugging purposes
+   */
+  public void printCurrentStackTrace () {
+    ThreadInfo th = ThreadInfo.getCurrentThread();
+
+    if (th != null) {
+      th.printStackTrace();
+    }
+  }
+
+
+  public void restoreState (RestorableVMState state) {
+    if (state.path == null) {
+      throw new JPFException("tried to restore partial VMState: " + state);
+    }
+    backtracker.restoreState(state.getBkState());
+    path = state.path.clone();
+  }
+
+  public void activateGC () {
+    ss.activateGC();
+  }
+
+
+  //--- various state attribute getters and setters (mostly forwarding to SystemState)
+
+  public void retainStateAttributes (boolean isRetained){
+    ss.retainAttributes(isRetained);
+  }
+
+  public void forceState () {
+    ss.setForced(true);
+  }
+
+  /**
+   * override the state matching - ignore this state, no matter if we changed
+   * the heap or stacks.
+   * use this with care, since it prunes whole search subtrees
+   */
+  public void ignoreState (boolean cond) {
+    ss.setIgnored(cond);
+  }
+
+  public void ignoreState(){
+    ignoreState(true);
+  }
+
+  /**
+   * imperatively break the transition to enable state matching
+   */
+  public void breakTransition (String reason) {
+    ThreadInfo ti = ThreadInfo.getCurrentThread();
+    ti.breakTransition(reason);
+  }
+
+  public boolean transitionOccurred(){
+    return transitionOccurred;
+  }
+
+  /**
+   * answers if the current state already has been visited. This is mainly
+   * used by the searches (to control backtracking), but could also be useful
+   * for observers to build up search graphs (based on the state ids)
+   *
+   * this returns true if no state has been produced yet, and false if
+   * no transition occurred after a forward call
+   */
+  public boolean isNewState() {
+
+    if (!transitionOccurred){
+      return false;
+    }
+
+    if (stateSet != null) {
+      if (ss.isForced()){
+        return true;
+      } else if (ss.isIgnored()){
+        return false;
+      } else {
+        return (newStateId == ss.getId());
+      }
+
+    } else { // stateless model checking - each transition leads to a new state
+      return true;
+    }
+  }
+
+  /**
+   * We made this to be overriden by Single/MultiprcessesVM implementations,
+   * since for MultiprcessesVM one can decide when to terminate (after the
+   * the termination of all processes or only one process).
+   * todo - that needs to be specified through the properties file
+   */
+  public abstract boolean isEndState ();
+
+  public boolean isVisitedState(){
+    return !isNewState();
+  }
+
+  public boolean isIgnoredState(){
+    return ss.isIgnored();
+  }
+
+  public boolean isInterestingState () {
+    return ss.isInteresting();
+  }
+
+  public boolean isBoringState () {
+    return ss.isBoring();
+  }
+
+  public boolean hasPendingException () {
+    return (getPendingException() != null);
+  }
+
+  public abstract boolean isDeadlocked ();
+  
+  public Exception getException () {
+    return ss.getUncaughtException();
+  }
+
+
+
+  /**
+   * get the numeric id for the current state
+   * Note: this can be called several times (by the search and observers) for
+   * every forward()/backtrack(), so we want to cache things a bit
+   */
+  public int getStateId() {
+    return ss.getId();
+  }
+
+  public int getStateCount() {
+    return newStateId;
+  }
+
+
+  /**
+   * <2do> this is a band aid to bundle all these legacy reference chains
+   * from JPFs past. The goal is to replace them with proper accessors (normally
+   * through ThreadInfo, MJIEnv or VM, which all act as facades) wherever possible,
+   * and use VM.getVM() where there is no access to such a facade. Once this
+   * has been completed, we can start refactoring the users of VM.getVM() to
+   * get access to a suitable facade. 
+   */
+  public static VM getVM () {
+    return vm;
+  }
+
+  /**
+   * not ideal to have this here since it is kind of a backlink, but it's not
+   * any better if listeners have to dig this out from JPF
+   * Note - this isn't set during initialization, since the VM object is created first
+   */
+  public Search getSearch() {
+    return jpf.getSearch();
+  }
+  
+  /**
+   * pushClinit all our static fields. Called from <clinit> and reset
+   */
+  static void initStaticFields () {
+    error_id = 0;
+  }
+
+  /**
+   *  given an object reference, it returns the ApplicationContext of the process to which
+   *  this object belongs
+   */
+  public abstract ApplicationContext getCurrentApplicationContext();
+  public abstract ApplicationContext getApplicationContext(int objRef);
+  public abstract ApplicationContext[] getApplicationContexts();
+  public abstract String getSUTName();
+  public abstract String getSUTDescription();
+
+  public abstract int getNumberOfApplications();
+  
+  public Heap getHeap() {
+    return ss.getHeap();
+  }
+
+  public ElementInfo getElementInfo(int objref){
+    return ss.getHeap().get(objref);
+  }
+
+  public ElementInfo getModifiableElementInfo(int objref){
+    return ss.getHeap().getModifiable(objref);
+  }
+
+  
+  public ThreadInfo getCurrentThread () {
+    return ThreadInfo.currentThread;
+  }
+  
+  public void registerClassLoader(ClassLoaderInfo cl) {
+    this.getKernelState().addClassLoader(cl);
+  }
+
+  public int registerThread (ThreadInfo ti){
+    getKernelState().changed();
+    return getThreadList().add(ti);    
+  }
+
+  /**
+   * Returns the ClassLoader with the given globalId
+   */
+  protected ClassLoaderInfo getClassLoader(int gid) {
+    return ss.ks.getClassLoader(gid);
+  }
+
+  /**
+   * <2do> this is where we will hook in a better time model
+   */
+  public long currentTimeMillis () {
+    return timeModel.currentTimeMillis();
+  }
+
+  /**
+   * <2do> this is where we will hook in a better time model
+   */
+  public long nanoTime() {
+    return timeModel.nanoTime();
+  }
+
+  public void resetNextCG() {
+    if (ss.nextCg != null) {
+      ss.nextCg.reset();
+    }
+  }
+  
+  /**
+   * only for debugging, this is expensive
+   *
+   * If this is a store (forward) this is called before the state is stored.
+   *
+   * If this is a restore (visited forward or backtrack), this is called after
+   * the state got restored
+   */
+  public void checkConsistency(boolean isStateStore) {
+    getThreadList().checkConsistency( isStateStore);
+    getHeap().checkConsistency( isStateStore);
+  }
+  
+  public abstract void terminateProcess (ThreadInfo ti);
+  
+  // this is invoked by the heap (see GenericHeap.newInternString()) upon creating
+  // the very first intern string
+  public abstract Map<Integer,IntTable<String>> getInitialInternStringsMap();
+  
+  // ---------- Predicates used to query threads from ThreadList ---------- //
+  
+  public abstract Predicate<ThreadInfo> getRunnablePredicate();
+  
+  public abstract Predicate<ThreadInfo> getDaemonRunnablePredicate();
+  
+  public abstract Predicate<ThreadInfo> getAppTimedoutRunnablePredicate();
+  
+  public Predicate<ThreadInfo> getUserTimedoutRunnablePredicate () {
+    return userTimedoutRunnablePredicate;
+  }
+  
+  public Predicate<ThreadInfo> getUserLiveNonDaemonPredicate() {
+    return userliveNonDaemonPredicate;
+  }
+  
+  public Predicate<ThreadInfo> getTimedoutRunnablePredicate () {
+    return timedoutRunnablePredicate;
+  }
+  
+  public Predicate<ThreadInfo> getAlivePredicate () {
+    return alivePredicate;
+  }
+  
+  
+  // ---------- Methods for handling finalizers ---------- //
+    
+  public FinalizerThreadInfo getFinalizerThread() {
+    return getCurrentApplicationContext().getFinalizerThread();
+  }
+  
+  abstract void updateFinalizerQueues();
+  
+  public void processFinalizers() {
+    if(processFinalizers) {
+      updateFinalizerQueues();
+      ChoiceGenerator<?> cg = getNextChoiceGenerator();
+      if(cg==null || (cg.isSchedulingPoint() && !cg.isCascaded())) {
+        getFinalizerThread().scheduleFinalizer();
+      }
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/VMListener.java b/src/main/gov/nasa/jpf/vm/VMListener.java
new file mode 100644 (file)
index 0000000..300d7f6
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFListener;
+import gov.nasa.jpf.jvm.ClassFile;
+
+/**
+ * interface to register for callbacks by the VM
+ * Observer role in equally named pattern
+ * 
+ * Note that we only have notifications for generic events, NOT for conditions that
+ * are property specific, and especially nothing that is just triggered from an extension.
+ * If listeners are used to implement high level properties, the notifications should be
+ * used to implement properties, not to report some property violation that was detected
+ * by JPF 
+ */
+public interface VMListener extends JPFListener {
+  
+  /**
+   * VM got initialized (but search is not yet running). This can be used to
+   * do type initialization in listeners, since the ClassLoader mechanism is now functional
+   */
+  void vmInitialized (VM vm);
+    
+  /**
+   * VM is about to execute the next instruction
+   */
+  void executeInstruction (VM vm, ThreadInfo currentThread, Instruction instructionToExecute);
+  
+  /**
+   * VM has executed the next instruction
+   * (can be used to analyze branches, monitor PUTFIELD / GETFIELD and
+   * INVOKExx / RETURN instructions)
+   */
+  void instructionExecuted (VM vm, ThreadInfo currentThread, Instruction nextInstruction, Instruction executedInstruction);
+  
+  /**
+   * new Thread entered run() method
+   */
+  void threadStarted (VM vm, ThreadInfo startedThread);
+    
+  /**
+   * thread waits to acquire a lock
+  // NOTE: vm.getLastThreadInfo() does NOT have to be the running thread, as this
+  // notification can occur as a result of a lock operation in the current thread
+   */
+  void threadBlocked (VM vm, ThreadInfo blockedThread, ElementInfo lock);
+  
+  /**
+   * thread is waiting for signal
+   */
+  void threadWaiting (VM vm, ThreadInfo waitingThread);
+
+  /**
+   * thread got notified
+   */
+  void threadNotified (VM vm, ThreadInfo notifiedThread);
+    
+  /**
+   * thread got interrupted
+   */
+  void threadInterrupted (VM vm, ThreadInfo interruptedThread);
+  
+  /**
+   * Thread exited run() method
+   */
+  void threadTerminated (VM vm, ThreadInfo terminatedThread);
+
+  /**
+   * new thread was scheduled by VM
+   */
+  void threadScheduled (VM vm, ThreadInfo scheduledThread); // this might go into the choice generator notifications
+
+  /**
+   * a new classfile is about to be parsed. This notification allows replacement
+   * of the related classfile data via ClassFile.{get/set}Data() and can be
+   * used to do on-the-fly classfile instrumentation with 3rd party libraries 
+   */
+  public void loadClass (VM vm, ClassFile cf);
+  
+  /**
+   * new class was loaded. This is notified after the ClassInfo has been
+   * instantiated, but before the class object is initialized, i.e. clinit
+   * is called. The main use for this notification is to identify and 
+   * store ClassInfos, MethodInfos, FieldInfos or Instructions that are
+   * used by listeners etc. in order to enable efficient identify based filters
+   * in the performance critical instruction notifications
+   */
+  void classLoaded (VM vm, ClassInfo loadedClass);
+  
+  /**
+   * new object was created
+   */
+  void objectCreated (VM vm, ThreadInfo currentThread, ElementInfo newObject);
+  
+  /**
+   * object was garbage collected (after potential finalization)
+   */
+  void objectReleased (VM vm, ThreadInfo currentThread, ElementInfo releasedObject);
+  
+  /**
+   * notify if an object lock was taken (this includes automatic
+   * surrender during a wait())
+   */
+  void objectLocked (VM vm, ThreadInfo currentThread, ElementInfo lockedObject);
+  
+  /**
+   * notify if an object lock was released (this includes automatic
+   * reacquisition after a notify())
+   */
+  void objectUnlocked (VM vm, ThreadInfo currentThread, ElementInfo unlockedObject);
+  
+  /**
+   * notify if a wait() is executed
+   */
+  void objectWait (VM vm, ThreadInfo currentThread, ElementInfo waitingObject);
+  
+  /**
+   * notify if an object notifies a single waiter
+   */
+  void objectNotify (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject);
+
+  /**
+   * notify if an object notifies all waiters
+   */
+  void objectNotifyAll (VM vm, ThreadInfo currentThread, ElementInfo notifyingObject);
+  
+  
+  /**
+   * object becomes reachable through a shared reference 
+   * 'sharedObject' is the (already shared) owner of the field to which the (yet unshared) 'exposedObject' reference got assigned
+   */
+  void objectExposed (VM vm, ThreadInfo currentThread, ElementInfo fieldOwnerObject, ElementInfo exposedObject);
+  
+  /**
+   * object fields accessed by more than one live thread 
+   */
+  void objectShared (VM vm, ThreadInfo currentThread, ElementInfo sharedObject);
+
+  
+  void gcBegin (VM vm);
+  
+  void gcEnd (VM vm);
+  
+  /**
+   * exception was thrown
+   */
+  void exceptionThrown (VM vm, ThreadInfo currentThread, ElementInfo thrownException);
+
+  /**
+   * exception causes top frame to be purged
+   */
+  void exceptionBailout (VM vm, ThreadInfo currentThread);
+
+  /**
+   * exception handled by current top frame
+   */
+  void exceptionHandled (VM vm, ThreadInfo currentThread);
+
+  /**
+   * next ChoiceGenerator was registered, which means this is the end of the current transition
+   * 
+   * the reason why we have this in addition to the choiceGeneratorSet is that listeners
+   * can reset the registered CG and so force the current transition to continue (although the
+   * listener in this case has to make sure the operand stack is in a consistent state for
+   * continued execution because there might be a bottom half of an Instruction.execute() missing)
+   */
+  void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction);
+
+  /**
+   * a new ChoiceGenerator was set, which means we are at the beginning of a new transition.
+   *
+   * NOTE - this notification happens before the KernelState is stored, i.e. listeners are NOT
+   * allowed to alter the KernelState (e.g. by changing field values or thread states)
+   */
+  void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG);
+  
+  /**
+   * the next choice was requested from a previously registered ChoiceGenerator
+   *
+   * NOTE - this notification happens before the KernelState is stored, i.e. listeners are NOT
+   * allowed to alter the KernelState (e.g. by changing field values or thread states)
+   */
+  void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG);
+  
+  /**
+   * a ChoiceGnerator has returned all his choices
+   *
+   * NOTE - this notification happens before the KernelState is stored, i.e. listeners are NOT
+   * allowed to alter the KernelState (e.g. by changing field values or thread states)
+   */
+  void choiceGeneratorProcessed (VM vm, ChoiceGenerator<?> processedCG);
+
+  /**
+   * method body was entered. This is notified before the first instruction
+   * is executed
+   */
+  void methodEntered (VM vm, ThreadInfo currentThread, MethodInfo enteredMethod);
+
+  /**
+   * method body was left. This is notified after the last instruction had
+   * been executed
+   * NOTE - this is also notified when a StackFrame is dropped due to unhandled exceptions
+   */
+  void methodExited (VM vm, ThreadInfo currentThread, MethodInfo exitedMethod);
+
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/VariableAnnotationInfo.java b/src/main/gov/nasa/jpf/vm/VariableAnnotationInfo.java
new file mode 100644 (file)
index 0000000..70a8a71
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * type annotation for local vars and resource vars
+ */
+public class VariableAnnotationInfo extends AbstractTypeAnnotationInfo {
+  
+  protected long[] scopeEntries;
+  
+  public VariableAnnotationInfo (AnnotationInfo base, int targetType, short[] typePath, long[] scopeEntries) {
+    super( base, targetType, typePath);
+    
+    this.scopeEntries = scopeEntries;
+  }
+  
+  public int getNumberOfScopeEntries(){
+    return scopeEntries.length;
+  }
+  
+  public int getStartPC (int idx){
+    return (int)(scopeEntries[idx] >> 32) & 0xffff;
+  }
+  
+  public int getLength (int idx){
+    return (int)(scopeEntries[idx] >> 16) & 0xffff;
+  }
+  
+  public int getEndPC (int idx){
+    long e = scopeEntries[idx];
+    
+    int startPC = (int)(e >> 32) & 0xffff;
+    int len = (int)(e >> 16) & 0xffff;
+    
+    return startPC + len;
+  }
+  
+  public int getSlotIndex (int idx){
+    return (int)scopeEntries[idx] & 0xffff;    
+  }
+  
+  
+  @Override
+  protected void addArgs(StringBuilder sb){
+    sb.append(",scope:");
+    for (int i=0; i<scopeEntries.length;i++){
+      long e = scopeEntries[i];
+      int slotIndex = (int)(e & 0xffff);
+      int length = (int)((e >> 16) & 0xffff);
+      int startPc = (int)((e >> 32) & 0xffff);
+      
+      if (i>0){
+        sb.append(',');
+      }
+      
+      sb.append('[');
+      sb.append( Integer.toString(startPc));
+      sb.append("..");
+      sb.append( Integer.toString(startPc + length-1));
+      sb.append("]#");
+      sb.append(slotIndex);
+    }
+  }
+  
+  // 2do - perhaps we should map to LocalVarInfos here (in case we have them), but
+  // this would probably belong to LocalVarInfo (turning them into full InfoObjects)
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/Verify.java b/src/main/gov/nasa/jpf/vm/Verify.java
new file mode 100644 (file)
index 0000000..322585f
--- /dev/null
@@ -0,0 +1,624 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.BitSet;
+import java.util.Random;
+
+
+/**
+ * Verify is the programmatic interface of JPF that can be used from inside of
+ * applications. In order to enable programs to run outside of the JPF
+ * environment, we provide (mostly empty) bodies for the methods that are
+ * otherwise intercepted by the native peer class
+ */
+public class Verify {
+  static final int MAX_COUNTERS = 10;
+  static int[] counter;  // only here so that we don't pull in all JPF classes at RT
+
+  private static Random random;
+
+  /*
+   * only set if this was used from within a JPF context. This is mainly to
+   * enable encapsulation of JPF specific types so that they only get
+   * pulled in on demand, and we otherwise can still use the same Verify class
+   * for JPF-external execution. We use a class object to make sure it doesn't
+   * get recycled once JPF is terminated.
+   */
+  static Class<?> peer;
+
+  private static Random getRandom() {
+    if (random == null) {
+      random = new Random(42);
+    }
+    return random;
+  }
+
+  /*
+   * register the peer class, which is only done from within a JPF execution
+   * context. Be aware of that this migh actually load the real Verify class.
+   * The sequence usually is
+   *   JPF(Verify) -> VM(JPF_gov_nasa_jpf_vm_Verify) -> VM(Verify)
+   */
+  public static void setPeerClass (Class<?> cls) {
+    peer = cls;
+  }
+
+  // note this is NOT marked native because we might also call it from host VM code
+  // (beware that Verify is a different class there!). When executed by JPF,
+  // this is an MJI method
+  public static int getCounter (int id) {
+    if (peer != null) {
+      // this is executed if we are in a JPF context
+      return JPF_gov_nasa_jpf_vm_Verify.getCounter__I__I(null, 0, id);
+    } else {
+      if (counter == null) {
+        counter = new int[id >= MAX_COUNTERS ? (id+1) : MAX_COUNTERS];
+      }
+      if ((id < 0) || (id >= counter.length)) {
+        return 0;
+      }
+
+      return counter[id];
+    }
+  }
+
+  public static void resetCounter (int id) {
+    if (peer != null){
+      JPF_gov_nasa_jpf_vm_Verify.resetCounter__I__V(null, 0, id);
+    } else {
+      if ((counter != null) && (id >= 0) && (id < counter.length)) {
+        counter[id] = 0;
+      }
+    }
+  }
+
+  public static void setCounter (int id, int val) {
+    if (peer != null){
+      JPF_gov_nasa_jpf_vm_Verify.setCounter__II__V(null, 0, id, val);
+    } else {
+      if ((counter != null) && (id >= 0) && (id < counter.length)) {
+        counter[id] = val;
+      }
+    }
+  }
+
+  
+  public static int incrementCounter (int id) {
+    if (peer != null){
+      return JPF_gov_nasa_jpf_vm_Verify.incrementCounter__I__I(null, 0, id);
+    } else {
+      if (counter == null) {
+        counter = new int[(id >= MAX_COUNTERS) ? id+1 : MAX_COUNTERS];
+      } else if (id >= counter.length) {
+        int[] newCounter = new int[id+1];
+        System.arraycopy(counter, 0, newCounter, 0, counter.length);
+        counter = newCounter;
+      }
+
+      if ((id >= 0) && (id < counter.length)) {
+        return ++counter[id];
+      }
+
+      return 0;
+    }
+  }
+
+  public static final int NO_VALUE = -1;
+  
+  public static void putValue (String key, int value) {
+    throw new UnsupportedOperationException("putValue requires JPF execution");
+  }
+  
+  public static int getValue (String key) {
+    throw new UnsupportedOperationException("getValue requires JPF execution");    
+  }
+  
+  // same mechanism and purpose as the counters, but with BitSets, which is
+  // more convenient if we have a lot of different events to check
+
+  static BitSet[] bitSets;
+
+  private static void checkBitSetId(int id) {
+    if (bitSets == null) {
+      bitSets = new BitSet[id + 1];
+    } else if (id >= bitSets.length) {
+      BitSet[] newBitSets = new BitSet[id + 1];
+      System.arraycopy(bitSets, 0, newBitSets, 0, bitSets.length);
+      bitSets = newBitSets;
+    }
+
+    if (bitSets[id] == null) {
+      bitSets[id] = new BitSet();
+    }
+  }
+
+
+  public static void setBitInBitSet(int id, int bit, boolean value) {
+    if (peer != null){
+      // this is executed if we did run JPF
+      JPF_gov_nasa_jpf_vm_Verify.setBitInBitSet__IIZ__V(null, 0, id, bit, value);
+    } else {
+      // this is executed if we run this without previously executing JPF
+      checkBitSetId(id);
+      bitSets[id].set(bit, value);
+    }
+  }
+
+  public static boolean getBitInBitSet(int id, int bit) {
+    if (peer != null){
+      // this is executed if we did run JPF
+      return JPF_gov_nasa_jpf_vm_Verify.getBitInBitSet__II__Z(null, 0, id, bit);
+
+    } else {
+      // this is executed if we run this without previously executing JPF
+      checkBitSetId(id);
+      return bitSets[id].get(bit);
+    }
+  }
+
+  /**
+   * Adds a comment to the error trace, which will be printed and saved.
+   */
+  public static void addComment (String s) {}
+
+  /**
+   * Backwards compatibility START
+   * @deprecated use "assert cond : msg"
+   */
+  @Deprecated
+  public static void assertTrue (String s, boolean cond) {
+    if (!cond) {
+      System.out.println(s);
+      assertTrue(cond);
+    }
+  }
+
+  /**
+   * Checks that the condition is true.
+   * @deprecated use 'assert' directly
+   */
+  @Deprecated
+  public static void assertTrue (boolean cond) {
+    if (!cond) {
+      throw new AssertionError("Verify.assertTrue failed");
+    }
+  }
+
+  public static void atLabel (String label) {}
+
+  public static void atLabel (int label) {}
+
+  /**
+   * Marks the beginning of an atomic block.
+   * THIS IS EVIL, DON'T USE IT FOR OPTIMIZATION - THAT'S WHAT POR IS FOR!
+   * (it's mostly here to support model classes that need to execute atomic)
+   */
+  public static void beginAtomic () {}
+
+  /**
+   * Marks the end of an atomic block.
+   * EVIL - see beginAtomic()
+   */
+  public static void endAtomic () {}
+
+  public static void boring (boolean cond) {}
+
+  public static void busyWait (long duration) {
+    // this gets only executed outside of JPF
+    while (duration > 0) {
+      duration--;
+    }
+  }
+
+  public static boolean isCalledFromClass (String refClsName) {
+    Throwable t = new Throwable();
+    StackTraceElement[] st = t.getStackTrace();
+
+    if (st.length < 3) {
+      // main() or run()
+      return false;
+    }
+
+    try {
+      Class<?> refClazz = Class.forName(refClsName);
+      Class<?> callClazz = Class.forName(st[2].getClassName());
+
+      return (refClazz.isAssignableFrom(callClazz));
+
+    } catch (ClassNotFoundException cfnx) {
+      return false;
+    }
+  }
+
+  public static void ignoreIf (boolean cond) {}
+
+  public static void instrumentPoint (String label) {}
+
+  public static void instrumentPointDeep (String label) {}
+
+  public static void instrumentPointDeepRecur (String label, int depth) {}
+
+  public static void interesting (boolean cond) {}
+
+  public static void breakTransition (String reason) {}
+
+ /** for testing and debugging purposes */
+  public static int breakTransition (String reason, int min, int max) {
+    return -1;
+  }
+
+  /**
+   * simple debugging aids to imperatively print the current path output of the SUT
+   * (to be used with vm.path_output)
+   */
+  public static void printPathOutput(String msg) {}
+  public static void printPathOutput(boolean cond, String msg) {}
+
+  public static void threadPrint (String s) {
+    System.out.print( Thread.currentThread().getName());
+    System.out.print(": ");
+    System.out.print(s);
+  }
+
+  public static void threadPrintln (String s) {
+    threadPrint(s);
+    System.out.println();
+  }
+  
+  public static void print (String s) {
+    System.out.print(s);
+  }
+
+  public static void println (String s) {
+    System.out.println(s);
+  }
+  
+  public static void print (String s, int i) {
+    System.out.print(s + " : " + i);
+  }
+
+  public static void print (String s, boolean b) {
+    System.out.print(s + " : " + b);
+  }
+
+  public static void println() {
+    System.out.println();
+  }
+
+  /**
+   * this is to avoid StringBuilders
+   */
+  public static void print (String... args){
+    for (String s : args){
+      System.out.print(s);
+    }
+  }
+
+  /**
+   * note - these are mostly for debugging purposes (to see if attributes get
+   * propagated correctly, w/o having to write a listener), since attributes are
+   * supposed to be created at the native side, and hence can't be accessed from
+   * the application
+   */
+  
+  //--- use these if you know there are single attributes
+  public static void setFieldAttribute (Object o, String fieldName, int val) {}
+  public static int getFieldAttribute (Object o, String fieldName) { return 0; }
+  
+  //--- use these for multiple attributes
+  public static void addFieldAttribute (Object o, String fieldName, int val) {}
+  public static int[] getFieldAttributes (Object o, String fieldName) { return new int[0]; }
+
+  public static void setLocalAttribute (String varName, int val) {}
+  public static int getLocalAttribute (String varName) { return 0; }
+
+  public static void addLocalAttribute (String varName, int val) {}
+  public static int[] getLocalAttributes (String varName) { return new int[0]; }
+
+  public static void setElementAttribute (Object arr, int idx, int val){}
+  public static int getElementAttribute (Object arr, int idx) { return 0; }
+  
+  public static void addElementAttribute (Object arr, int idx, int val){}
+  public static int[] getElementAttributes (Object arr, int idx) { return new int[0]; }
+
+  public static void setObjectAttribute (Object o, int val) {}
+  public static int getObjectAttribute (Object o) { return 0; }
+  
+  public static void addObjectAttribute (Object o, int val) {}
+  public static int[] getObjectAttributes (Object o) { return new int[0]; }
+
+  
+  /**
+   * this is the new boolean choice generator. Since there's no real
+   * heuristic involved with boolean values, we skip the id (it's a
+   * hardwired "boolean")
+   */
+  public static boolean getBoolean () {
+    // just executed when not running inside JPF, native otherwise
+    return ((System.currentTimeMillis() & 1) != 0);
+  }
+
+  /**
+   * new boolean choice generator that also tells jpf which value to
+   * use first by default in a search.
+   */
+  public static boolean getBoolean (boolean falseFirst) {
+    // this is only executed when not running JPF
+    return getBoolean();
+  }
+
+
+  /**
+   * Returns int nondeterministically between (and including) min and max.
+   */
+  public static int getInt (int min, int max) {
+    // this is only executed when not running JPF, native otherwise
+    return getRandom().nextInt((max-min+1)) + min;
+  }
+
+  public static int getIntFromList (int... values){
+    if (values != null && values.length > 0) {
+      int i = getRandom().nextInt(values.length);
+      return values[i];
+    } else {
+      return getRandom().nextInt();
+    }
+  }
+
+  public static Object getObject (String key) {
+    return "?";
+  }
+
+  /**
+   * this is the API for int value choice generators. 'id' is used to identify
+   * both the corresponding ChoiceGenerator subclass, and the application specific
+   * ctor parameters from the normal JPF configuration mechanism
+   */
+  public static int getInt (String key){
+    // this is only executed when not running JPF, native otherwise
+    return getRandom().nextInt();
+  }
+
+  /**
+   * this is the API for double value choice generators. 'id' is used to identify
+   * both the corresponding ChoiceGenerator subclass, and the application specific
+   * ctor parameters from the normal JPF configuration mechanism
+   */
+  public static double getDouble (String key){
+    // this is only executed when not running JPF, native otherwise
+    return getRandom().nextDouble();
+  }
+
+  public static double getDoubleFromList (double... values){
+    if (values != null && values.length > 0) {
+      int i = getRandom().nextInt(values.length);
+      return values[i];
+    } else {
+      return getRandom().nextDouble();
+    }
+  }
+  
+  public static long getLongFromList (long...values){
+    if (values != null && values.length > 0) {
+      int i = getRandom().nextInt(values.length);
+      return values[i];
+    } else {
+      return getRandom().nextLong();
+    }    
+  }
+
+  public static float getFloatFromList (float...values){
+    if (values != null && values.length > 0) {
+      int i = getRandom().nextInt(values.length);
+      return values[i];
+    } else {
+      return getRandom().nextFloat();
+    }    
+  }
+
+  
+  /**
+   * Returns a random number between 0 and max inclusive.
+   */
+  public static int random (int max) {
+    // this is only executed when not running JPF
+    return getRandom().nextInt(max + 1);
+  }
+
+  /**
+   * Returns a random boolean value, true or false. Note this gets
+   * handled by the native peer, and is just here to enable running
+   * instrumented applications w/o JPF
+   */
+  public static boolean randomBool () {
+    // this is only executed when not running JPF
+    return getRandom().nextBoolean();
+  }
+
+  public static long currentTimeMillis () {
+    return System.currentTimeMillis();
+  }
+
+  // Backwards compatibility START
+  public static Object randomObject (String type) {
+    return null;
+  }
+
+  public static boolean isRunningInJPF() {
+    return false;
+  }
+
+  /**
+   * USE CAREFULLY - returns true if the virtual machine this code is
+   * running under is doing state matching.  This can be used as a hint
+   * as to whether data structures (that are known to be correct!)
+   * should be configured to use a canonical representation.  For example,
+   * <pre><code>
+   * Vector v = new Vector();
+   * v.add(obj1);
+   * if (Verify.getBoolean()) {
+   *     v.addAll(eleventyBillionObjectCollection);
+   *     v.setSize(1);
+   * }
+   * // compare states here
+   * </code></pre>
+   * To the programmer, the states are (almost certainly) the same.  To
+   * the VM, they could be different (capacity inside the Vector).  For
+   * the sake of speed, Vector does not store things canonically, but this
+   * can cause (probably mild) state explosion if matching states.  If
+   * you know whether states are being matched, you can choose the right
+   * structure--as long as those structures aren't what you're looking for
+   * bugs in!
+   */
+  public static boolean vmIsMatchingStates() {
+    return false;
+  }
+
+  public static void storeTrace (String fileName, String comment) {
+    // intercepted in NativePeer
+  }
+
+  public static void storeTraceIf (boolean cond, String fileName, String comment) {
+    if (cond) {
+      storeTrace(fileName, comment);
+    }
+  }
+
+  public static void storeTraceAndTerminate (String fileName, String comment) {
+    storeTrace(fileName, comment);
+    terminateSearch();
+  }
+
+  public static void storeTraceAndTerminateIf (boolean cond, String fileName, String comment) {
+    if (cond) {
+      storeTrace(fileName, comment);
+      terminateSearch();
+    }
+  }
+
+  public static boolean isTraceReplay () {
+    return false; // native
+  }
+
+  public static boolean isShared (Object o){
+    return false; // native
+  }
+  
+  public static void setShared (Object o, boolean isShared) {
+    // native
+  }
+  
+  public static void freezeSharedness (Object o, boolean freeze) {
+    // native
+  }
+  
+  public static void terminateSearch () {
+    // native
+  }
+
+  public static void setHeuristicSearchValue (int n){
+    // native - to control UserHeuristic
+  }
+
+  public static void resetHeuristicSearchValue (){
+    // native - to control UserHeuristic
+  }
+
+  public static int getHeuristicSearchValue (){
+    // native - to control UserHeuristic
+    return 0;
+  }
+
+  public static void setProperties (String... p) {
+    // native
+  }
+
+  public static String getProperty (String key) {
+    // native
+    return null;
+  }
+
+  public static <T> T createFromJSON(Class<T> clazz, String json){
+    return null;
+  }
+
+  public static void writeObjectToFile(Object object, String fileName) {
+    try {
+      FileOutputStream fso = new FileOutputStream(fileName);
+      ObjectOutputStream oos = new ObjectOutputStream(fso);
+      oos.writeObject(object);
+      oos.flush();
+      oos.close();
+
+    } catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+
+  }
+
+  public static <T> T readObjectFromFile(Class<T> clazz, String fileName) {
+    try
+    {
+      FileInputStream fis = new FileInputStream(fileName);
+      ObjectInputStream ois = new ObjectInputStream(fis);
+
+      Object read = ois.readObject();
+      
+      return (T) read;
+      
+    }
+    catch (Exception ex) {
+      throw new RuntimeException(ex);
+    }
+
+  }
+  
+  
+  //--- model logging support
+  
+  /*
+   * we add these here so that we don't need to pull in any java.util.logging classes
+   * Note - these need to be kept in sync with our native peer
+   */
+  public static final int SEVERE = 1;
+  public static final int WARNING = 2;
+  public static final int INFO = 3;
+  public static final int FINE = 4;
+  public static final int FINER = 5;
+  public static final int FINEST = 6;
+  
+  public static void log( String loggerId, int logLevel, String msg){
+    System.err.println(msg);
+  }
+
+  // to avoid construction of strings on the model side
+  public static void log( String loggerId, int logLevel, String msg, String arg){
+    System.err.println(msg);
+  }
+
+  public static void log( String loggerId, int logLevel, String format, Object... args){
+    System.err.printf(format, args);
+  }
+
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/ArrayElementInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/ArrayElementInstruction.java
new file mode 100644 (file)
index 0000000..5936b6b
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Instruction;
+
+/**
+ * abstract base for all array element access instructions
+ */
+public abstract class ArrayElementInstruction extends Instruction implements ReadOrWriteInstruction {
+  
+  public abstract ElementInfo peekArrayElementInfo (ThreadInfo ti);
+  
+  public abstract int peekIndex(ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/FieldInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/FieldInstruction.java
new file mode 100644 (file)
index 0000000..23138f5
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.Types;
+
+/**
+ * abstract base for all field access instructions
+ */
+public abstract class FieldInstruction extends Instruction implements ReadOrWriteInstruction {
+
+  protected String fname;
+  protected String ftype;
+  protected String className;
+  protected String varId;
+
+  protected FieldInfo fi; // lazy eval, hence not public
+
+  protected int    size;  // is it a word or a double word field
+  protected boolean isReferenceField;
+
+  protected long lastValue;
+
+  protected FieldInstruction (String name, String clsName, String fieldDescriptor){
+    fname = name;
+    ftype = fieldDescriptor;
+    className = Types.getClassNameFromTypeName(clsName);
+    isReferenceField = Types.isReferenceSignature(fieldDescriptor);
+    size = Types.getTypeSize(fieldDescriptor);
+  }
+
+  /**
+   * for explicit construction
+   */
+  public void setField (String fname, String fclsName) {
+    this.fname = fname;
+    this.className = fclsName;
+    if (fclsName.equals("long") || fclsName.equals("double")) {
+      this.size = 2;
+      this.isReferenceField = false;
+    } else {
+      this.size = 1;
+      if (fclsName.equals("boolean") || fclsName.equals("byte") || fclsName.equals("char") || fclsName.equals("short") || fclsName.equals("int")) {
+        this.isReferenceField = false;
+      } else {
+        this.isReferenceField = true;
+      }
+    }
+  }
+  
+  public abstract FieldInfo getFieldInfo();
+  @Override
+  public abstract boolean isRead();
+  
+  // for use in instructionExecuted() implementations
+  public abstract ElementInfo getLastElementInfo();
+  
+  // for use in executeInstruction implementations
+  public abstract ElementInfo peekElementInfo (ThreadInfo ti);
+  
+  public String getClassName(){
+     return className;
+  }
+
+  public String getFieldName(){
+         return fname;
+  }
+
+  public int getFieldSize() {
+    return size;
+  }
+  public boolean isReferenceField () {
+    return isReferenceField;
+  }
+  
+  /**
+   * only defined in instructionExecuted() notification context
+   */
+  public long getLastValue() {
+    return lastValue;
+  }
+
+  public String getVariableId () {
+    if (varId == null) {
+      varId = className + '.' + fname;
+    }
+    return varId;
+  }
+
+  public String getId (ElementInfo ei) {
+    // <2do> - OUTCH, should be optimized (so far, it's only called during reporting)
+    if (ei != null){
+      return (ei.toString() + '.' + fname);
+    } else {
+      return ("?." + fname);
+    }
+  }
+  
+  @Override
+  public String toString() {
+    return getMnemonic() + " " + className + '.' + fname;
+  }
+  
+  
+  @Override
+  public boolean isMonitorEnterPrologue(){
+    // override if this insn can be part of a monitorenter code pattern
+    return false;
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/InstanceFieldInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/InstanceFieldInstruction.java
new file mode 100644 (file)
index 0000000..8aed7ff
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+/**
+ * common machine independent type for all instance field access instructions
+ */
+public abstract class InstanceFieldInstruction extends FieldInstruction {
+
+  protected int lastThis = MJIEnv.NULL;
+
+  protected InstanceFieldInstruction (String fieldName, String classType, String fieldDescriptor){
+    super(fieldName, classType, fieldDescriptor);
+  }
+
+  public abstract int getObjectSlot (StackFrame frame);
+  
+  @Override
+  public ElementInfo getElementInfo (ThreadInfo ti){
+    if (isCompleted(ti)){
+      return ti.getElementInfo(lastThis);
+    } else {
+      return peekElementInfo(ti);
+    }
+  }
+  
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(' ');
+    sb.append( getLastElementInfo());
+    sb.append('.');
+    sb.append(fname);
+    
+    return sb.toString();
+  }
+
+  @Override
+  public FieldInfo getFieldInfo () {
+    if (fi == null) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(className);
+      if (ci != null) {
+        fi = ci.getInstanceField(fname);
+      }
+    }
+    return fi;
+  }
+
+  /**
+   * NOTE - the return value is *only* valid in a instructionExecuted() context, since
+   * the same instruction can be executed from different threads
+   */
+  public int getLastThis() {
+    return lastThis;
+  }
+
+  /**
+   * since this is based on getLastThis(), the same context restrictions apply
+   */
+  @Override
+  public ElementInfo getLastElementInfo () {
+    if (lastThis != MJIEnv.NULL) {
+      return VM.getVM().getHeap().get(lastThis); // <2do> remove - should be in clients
+    }
+
+    return null;
+  }
+
+  public String getFieldDescriptor () {
+    ElementInfo ei = getLastElementInfo();
+    FieldInfo fi = getFieldInfo();
+
+    return ei.toString() + '.' + fi.getName();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/InstanceInvokeInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/InstanceInvokeInstruction.java
new file mode 100644 (file)
index 0000000..34f9292
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * common type for all method invocations on objects
+ */
+public interface InstanceInvokeInstruction extends InstructionInterface {
+
+  MethodInfo getInvokedMethod();
+  int getObjectSlot (StackFrame frame);
+  int getCalleeThis(ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/InstructionInterface.java b/src/main/gov/nasa/jpf/vm/bytecode/InstructionInterface.java
new file mode 100644 (file)
index 0000000..030c308
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.util.Attributable;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * we use that to access Instruction methods from xInstruction interfaces
+ * 
+ * NOTE - this has to be kept in sync with Instruction
+ */
+public interface InstructionInterface extends Attributable {
+
+  /**
+   * this is for cases where we need the Instruction type. Try to use InstructionInterface in clients
+   */
+  Instruction asInstruction();
+  
+  int getByteCode();
+  boolean isFirstInstruction();
+  boolean isBackJump();
+  boolean isExtendedInstruction();
+  Instruction getNext();
+  int getInstructionIndex();
+  int getPosition();
+  MethodInfo getMethodInfo();
+  int getLength();
+  Instruction getPrev();
+  boolean isCompleted(ThreadInfo ti);
+  String getSourceLine();
+  String getSourceLocation();
+  Instruction execute(ThreadInfo ti);
+  String toPostExecString();
+  String getMnemonic();
+  int getLineNumber();
+  String getFileLocation();
+  String getFilePos();
+  Instruction getNext (ThreadInfo ti);
+
+  
+  
+  //.. and probably a lot still missing
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/InvokeInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/InvokeInstruction.java
new file mode 100644 (file)
index 0000000..fd5f0d0
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * abstract base for InvokeInstructions
+ */
+public abstract class InvokeInstruction extends Instruction {
+
+  public abstract MethodInfo getInvokedMethod();
+  
+  public abstract String getInvokedMethodName();
+  public abstract String getInvokedMethodSignature();
+  public abstract String getInvokedMethodClassName();
+  
+  /**
+   * this does the lock registration/acquisition and respective transition break 
+   * return true if the caller has to re-execute
+   */
+  protected boolean reschedulesLockAcquisition (ThreadInfo ti, ElementInfo ei){
+    Scheduler scheduler = ti.getScheduler();
+    ei = ei.getModifiableInstance();
+    
+    if (!ti.isLockOwner(ei)){ // we only need to register, block and/or reschedule if this is not a recursive lock
+      if (ei.canLock(ti)) {
+        // record that this thread would lock the object upon next execution if we break the transition
+        // (note this doesn't re-add if already registered)
+        ei.registerLockContender(ti);
+        if (scheduler.setsLockAcquisitionCG(ti, ei)) { // optional scheduling point
+          return true;
+        }
+        
+      } else { // we need to block
+        ei.block(ti); // this means we only re-execute once we can acquire the lock
+        if (scheduler.setsBlockedThreadCG(ti, ei)){ // mandatory scheduling point
+          return true;
+        }
+        throw new JPFException("blocking synchronized call without transition break");            
+      }
+    }
+    
+    // locking will be done by ti.enter()
+    return false;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/LocalVariableInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/LocalVariableInstruction.java
new file mode 100644 (file)
index 0000000..1c828dd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.LocalVarInfo;
+
+/**
+ * common type for various instructions that access local variables
+ */
+public interface LocalVariableInstruction  extends InstructionInterface {
+  
+  String getVariableId();
+  
+  int getLocalVariableSlot();
+  LocalVarInfo getLocalVarInfo();
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/LookupSwitchInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/LookupSwitchInstruction.java
new file mode 100644 (file)
index 0000000..6251b99
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+/**
+ * interface to insulate us from any specific Instruction hierarchy
+ */
+public interface LookupSwitchInstruction {
+
+  void setTarget(int index, int match, int targetPc);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/NewInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/NewInstruction.java
new file mode 100644 (file)
index 0000000..7de04eb
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+
+/**
+ * abstract base for all allocating instructions
+ */
+public abstract class NewInstruction extends Instruction {
+  // nothing in here
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/ReadInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/ReadInstruction.java
new file mode 100644 (file)
index 0000000..7950052
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+/**
+ * common type for all GETx insns
+ * This has to be an interface since implementors have to be derived from the abstract FieldInstruction
+ */
+public interface ReadInstruction extends ReadOrWriteInstruction {
+  // nothing here yet
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/ReadOrWriteInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/ReadOrWriteInstruction.java
new file mode 100644 (file)
index 0000000..7f09c5d
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * common type for FieldInstructions and ArrayElementInstructions
+ */
+public interface ReadOrWriteInstruction extends InstructionInterface {
+
+  boolean isRead();
+  
+  /**
+   * where do we read from/write to 
+   * 
+   * NOTE - this is a StaticElementInfo in case this is a PUT/GETSTATIC
+   */
+  ElementInfo getElementInfo(ThreadInfo ti);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/ReturnInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/ReturnInstruction.java
new file mode 100644 (file)
index 0000000..8a4db61
--- /dev/null
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.Instruction;
+
+/**
+ * abstract base for return instructions
+ *
+ * since .jvm so far does not use any specific feature of ReturnInstructions,
+ * we just need an empty type to make .jvm independent of a specific
+ * Instruction set
+ */
+public abstract class ReturnInstruction extends Instruction {
+  // nothing in here yet
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/ReturnValueInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/ReturnValueInstruction.java
new file mode 100644 (file)
index 0000000..c9ba5da
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.StackFrame;
+
+/**
+ * common type for ReturnInstructions that return a value
+ */
+public interface ReturnValueInstruction extends InstructionInterface {
+
+  /**
+   * where do we get from what we return
+   * NOTE: only makes sense in a executeInstruction() context
+   */
+  int getValueSlot(StackFrame frame);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/StaticFieldInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/StaticFieldInstruction.java
new file mode 100644 (file)
index 0000000..65b127c
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * common machine independent type for static field accessors
+ */
+public abstract class StaticFieldInstruction extends FieldInstruction {
+
+  protected StaticFieldInstruction(String fieldName, String clsDescriptor, String fieldDescriptor){
+    super(fieldName, clsDescriptor, fieldDescriptor);
+  }
+
+  /**
+   * on-demand initialize the ClassInfo and FieldInfo fields. Note that
+   * classinfo might not correspond with the static className, but can be one of
+   * the super classes. Rather than checking for this on each subsequent access,
+   * we get the right one that declares the field here
+   */
+  protected void initialize() {
+    ClassInfo ciRef = mi.getClassInfo().resolveReferencedClass(className);
+    
+    FieldInfo f = ciRef.getStaticField(fname);
+    if (f != null){
+      ClassInfo ciField = f.getClassInfo();
+      if (!ciField.isRegistered()){
+        // classLoaded listeners might change/remove this field
+        ciField.registerClass(ThreadInfo.getCurrentThread());
+        f = ciField.getStaticField(fname);
+      }
+
+      fi = f;
+    }
+    // otherwise the referenced class has changed since compilation, which
+    // should throw a NoSuchFieldError in the caller
+  }
+
+  /**
+   * who owns the field?
+   * NOTE: this should only be used from a executeInstruction()/instructionExecuted() context
+   */
+  @Override
+  public ElementInfo getElementInfo(ThreadInfo ti){
+    return getFieldInfo().getClassInfo().getStaticElementInfo();
+  }
+  
+  @Override
+  public String toPostExecString(){
+    StringBuilder sb = new StringBuilder();
+    sb.append(getMnemonic());
+    sb.append(' ');
+    sb.append( fi.getFullName());
+    
+    return sb.toString();
+  }
+
+  public ClassInfo getClassInfo() {
+    if (fi == null) {
+      initialize();
+    }
+    return fi.getClassInfo();
+  }
+
+  @Override
+  public FieldInfo getFieldInfo() {
+    if (fi == null) {
+      initialize();
+    }
+    return fi;
+  }
+
+  /**
+   *  that's invariant, as opposed to InstanceFieldInstruction, so it's
+   *  not really a peek
+   */
+  @Override
+  public ElementInfo peekElementInfo (ThreadInfo ti) {
+    return getLastElementInfo();
+  }
+
+  @Override
+  public StaticElementInfo getLastElementInfo() {
+    return getFieldInfo().getClassInfo().getStaticElementInfo();
+  }
+
+  // this can be different than ciField - the field might be in one of its
+  // superclasses
+  public ClassInfo getLastClassInfo(){
+    return getFieldInfo().getClassInfo();
+  }
+
+  public String getLastClassName() {
+    return getLastClassInfo().getName();
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/StoreInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/StoreInstruction.java
new file mode 100644 (file)
index 0000000..bf21877
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+/**
+ * abstraction for various store instructions
+ */
+public interface StoreInstruction
+{
+  // just a type tag for now
+}
+
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/TableSwitchInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/TableSwitchInstruction.java
new file mode 100644 (file)
index 0000000..ffb3b33
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+/**
+ * interface to insulate us from any specific Instruction hierarchy
+ */
+public interface TableSwitchInstruction {
+
+  void setTarget(int index, int targetPc);
+}
diff --git a/src/main/gov/nasa/jpf/vm/bytecode/WriteInstruction.java b/src/main/gov/nasa/jpf/vm/bytecode/WriteInstruction.java
new file mode 100644 (file)
index 0000000..73365ab
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.bytecode;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.StackFrame;
+
+
+/**
+ * common type for all xPUT insns
+ */
+public interface WriteInstruction extends ReadOrWriteInstruction {
+
+  /**
+   * where do we store the value 
+   */
+  FieldInfo getFieldInfo();
+  
+  /**
+   * from where in the StackFrame do we get the value for the put
+   */
+  int getValueSlot(StackFrame frame);
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/BreakGenerator.java b/src/main/gov/nasa/jpf/vm/choice/BreakGenerator.java
new file mode 100644 (file)
index 0000000..2f4ab65
--- /dev/null
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+
+/**
+ * a pseudo CG that is used to break transitions. It can be used to break and
+ * just reschedule the current thread, or to indicate an end state
+ * (e.g. for System.exit())
+ */
+public class BreakGenerator extends ChoiceGeneratorBase<ThreadInfo> implements ThreadChoiceGenerator {
+
+  protected ThreadInfo ti;
+  protected int state = -1;
+  protected boolean isTerminator;
+
+  public BreakGenerator (String id, ThreadInfo ti, boolean isTerminator) {
+    super(id);
+    
+    this.ti = ti;
+    this.isTerminator = isTerminator;
+  }
+  
+  @Override
+  public ThreadInfo getNextChoice () {
+    assert !isTerminator : "illegal operation on terminal BreakGenerator";
+    return (state == 0) ? ti : null;
+  }
+
+  @Override
+  public ThreadInfo getChoice (int idx){
+    if (idx == 0){
+      return ti;
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+  
+  @Override
+  public void printOn (PrintWriter pw) {
+    pw.println("BreakGenerator {" + ti.getName() + "}");
+  }
+
+  @Override
+  public void advance () {
+    assert !isTerminator : "illegal operation on terminal BreakGenerator";
+    state++;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return (state >= 0) ? 1 : 0;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return 1;
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    if (isTerminator){
+      return false;
+    }
+    
+    return (state < 0);
+  }
+
+  @Override
+  public void reset () {
+    state = -1;
+    isDone = false;
+  }
+
+  @Override
+  public boolean contains (ThreadInfo ti){
+    return this.ti == ti;
+  }
+
+  @Override
+  public Class<ThreadInfo> getChoiceType() {
+    return ThreadInfo.class;
+  }
+
+  @Override
+  public ChoiceGenerator<ThreadInfo> randomize() {
+    return this;
+  }
+  
+  @Override
+  public boolean isSchedulingPoint(){
+    return true; // that's the whole point of having a BreakGenerator
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/CompoundChoiceGenerator.java b/src/main/gov/nasa/jpf/vm/choice/CompoundChoiceGenerator.java
new file mode 100644 (file)
index 0000000..4e6c7a2
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+
+/**
+ * an abstract choice generator that is just a list of choice generators
+ */
+public abstract class CompoundChoiceGenerator<T> extends ChoiceGeneratorBase<T> {
+
+  //--- helper to implement ad hoc linked lists
+  protected class Entry {
+    ChoiceGenerator<T> cg;
+    Entry next;
+    
+    Entry (ChoiceGenerator<T> cg, Entry next){
+      this.cg = cg;
+      this.next = next;
+    }
+  }
+  
+  protected Entry base;
+  protected Entry cur;
+
+  protected CompoundChoiceGenerator (String id){
+    super(id);
+  }
+  
+  //--- to be called from derived ctors
+  
+  protected void setBase (ChoiceGenerator<T> cg){
+    base = cur = new Entry( cg, null);
+  }
+  
+  protected void add (ChoiceGenerator<T> cg){
+    base = cur = new Entry( cg, cur);
+  }
+  
+  //--- the public ChoiceGenerator interface
+  
+  @Override
+  public T getNextChoice () {
+    if (cur != null){
+      return cur.cg.getNextChoice();
+    } else {
+      return null;
+    }
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    if (cur != null){
+      if (cur.cg.hasMoreChoices()){
+        return true;
+      } else {
+        for (Entry e = cur.next; e != null; e = e.next){
+          if (e.cg.hasMoreChoices()){
+            return true;
+          }
+        }
+        
+        return false;
+      }
+      
+    } else {
+      return false;
+    }
+  }
+
+  @Override
+  public void advance () {
+    if (cur != null){
+      if (cur.cg.hasMoreChoices()){
+        cur.cg.advance();
+      } else {
+        cur = cur.next;
+        advance();
+      }
+    }
+  }
+
+  @Override
+  public void reset () {
+    cur = base;
+    
+    for (Entry e = base; e != null; e = e.next){
+      e.cg.reset();
+    }
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    int n = 0;
+    
+    for (Entry e = base; e != null; e = e.next){
+      n += e.cg.getTotalNumberOfChoices();
+    }
+    
+    return n;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    int n=0;
+    
+    for (Entry e = base; e != null; e = e.next){
+      n += e.cg.getProcessedNumberOfChoices();
+      if (e == cur){
+        break;
+      }
+    }
+    
+    return n;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromList.java b/src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromList.java
new file mode 100644 (file)
index 0000000..5b3cf95
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.DoubleChoiceGenerator;
+
+/**
+ * simple DoubleChoiceGenerator that takes it's values from a single
+ * property "values" (comma or blank separated list)
+ * 
+ */
+public class DoubleChoiceFromList extends NumberChoiceFromList<Double> implements DoubleChoiceGenerator {
+
+  @Override
+  protected Double[] createValueArray(int len) {
+    return new Double[len];
+  }
+
+  @Override
+  protected Double getDefaultValue() {
+    return 0.0;
+  }
+
+  @Override
+  public Class<Double> getChoiceType() {
+    return Double.class;
+  }
+
+  @Override
+  protected Double parseLiteral(String literal, int sign) {
+    double val = Double.parseDouble(literal);
+    return new Double(val * sign);
+  }
+
+  @Override
+  protected Double newValue(Number num, int sign) {
+    return new Double(num.intValue() * sign);
+  }
+
+  /**
+   * super constructor for subclasses that want to configure themselves
+   * 
+   * @param id
+   *          name used in choice config
+   */
+  protected DoubleChoiceFromList(String id) {
+    super(id);
+  }
+
+  protected DoubleChoiceFromList(String id, Double[] vals) {
+    super(id, vals);
+  }
+
+  public DoubleChoiceFromList(Config conf, String id) {
+    super(conf, id);
+  }
+
+  public DoubleChoiceFromList(String id, double... val) {
+    super(id);
+
+    if (val != null) {
+      values = new Double[val.length];
+      for (int i = 0; i < val.length; i++) {
+        values[i] = val[i]; // enable use of cached Double values
+      }
+    } else {
+      throw new JPFException("empty set for DoubleChoiceFromList");
+    }
+
+    count = -1;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromSet.java b/src/main/gov/nasa/jpf/vm/choice/DoubleChoiceFromSet.java
new file mode 100644 (file)
index 0000000..22e4a49
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+
+public class DoubleChoiceFromSet extends DoubleChoiceFromList {
+
+  public DoubleChoiceFromSet (Config conf, String id) {
+    super(conf, id);
+  }
+
+  
+  public DoubleChoiceFromSet(String id, double... val){
+    super(id, val);
+    removeDuplicates();
+    count = -1;
+  }
+
+  /** super constructor for subclasses that want to configure themselves
+   * 
+   * @param id name used in choice config
+   */
+  protected DoubleChoiceFromSet(String id){
+    super(id);
+  }
+  
+  /*
+   *  Remove duplicate values. This is pretty redundant to IntChoiceFromSet, but
+   *  unfortunately we rely on boxing/unboxing and array creation, for which the compiler
+   *  needs the concrete type
+   */
+  private void removeDuplicates() {
+    int len = values.length;
+    for (int i = 0; i < len; i++) {
+      int j = i + 1;
+      while (j < len) {
+        if (values[i] == values[j]) {
+          values[j] = values[len - 1];
+          len--;
+          // don't increment j as new element has been placed there and needs to be re-tested
+        } else {
+          j++;
+        }
+      }
+    }
+    if (len < values.length) {
+      Double[] uniqVals = new Double[len];
+      System.arraycopy(values, 0, uniqVals, 0, len);
+      values = uniqVals;
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/DoubleSpec.java b/src/main/gov/nasa/jpf/vm/choice/DoubleSpec.java
new file mode 100644 (file)
index 0000000..f437523
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+public class DoubleSpec {
+
+  /**
+   * return double from String spec, which can be either a literal
+   * or a local variable name, or a field name
+   */
+  public static double eval (String spec) {
+    double ret;
+    
+    char c = spec.charAt(0);
+    if (Character.isDigit(c) || (c == '+') || (c == '-') || (c == '.')) {
+      try {
+        ret = Double.parseDouble(spec); 
+      } 
+      catch (NumberFormatException nfx) {
+        throw new JPFException("illegal double spec: " + spec);
+      }
+    } else {
+      ret = resolveVar(spec);      
+    }
+    return ret;
+  }
+
+  public static double resolveVar(String spec){
+    VM vm = VM.getVM();
+    String[] varId = spec.split("[.]+");
+
+    double ret;
+    switch (varId.length){
+    case 1: { // variable name
+      ThreadInfo ti = ThreadInfo.getCurrentThread();
+      try {
+        StackFrame frame = ti.getTopFrame();
+
+        ret = frame.getDoubleLocalVariable(varId[0]);
+        // that throws an exception (a few calls down) if  
+        // the name is not found...
+      }
+      catch (JPFException e){ //not local? try a field!
+        int id = ti.getThis();
+        if(id>=0){  // in a normal (non-static) method
+          ElementInfo ei = vm.getElementInfo(id);
+          ret = ei.getDoubleField(varId[0]);
+        }
+        else { // static method (no this)- must be static var
+          ClassInfo ci = ti.getTopFrameMethodInfo().getClassInfo();
+          ElementInfo ei = ci.getStaticElementInfo();
+          ret = ei.getDoubleField(varId[0]);
+        }
+      }
+      break;
+    }
+    case 2: { // static variable name TODO other cases here...
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo( varId[0]);
+      ElementInfo ei = ci.getStaticElementInfo();
+      ret = ei.getDoubleField(varId[1]);
+      break;
+    }
+    default: 
+      throw new JPFException("Choice value format error parsing \"" + spec +"\"");
+    }
+    return ret;
+  }
+
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/DoubleThresholdGenerator.java b/src/main/gov/nasa/jpf/vm/choice/DoubleThresholdGenerator.java
new file mode 100644 (file)
index 0000000..a13f1e0
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.DoubleChoiceGenerator;
+
+/**
+ * ChoiceGenerator instance that produces a simple 3 value enumeration
+ * 
+ */
+public class DoubleThresholdGenerator extends ChoiceGeneratorBase<Double> implements DoubleChoiceGenerator {
+
+  protected double[] values = new double[3];
+  protected int count;
+
+  public DoubleThresholdGenerator(Config conf, String id) {
+    super(id);
+
+    values[0] = conf.getDouble(id + ".low");
+    values[1] = conf.getDouble(id + ".threshold");
+    values[2] = conf.getDouble(id + ".high");
+    count = -1;
+  }
+
+  @Override
+  public Double getChoice (int idx){
+    if (idx >= 0 && idx < 3){
+      return values[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+  
+  @Override
+  public void reset () {
+    count = -1;
+
+    isDone = false;
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    return !isDone && (count < 2);
+  }
+
+  @Override
+  public Double getNextChoice () {
+    if (count >=0) {
+      return new Double(values[count]);
+    } else {
+      return new Double(values[0]);
+    }
+  }
+
+  @Override
+  public void advance () {
+    if (count < 2)
+      count++;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return 3;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return count + 1;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append("[id=\"");
+    sb.append(id);
+    sb.append("\",");
+    
+    for (int i=0; i<3; i++) {
+      if (count == i) {
+        sb.append(MARKER);
+      }
+      sb.append(values[i]);
+      if (count < 2) {
+        sb.append(',');
+      }
+    }
+    sb.append(']');
+    return sb.toString();
+  }
+  
+  @Override
+  public DoubleThresholdGenerator randomize () {
+    for (int i = values.length - 1; i > 0; i--) {
+      int j = random.nextInt(i + 1);
+      double tmp = values[i];
+      values[i] = values[j];
+      values[j] = tmp;
+    }    
+    return this;
+  }
+
+  @Override
+  public Class<Double> getChoiceType() {
+    return Double.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/ExceptionThreadChoiceFromSet.java b/src/main/gov/nasa/jpf/vm/choice/ExceptionThreadChoiceFromSet.java
new file mode 100644 (file)
index 0000000..941fba5
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * a ThreadChoiceFromSet that reschedules the specified thread with exceptions
+ */
+public class ExceptionThreadChoiceFromSet extends ThreadChoiceFromSet {
+
+  protected ThreadInfo exceptionThread;
+  protected String[] exceptions;
+  
+  public ExceptionThreadChoiceFromSet (String id, ThreadInfo[] runnables, ThreadInfo exceptionThread, String[] exceptionClsNames){
+    super(id);
+    
+    this.exceptionThread = exceptionThread;
+    
+    values = new ThreadInfo[runnables.length + exceptionClsNames.length];
+    exceptions = new String[values.length];
+    
+    System.arraycopy(runnables, 0, values, 0, runnables.length);
+    for (int i=0, j=runnables.length; i<exceptionClsNames.length; i++, j++){
+      values[j] = exceptionThread;
+      exceptions[j] = exceptionClsNames[i];
+    }
+    
+    isSchedulingPoint = true; // not much use otherwise
+  }
+  
+  public String getExceptionForCurrentChoice(){
+    if ((count >= 0) && (count < values.length)) {
+      return exceptions[count];
+    } else {
+      return null;
+    }
+  }
+  
+  @Override
+  public ThreadChoiceGenerator reorder (Comparator<ThreadInfo> comparator){
+    ThreadInfo[] newValues = values.clone();
+    Arrays.sort(newValues, comparator);
+    
+    // we don't really reorder occurrences of the exceptionThread, but since the Comparator 
+    // only knows ThreadInfos that shouldn't matter
+    String[] newExceptions = new String[exceptions.length];
+    for (int i=0, j=-1; i<newValues.length; i++){
+      if (newValues[i] == exceptionThread){
+        for (j++; exceptions[j] == null; j++);
+        newExceptions[i] = exceptions[j];
+      }
+    }
+
+    try {
+      ExceptionThreadChoiceFromSet reorderedCG = (ExceptionThreadChoiceFromSet)clone();
+      reorderedCG.values = newValues;
+      reorderedCG.exceptions = newExceptions;
+      reorderedCG.count = -1;
+      
+      return reorderedCG;
+      
+    } catch (CloneNotSupportedException cnsx){
+      throw new JPFException("clone of ExceptionalThreadChoice failed");
+    }
+  }
+  
+  @Override
+  public ThreadChoiceFromSet randomize () {
+    for (int i = values.length - 1; i > 0; i--) {
+      int j = random.nextInt(i + 1);
+      ThreadInfo tmp = values[i];
+      values[i] = values[j];
+      values[j] = tmp;
+      
+      String tmpX = exceptions[i];
+      exceptions[i] = exceptions[j];
+      exceptions[j] = tmpX;
+    }
+    return this;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/ExposureCG.java b/src/main/gov/nasa/jpf/vm/choice/ExposureCG.java
new file mode 100644 (file)
index 0000000..0a9abc4
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * a scheduling point for exposure of objects that might become shared
+ * by having their reference stored in a shared object
+ */
+public class ExposureCG extends ThreadChoiceFromSet {
+  protected int exposedObjRef;
+  
+  public ExposureCG (String id, ThreadInfo[] set, ElementInfo eiExposed){
+    super( id, set, true);
+    
+    exposedObjRef = eiExposed.getObjectRef();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/FloatChoiceFromList.java b/src/main/gov/nasa/jpf/vm/choice/FloatChoiceFromList.java
new file mode 100644 (file)
index 0000000..ee7ea06
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.FloatChoiceGenerator;
+
+public class FloatChoiceFromList extends NumberChoiceFromList<Float> implements FloatChoiceGenerator {
+
+  
+  @Override
+  protected Float[] createValueArray(int len){
+    return new Float[len];
+  }
+  @Override
+  protected Float getDefaultValue() {
+    return 0.0f;
+  }
+    
+  @Override
+  public Class<Float> getChoiceType(){
+    return Float.class;
+  }
+    
+  @Override
+  protected Float parseLiteral (String literal, int sign){
+    Float val = Float.parseFloat(literal);
+    return new Float( val * sign);
+  }
+  
+  @Override
+  protected Float newValue (Number num, int sign){
+    return new Float( num.intValue() * sign);
+  }
+  
+  /**
+   *  super constructor for subclasses that want to configure themselves
+   * @param id name used in choice config
+   */
+  protected FloatChoiceFromList(String id){
+    super(id);
+  }
+
+  protected FloatChoiceFromList (String id, Float[] vals){
+    super(id, vals);
+  }
+  
+  public FloatChoiceFromList(Config conf, String id) {
+    super(conf, id);
+  }
+
+  public FloatChoiceFromList(String id, float... val){
+    super(id);
+
+    if (val != null){
+      values = new Float[val.length];
+      for (int i=0; i<val.length; i++){
+        values[i] = val[i];  // enable use of cached Float values
+      }
+    } else {
+      throw new JPFException("empty set for FloatChoiceFromList");
+    }
+
+    count = -1;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/IntChoiceFromList.java b/src/main/gov/nasa/jpf/vm/choice/IntChoiceFromList.java
new file mode 100644 (file)
index 0000000..ebb7706
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+/**
+ * @author jpenix
+ *
+ * choose from a set of values provided in configuration as
+ * xxx.class = IntChoiceFromList
+ * xxx.values = {1, 2, 3, 400}
+ * where "xxx" is the choice id.
+ * 
+ * choices can then made using: getInt("xxx");
+ */
+public class IntChoiceFromList extends NumberChoiceFromList<Integer> implements IntChoiceGenerator {
+
+  
+  @Override
+  protected Integer[] createValueArray(int len){
+    return new Integer[len];
+  }
+  @Override
+  protected Integer getDefaultValue() {
+    return 0;
+  }
+    
+  @Override
+  public Class<Integer> getChoiceType(){
+    return Integer.class;
+  }
+       
+  @Override
+  protected Integer parseLiteral (String literal, int sign){
+    int val = Integer.parseInt(literal);
+    return new Integer( val * sign);
+  }
+  
+  @Override
+  protected Integer newValue (Number num, int sign){
+    return new Integer( num.intValue() * sign);
+  }
+  
+  /**
+   *  super constructor for subclasses that want to configure themselves
+   * @param id name used in choice config
+   */
+  protected IntChoiceFromList(String id){
+    super(id);
+  }
+
+  protected IntChoiceFromList (String id, Integer[] vals){
+    super(id, vals);
+  }
+  
+       public IntChoiceFromList(Config conf, String id) {
+               super(conf, id);
+       }
+
+  public IntChoiceFromList(String id, int... val){
+    super(id);
+
+    if (val != null){
+      values = new Integer[val.length];
+      for (int i=0; i<val.length; i++){
+        values[i] = val[i];  // enable use of cached Integer values
+      }
+    } else {
+      throw new JPFException("empty set for IntChoiceFromList");
+    }
+
+    count = -1;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/IntChoiceFromSet.java b/src/main/gov/nasa/jpf/vm/choice/IntChoiceFromSet.java
new file mode 100644 (file)
index 0000000..3d71eff
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+/**
+ * @author cartho
+ *
+ * choose from a set of values provided in configuration as
+ * xxx.class = IntChoiceFromSet
+ * xxx.values = {1, 2, 3, 400}
+ * where "xxx" is the choice id.
+ * 
+ * choices can then made using: getInt("xxx");
+ */
+public class IntChoiceFromSet extends IntChoiceFromList {
+
+       /**
+        * @param conf JPF configuration object
+        * @param id name used in choice config
+        */
+       public IntChoiceFromSet(Config conf, String id) {
+               super(conf, id);
+               removeDuplicates();
+       }
+
+       /* Remove duplicate values; currently implemented as iteration
+        * on array to avoid heavyweight TreeSet */
+       private void removeDuplicates() {
+               int len = values.length;
+               for (int i = 0; i < len; i++) {
+                       int j = i + 1;
+                       while (j < len) {
+                               if (values[i] - values[j] == 0) { // strange comparison to avoid unboxing
+                                       values[j] = values[len - 1];
+                                       len--;
+                                       // don't increment j as new element has been placed there and needs to be re-tested
+                               } else {
+                                       j++;
+                               }
+                       }
+               }
+               if (len < values.length) {
+                       Integer[] uniqVals = new Integer[len];
+                       System.arraycopy(values, 0, uniqVals, 0, len);
+                       values = uniqVals;
+               }
+       }
+
+  public IntChoiceFromSet(String id, int... val){
+    super(id, val);
+    removeDuplicates();
+    count = -1;
+  }
+
+       /** super constructor for subclasses that want to configure themselves
+        * 
+        * @param id name used in choice config
+        */
+       protected IntChoiceFromSet(String id){
+               super(id);
+       }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/IntIntervalGenerator.java b/src/main/gov/nasa/jpf/vm/choice/IntIntervalGenerator.java
new file mode 100644 (file)
index 0000000..8120c9b
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/**
+ * Choice Generator that enumerates an interval of int values. Pretty simplistic
+ * implementation for now, but at least it can count up and down
+ *
+ * randomizing is handled through RandomOrderIntCG
+ */
+public class IntIntervalGenerator extends ChoiceGeneratorBase<Integer> implements IntChoiceGenerator {
+
+  protected int min, max;
+  protected int next;
+  protected int delta;
+
+  @Override
+  public void reset () {
+    isDone = false;
+
+    if (delta == 0) {
+      throw new JPFException("IntIntervalGenerator delta value is 0");
+    }
+
+    if (min > max) {
+      int t = max;
+      max = min;
+      min = t;
+    }
+
+    if (delta > 0) {
+      next = min - delta;
+    } else {
+      next = max - delta;
+    }
+  }
+
+  /**
+   *  don't use this since it is not safe for cascaded ChoiceGenerators
+   * (we need the 'id' to be as context specific as possible)
+   */
+  @Deprecated public IntIntervalGenerator(int min, int max){
+    this("?", min, max);
+  }
+
+  @Deprecated public IntIntervalGenerator(int min, int max, int delta){
+    this("?", min, max, delta);
+  }
+
+  public IntIntervalGenerator(String id, int min, int max, int delta) {
+    super(id);
+
+    this.min = min;
+    this.max = max;
+    this.delta = delta;
+
+    reset();
+  }
+
+  public IntIntervalGenerator(String id, int min, int max) {
+    this(id, min, max, 1);
+  }
+
+  public IntIntervalGenerator(Config conf, String id) {
+    super(id);
+    min = conf.getInt(id + ".min");
+    max = conf.getInt(id + ".max");
+    delta = conf.getInt(id + ".delta", 1);
+
+    reset();
+  }
+
+  @Override
+  public Integer getChoice (int idx){
+    int nChoices = getTotalNumberOfChoices();
+    if (idx >= 0 && idx < nChoices){
+      return min + idx*delta;
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+  
+  @Override
+  public Integer getNextChoice () {
+    return new Integer(next);
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    if (isDone) {
+      return false;
+    } else {
+      if (delta > 0) {
+        return (next < max);
+      } else {
+        return (next > min);
+      }
+    }
+  }
+
+  @Override
+  public void advance () {
+    next += delta;
+  }
+  
+  @Override
+  public int getTotalNumberOfChoices () {
+    return Math.abs((max - min) / delta) + 1;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    if (delta > 0) {
+      if (next < min){
+        return 0;
+      } else {
+        return (Math.abs((next - min) / delta) + 1);
+      }
+    } else {
+      if (next > max){
+        return 0;
+      } else {
+        return (Math.abs((max - next) / delta) + 1);
+      }
+    }
+  }
+  
+  public boolean isAscending(){
+    return delta > 0;
+  }
+
+  /**
+   *  note this should only be called before the CG is advanced since it resets
+   *  the enumeration state 
+   */
+  public void reverse(){
+    delta = -delta;
+    reset();
+  }
+  
+  
+  public Integer[] getChoices(){
+    int n = getTotalNumberOfChoices();
+    Integer[] vals = new Integer[n];
+    int v = (delta > 0) ? min : max;
+    
+    for (int i=0; i<n; i++){
+      vals[i] = v;
+      v += delta;
+    }
+    
+    return vals;
+  }
+
+  @Override
+  public boolean supportsReordering(){
+    return true;
+  }
+  
+  @Override
+  public ChoiceGenerator<Integer> reorder (Comparator<Integer> comparator){
+    Integer[] vals = getChoices();
+    Arrays.sort(vals, comparator);
+    
+    return new IntChoiceFromList(id, vals);
+  }
+  
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append("[id=\"");
+    sb.append(id);
+    sb.append('"');
+
+    sb.append(",isCascaded:");
+    sb.append(isCascaded);
+
+    sb.append(",");
+    sb.append(min);
+    sb.append("..");
+    sb.append(max);
+    sb.append(",delta=");
+    if (delta > 0) {
+      sb.append('+');
+    }
+    sb.append(delta);
+    sb.append(",cur=");
+    sb.append(getNextChoice());
+    sb.append(']');
+    return sb.toString();
+  }
+
+  @Override
+  public Class<Integer> getChoiceType() {
+    return Integer.class;
+  }
+
+  @Override
+  public ChoiceGenerator<Integer> randomize() {
+    return new RandomOrderIntCG(this);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/InvocationCG.java b/src/main/gov/nasa/jpf/vm/choice/InvocationCG.java
new file mode 100644 (file)
index 0000000..f10e6ca
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.util.Invocation;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.ListIterator;
+
+/**
+ * ChoiceGenerator that represents method calls
+ */
+public class InvocationCG extends ChoiceGeneratorBase<Invocation> {
+
+  protected List<Invocation> invokes;
+  protected Invocation cur;
+  protected ListIterator<Invocation> it;
+  
+  public InvocationCG (String id, List<Invocation> invokes){
+    super(id);
+    
+    this.invokes = invokes;
+    
+    it = invokes.listIterator();
+  }
+  
+  @Override
+  public Invocation getChoice (int idx){
+    if (idx >=0 && idx < invokes.size()){
+      return invokes.get(idx);
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+  
+  @Override
+  public void advance () {
+    cur = it.next();
+  }
+
+  @Override
+  public Class<Invocation> getChoiceType () {
+    return Invocation.class;
+  }
+
+  @Override
+  public Invocation getNextChoice () {
+    return cur;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return it.nextIndex();
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return invokes.size();
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    return it.hasNext();
+  }
+
+  @Override
+  public ChoiceGenerator<Invocation> randomize () {
+    // <2do>
+    return this;
+  }
+
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append(" [");
+    int n = invokes.size();
+    for (int i=0; i<n; i++) {
+      if (i > 0) sb.append(',');
+      Invocation inv = invokes.get(i);
+      if (inv == cur) {
+        sb.append(MARKER);
+      }
+      sb.append(inv);
+    }
+    sb.append(']');
+    return sb.toString();
+  }
+  
+  public void printOn (PrintWriter pw) {
+    pw.print(toString());
+  }
+  
+  @Override
+  public void reset () {
+    cur = null;
+    it = invokes.listIterator();
+
+    isDone = false;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/LongChoiceFromList.java b/src/main/gov/nasa/jpf/vm/choice/LongChoiceFromList.java
new file mode 100644 (file)
index 0000000..10a77ab
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.LongChoiceGenerator;
+
+public class LongChoiceFromList extends NumberChoiceFromList<Long> implements LongChoiceGenerator {
+
+  @Override
+  protected Long[] createValueArray(int len) {
+    return new Long[len];
+  }
+
+  @Override
+  protected Long getDefaultValue() {
+    return 0L;
+  }
+
+  @Override
+  public Class<Long> getChoiceType() {
+    return Long.class;
+  }
+
+  @Override
+  protected Long parseLiteral(String literal, int sign) {
+    long val = Long.parseLong(literal);
+    return new Long(val * sign);
+  }
+
+  @Override
+  protected Long newValue(Number num, int sign) {
+    return new Long(num.longValue() * sign);
+  }
+
+  /**
+   * super constructor for subclasses that want to configure themselves
+   * 
+   * @param id
+   *          name used in choice config
+   */
+  protected LongChoiceFromList(String id) {
+    super(id);
+  }
+
+  protected LongChoiceFromList(String id, Long[] vals) {
+    super(id, vals);
+  }
+
+  public LongChoiceFromList(Config conf, String id) {
+    super(conf, id);
+  }
+
+  public LongChoiceFromList(String id, long... val) {
+    super(id);
+
+    if (val != null) {
+      values = new Long[val.length];
+      for (int i = 0; i < val.length; i++) {
+        values[i] = val[i]; // enable use of cached Integer values
+      }
+    } else {
+      throw new JPFException("empty set for LongChoiceFromList");
+    }
+
+    count = -1;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/NumberChoiceFromList.java b/src/main/gov/nasa/jpf/vm/choice/NumberChoiceFromList.java
new file mode 100644 (file)
index 0000000..ceb53a5
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.Arrays;
+import java.util.Comparator;
+
+/** 
+ * common root for list based number choice generators
+ */
+public abstract class NumberChoiceFromList<T extends Number> extends ChoiceGeneratorBase<T> {
+
+  // int values to choose from stored as Strings or Integers
+  protected T[] values;
+  protected int count = -1;
+  
+  /**
+   *  super constructor for subclasses that want to configure themselves
+   * @param id name used in choice config
+   */
+  protected NumberChoiceFromList(String id){
+    super(id);
+  }
+
+  protected NumberChoiceFromList (String id, T[] vals){
+    super(id);
+    values = vals;
+    count = -1;
+  }
+  
+  protected abstract T[] createValueArray(int len);
+  protected abstract T getDefaultValue();
+  protected abstract T parseLiteral (String literal, int sign);
+  protected abstract T newValue (Number num, int sign);
+  
+  /**
+   * @param conf JPF configuration object
+   * @param id name used in choice config
+   */
+  public NumberChoiceFromList(Config conf, String id) {
+    super(id);
+
+    String[] vals = conf.getCompactStringArray(id + ".values");
+    if (vals == null || vals.length == 0) {
+      throw new JPFException("no value specs for IntChoiceFromList " + id);
+    }
+
+    // get the choice values here because otherwise successive getNextChoice()
+    // calls within the same transition could see different values when looking
+    // up fields and locals
+    values = createValueArray(vals.length);
+    StackFrame resolveFrame = ThreadInfo.getCurrentThread().getLastNonSyntheticStackFrame();
+    for (int i=0; i<vals.length; i++){
+      values[i] = parse(vals[i], resolveFrame);
+    }
+  }
+  
+  @Override
+  public T getChoice (int idx){
+    if (idx >=0 && idx < values.length){
+      return values[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+  @Override
+  public void reset () {
+    count = -1;
+
+    isDone = false;
+  }
+      
+  /** 
+   * @see gov.nasa.jpf.vm.IntChoiceGenerator#getNextChoice()
+   **/
+  @Override
+  public T getNextChoice() {
+
+    if ((count >= 0) && (count < values.length)) {
+      return values[count];
+    }
+
+    return getDefaultValue();
+  }
+
+  /**
+   * @see gov.nasa.jpf.vm.ChoiceGenerator#hasMoreChoices()
+   **/
+  @Override
+  public boolean hasMoreChoices() {
+    if (!isDone && (count < values.length-1))  
+      return true;
+    else
+      return false;
+  }
+
+  /**
+   * @see gov.nasa.jpf.vm.ChoiceGenerator#advance()
+   **/
+  @Override
+  public void advance() {
+    if (count < values.length-1) count++;
+  }
+
+  /**
+   * get String label of current value, as specified in config file
+   **/
+  public String getValueLabel(){
+    return values[count].toString();
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return values.length;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return count+1;
+  }
+  
+  @Override
+  public boolean supportsReordering(){
+    return true;
+  }
+  
+  
+  protected T parse (String varSpec, StackFrame resolveFrame){
+    int sign = 1;
+
+    char c = varSpec.charAt(0);
+    if (c == '+'){
+      varSpec = varSpec.substring(1);
+    } else if (c == '-'){
+      sign = -1;
+      varSpec = varSpec.substring(1);
+    }
+
+    if (varSpec.isEmpty()){
+      throw new JPFException("illegal value spec for IntChoiceFromList " + id);
+    }
+
+    c = varSpec.charAt(0);
+    if (Character.isDigit(c)){ // its an integer literal
+      return parseLiteral(varSpec, sign);
+
+    } else { // a variable or field name
+      Object o = resolveFrame.getLocalOrFieldValue(varSpec);
+      if (o == null){
+        throw new JPFException("no local or field '" + varSpec + "' value found for NumberChoiceFromList " + id);
+      }
+      if (o instanceof Number){
+        return newValue( (Number)o, sign);
+      } else {
+        throw new JPFException("non-numeric local or field '" + varSpec + "' value for NumberChoiceFromList " + id);
+      }
+    }
+  }
+
+  
+  @Override
+  public NumberChoiceFromList<T> reorder (Comparator<T> comparator){
+    
+    T[] newValues = values.clone();
+    Arrays.sort( newValues, comparator);
+    
+    // we can't instantiate directly
+    try {
+    NumberChoiceFromList<T> clone = (NumberChoiceFromList<T>)clone();
+    clone.values = newValues;
+    clone.count = -1;
+    return clone;
+    
+    } catch (CloneNotSupportedException cnsx){
+      // can't happen
+      throw new JPFException("can't clone NumberChoiceFromList " + this);
+    }
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    sb.append("[id=\"");
+    sb.append(id);
+    sb.append('"');
+
+    sb.append(",isCascaded:");
+    sb.append(isCascaded);
+
+    sb.append(",");
+    for (int i=0; i<values.length; i++) {
+      if (i > 0) {
+        sb.append(',');
+      }
+      if (i == count) {
+        sb.append(MARKER);
+      }
+      sb.append(values[i]);
+    }
+    sb.append(']');
+    return sb.toString();
+  }
+  
+  
+  @Override
+  public NumberChoiceFromList<T> randomize () {
+    for (int i = values.length - 1; i > 0; i--) {
+      int j = random.nextInt(i + 1);
+      T tmp = values[i];
+      values[i] = values[j];
+      values[j] = tmp;
+    }
+    return this;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/PermutationCG.java b/src/main/gov/nasa/jpf/vm/choice/PermutationCG.java
new file mode 100644 (file)
index 0000000..e7bb2c9
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.util.PermutationGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+
+/**
+ * a CG that creates permutation choices
+ * 
+ * since PermutationCGs have a potentially huge number of choices, we don't
+ * compute and store them upfront, but rather keep the enumeration state in a
+ * low level pull-based generator
+ */
+public class PermutationCG extends ChoiceGeneratorBase<int[]>{
+  
+  protected PermutationGenerator pg;
+  protected int[] permutation;
+  
+  public PermutationCG (PermutationGenerator pg){
+    this.pg = pg;
+  }
+
+  public PermutationCG (String id, PermutationGenerator pg){
+    super(id);
+    this.pg = pg;
+  }
+  
+  @Override
+  public int[] getNextChoice() {
+    return permutation;
+  }
+
+  @Override
+  public Class<int[]> getChoiceType() {
+    return int[].class;
+  }
+
+  @Override
+  public boolean hasMoreChoices() {
+    return pg.hasNext();
+  }
+
+  @Override
+  public void advance() {
+    permutation = pg.next();
+  }
+
+  @Override
+  public void reset() {
+    pg.reset();
+  }
+
+  @Override
+  public int getTotalNumberOfChoices() {
+    return (int) pg.getNumberOfPermutations();
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices() {
+    return (int) pg.getNumberOfGeneratedPermutations();
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/RandomIntIntervalGenerator.java b/src/main/gov/nasa/jpf/vm/choice/RandomIntIntervalGenerator.java
new file mode 100644 (file)
index 0000000..dfad959
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+
+import java.util.Random;
+
+/**
+ * a IntChoiceGenerator that randomly chooses a configured number
+ * of values from a specified range
+ * this is usually configured through app properties
+ * 
+ * <2do> this is too redundant to RandomOrderIntCG - replace
+ */
+public class RandomIntIntervalGenerator extends ChoiceGeneratorBase<Integer> implements IntChoiceGenerator {
+
+  protected int min, max; // both inclusive
+  protected int nChoices;
+  protected long seed;
+
+  protected Random random;
+  protected int range;
+
+  protected int next;
+  protected int count = 0;
+
+  public RandomIntIntervalGenerator (String id, int min, int max, int nChoices){
+    this(id, min,max,nChoices,0L);
+  }
+
+  public RandomIntIntervalGenerator (String id, int min, int max, int nChoices, long seed){
+    super(id);
+
+    this.min = min;
+    this.max = max;
+    this.nChoices = nChoices;
+    this.seed = seed;
+
+    range = max - min;
+    random = new Random(seed);
+  }
+
+  public RandomIntIntervalGenerator(Config conf, String id) {
+    super(id);
+
+    min = conf.getInt(id + ".min");
+    max = conf.getInt(id + ".max");
+    nChoices = conf.getInt(id + ".n", 1);
+    seed = conf.getLong(id + ".seed", 1);
+
+    range = max - min;
+    random = new Random(seed);
+  }
+
+  @Override
+  public Integer getChoice (int idx){
+    if (idx >= 0 && idx < nChoices){
+      // Ok, this is really not efficient - use only for non-performance critical operations
+      Random r = new Random(seed);
+      int v=0;
+      for (int i=0; i<idx; i++){
+        v = r.nextInt(range);
+      }
+      return v + min;
+      
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+  @Override
+  public void reset () {
+    random = new Random(seed);
+    count = 0;
+
+    isDone = false;
+  }
+
+       @Override
+       public boolean hasMoreChoices() {
+    return !isDone && (count < nChoices);
+       }
+
+  @Override
+  public void advance (){
+    if (count < nChoices){
+      count++;
+      next = random.nextInt(range) + min;
+    }
+  }
+
+  @Override
+  public Integer getNextChoice () {
+    return new Integer(next);
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return nChoices;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return count;
+  }
+
+  @Override
+  public String toString () {
+    StringBuilder sb = new StringBuilder(getClass().getName());
+    if (id == null) {
+      sb.append('[');
+    } else {
+      sb.append("[id=\"");
+      sb.append(id);
+      sb.append("\",");
+    }
+    sb.append(min);
+    sb.append("..");
+    sb.append(max);
+    sb.append(",n=");
+    sb.append(nChoices);
+    sb.append(",cur=");
+    sb.append(getNextChoice());
+    sb.append(",count=");
+    sb.append(count);
+    sb.append(']');
+    return sb.toString();
+  }
+
+  @Override
+  public Class<Integer> getChoiceType() {
+    return Integer.class;
+  }
+
+  @Override
+  public ChoiceGenerator<Integer> randomize() {
+    return this;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/RandomOrderIntCG.java b/src/main/gov/nasa/jpf/vm/choice/RandomOrderIntCG.java
new file mode 100644 (file)
index 0000000..e16528f
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+
+/**
+ * a generic IntChoiceGenerator randomizer. Not very efficient for large int intervals
+ */
+public class RandomOrderIntCG extends ChoiceGeneratorBase<Integer> implements IntChoiceGenerator {
+  protected int[] choices;
+  
+  protected int nextIdx;
+  
+  public RandomOrderIntCG(IntChoiceGenerator sub) {
+    super(sub.getId());
+    setPreviousChoiceGenerator(sub.getPreviousChoiceGenerator());
+    choices = new int[sub.getTotalNumberOfChoices()];
+    for (int i = 0; i < choices.length; i++) {
+      sub.advance();
+      choices[i] = sub.getNextChoice();
+    }
+    for (int i = choices.length - 1; i > 0; i--) { // all but first
+      int j = random.nextInt(i + 1);
+      int tmp = choices[i];
+      choices[i] = choices[j];
+      choices[j] = tmp;
+    }
+    nextIdx = -1;
+  }
+
+  @Override
+  public Integer getChoice (int idx){
+    if (idx >= 0 && idx < choices.length){
+      return choices[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+  
+  @Override
+  public Integer getNextChoice() {
+    return new Integer(choices[nextIdx]);
+  }
+
+  @Override
+  public void advance() {
+    if (nextIdx + 1 < choices.length) nextIdx++;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices() {
+    return nextIdx+1;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices() {
+    return choices.length;
+  }
+
+  @Override
+  public boolean hasMoreChoices() {
+    return !isDone && (nextIdx + 1 < choices.length);
+  }
+
+  @Override
+  public void reset() {
+    nextIdx = -1;
+
+    isDone = false;
+  }
+
+  @Override
+  public Class<Integer> getChoiceType() {
+    return Integer.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/RandomOrderLongCG.java b/src/main/gov/nasa/jpf/vm/choice/RandomOrderLongCG.java
new file mode 100644 (file)
index 0000000..354a34d
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.LongChoiceGenerator;
+
+/**
+ *
+ */
+public class RandomOrderLongCG extends ChoiceGeneratorBase<Long> implements LongChoiceGenerator {
+  protected long[] choices;
+
+  protected int nextIdx;
+
+  public RandomOrderLongCG (LongChoiceGenerator sub) {
+    super(sub.getId());
+    setPreviousChoiceGenerator(sub.getPreviousChoiceGenerator());
+    choices = new long[sub.getTotalNumberOfChoices()];
+    for (int i = 0; i < choices.length; i++) {
+      sub.advance();
+      choices[i] = sub.getNextChoice();
+    }
+    for (int i = choices.length - 1; i > 0; i--) { // all but first
+      int j = random.nextInt(i + 1);
+      long tmp = choices[i];
+      choices[i] = choices[j];
+      choices[j] = tmp;
+    }
+    nextIdx = -1;
+  }
+  
+  @Override
+  public Long getChoice (int idx){
+    if (idx >= 0 && idx < choices.length){
+      return choices[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+
+  @Override
+  public Long getNextChoice() {
+    return new Long(choices[nextIdx]);
+  }
+
+  @Override
+  public void advance() {
+    if (nextIdx + 1 < choices.length) nextIdx++;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices() {
+    return nextIdx+1;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices() {
+    return choices.length;
+  }
+
+  @Override
+  public boolean hasMoreChoices() {
+    return !isDone && (nextIdx + 1 < choices.length);
+  }
+
+  @Override
+  public void reset() {
+    nextIdx = -1;
+
+    isDone = false;
+  }
+
+  @Override
+  public Class<Long> getChoiceType() {
+    return Long.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/ThreadChoiceFromSet.java b/src/main/gov/nasa/jpf/vm/choice/ThreadChoiceFromSet.java
new file mode 100644 (file)
index 0000000..49485a4
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.vm.ChoiceGeneratorBase;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Comparator;
+
+public class ThreadChoiceFromSet extends ChoiceGeneratorBase<ThreadInfo> implements ThreadChoiceGenerator {
+
+  protected boolean isSchedulingPoint;
+  protected ThreadInfo[] values;
+  protected int count;
+    
+  protected ThreadChoiceFromSet (String id){
+    super(id);
+    
+    // all other fields have to be computed by subclass ctor
+    count = -1;
+  }
+  
+  public ThreadChoiceFromSet (String id, ThreadInfo[] set, boolean isSchedulingPoint) {
+    super(id);
+        
+    values = set;
+    count = -1;
+
+    this.isSchedulingPoint = isSchedulingPoint;
+
+    /**
+    if (isSchedulingPoint){
+      // do a sanity check to see if the candidates are acutally runnable
+      for (int i = 0; i < set.length; i++) {
+        if (!set[i].isTimeoutRunnable()) {
+          throw new JPFException("trying to schedule non-runnable: " + set[i]);
+        }
+      }
+    }
+    **/
+  }
+  
+  @Override
+  public ThreadInfo getChoice (int idx){
+    if (idx >= 0 && idx < values.length){
+      return values[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+  
+  @Override
+  public void reset () {
+    count = -1;
+
+    isDone = false;
+  }
+  
+  @Override
+  public ThreadInfo getNextChoice () {
+    if ((count >= 0) && (count < values.length)) {
+      return values[count];
+    } else {
+      // we don't raise an exception here because this might be (mis)used
+      // from a listener, which shouldn't produce JPFExceptions
+      return null;
+    }
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    return (!isDone && (count < values.length-1));
+  }
+
+
+  /**
+   * this has to handle timeouts, which we do with temporary thread status
+   * changes (i.e. the TIMEOUT_WAITING threads are in our list of choices, but
+   * only change their status to TIMEDOUT when they are picked as the next choice)
+   *
+   * <2do> this should be in SystemState.nextSuccessor - there might be
+   * other ThreadChoiceGenerators, and we should handle this consistently
+   */
+  @Override
+  public void advance () {    
+    if (count < values.length-1) { // at least one choice left
+      count++;
+    }
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return values.length;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return count+1;
+  }
+
+  public Object getNextChoiceObject () {
+    return getNextChoice();
+  }
+  
+  public ThreadInfo[] getChoices(){
+    return values;
+  }
+  
+  @Override
+  public boolean supportsReordering(){
+    return true;
+  }
+  
+  @Override
+  public ThreadChoiceGenerator reorder (Comparator<ThreadInfo> comparator){
+    ThreadInfo[] newValues = values.clone();
+    Arrays.sort(newValues, comparator);
+    
+    return new ThreadChoiceFromSet( id, newValues, isSchedulingPoint);
+  }
+  
+  @Override
+  public void printOn (PrintWriter pw) {
+    pw.print(getClass().getName());
+    pw.append("[id=\"");
+    pw.append(id);
+    pw.append('"');
+
+    pw.append(",isCascaded:");
+    pw.append(Boolean.toString(isCascaded));
+
+    pw.print(",{");
+    for (int i=0; i<values.length; i++) {
+      if (i > 0) pw.print(',');
+      if (i == count) {
+        pw.print(MARKER);
+      }
+      pw.print(values[i].getName());
+    }
+    pw.print("}]");
+  }
+  
+  @Override
+  public ThreadChoiceFromSet randomize () {
+    for (int i = values.length - 1; i > 0; i--) {
+      int j = random.nextInt(i + 1);
+      ThreadInfo tmp = values[i];
+      values[i] = values[j];
+      values[j] = tmp;
+    }
+    return this;
+  }
+  
+  public ThreadInfo[] getAllThreadChoices() {
+         return values; 
+  }
+  
+  @Override
+  public boolean contains (ThreadInfo ti){
+    for (int i=0; i<values.length; i++){
+      if (values[i] == ti){
+        return true;
+      }
+    }
+    return false;
+  }
+
+  @Override
+  public Class<ThreadInfo> getChoiceType() {
+    return ThreadInfo.class;
+  }
+  
+  @Override
+  public boolean isSchedulingPoint() {
+    return isSchedulingPoint;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/choice/TypedObjectChoice.java b/src/main/gov/nasa/jpf/vm/choice/TypedObjectChoice.java
new file mode 100644 (file)
index 0000000..14604ad
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.*;
+
+import java.util.ArrayList;
+
+/**
+ * a choice generator that enumerates the set of all objects of a certain type. This
+ * is a replacement for the old 'Verify.randomObject'
+ */
+public class TypedObjectChoice extends ChoiceGeneratorBase<Integer> implements ReferenceChoiceGenerator {
+
+  // the requested object type
+  protected String type;
+  
+  // the object references
+  protected int[] values;
+  
+  // our enumeration state
+  protected int count;
+
+  
+  public TypedObjectChoice (Config conf, String id)  {
+    super(id);
+    
+    Heap heap = VM.getVM().getHeap();
+    
+    type = conf.getString(id + ".type");
+    if (type == null) {
+      throw conf.exception("missing 'type' property for TypedObjectChoice " + id);
+    }
+
+    ArrayList<ElementInfo> list = new ArrayList<ElementInfo>();
+    
+    for ( ElementInfo ei : heap.liveObjects()) {
+      ClassInfo ci = ei.getClassInfo();
+      if (ci.isInstanceOf(type)) {
+        list.add(ei);
+      }
+    }
+
+    if (conf.getBoolean(id + ".include_null", true)){
+      list.add(null);
+    }
+
+    values = new int[list.size()];
+    int i = 0;
+    for ( ElementInfo ei : list) {
+      if (ei != null) {
+        values[i++] = ei.getObjectRef();
+      } else {
+        values[i++] = MJIEnv.NULL;
+      }
+    }
+    
+    count = -1;
+  }
+  
+  @Override
+  public Integer getChoice (int idx){
+    if (idx >= 0 && idx < values.length){
+      return values[idx];
+    } else {
+      throw new IllegalArgumentException("choice index out of range: " + idx);
+    }
+  }
+
+  
+  @Override
+  public void advance () {
+    count++;
+  }
+
+  @Override
+  public int getProcessedNumberOfChoices () {
+    return count+1;
+  }
+
+  @Override
+  public int getTotalNumberOfChoices () {
+    return values.length;
+  }
+
+  @Override
+  public boolean hasMoreChoices () {
+    return !isDone && (count < values.length-1);
+  }
+
+  @Override
+  public void reset () {
+    count = -1;
+
+    isDone = false;
+  }
+
+  @Override
+  public Integer getNextChoice () {
+    if ((count >= 0) && (count < values.length)) {
+      return new Integer(values[count]);
+    } else {
+      return new Integer(-1);
+    }
+  }
+  
+  @Override
+  public String toString() {
+    StringBuilder sb = new StringBuilder("TypedObjectChoice [id=");
+    sb.append(id);
+    sb.append(",type=");
+    sb.append(type);
+    sb.append(",values=");
+    for (int i=0; i< values.length; i++) {
+      if (i>0) {
+        sb.append(',');
+      }
+      if (i == count) {
+        sb.append("=>");
+      }
+      sb.append(values[i]);
+    }
+    sb.append(']');
+    
+    return sb.toString();
+  }
+  
+  @Override
+  public TypedObjectChoice randomize() {
+    for (int i = values.length - 1; i > 0; i--) {
+      int j = random.nextInt(i + 1);
+      int tmp = values[i];
+      values[i] = values[j];
+      values[j] = tmp;
+    }
+    return this;
+  }
+
+  @Override
+  public Class<Integer> getChoiceType() {
+    return Integer.class;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/Abstraction.java b/src/main/gov/nasa/jpf/vm/serialize/Abstraction.java
new file mode 100644 (file)
index 0000000..190df68
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+/**
+ * interface to abstract concrete primitive values for the sake of state matching 
+ */
+public interface Abstraction {
+  
+  int getAbstractValue (int v);
+  int getAbstractValue (float v);
+  int getAbstractValue (long v);
+  int getAbstractValue (double v);
+  
+  int getAbstractObject (int ref);
+  boolean traverseObject (int ref);
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/AbstractionAdapter.java b/src/main/gov/nasa/jpf/vm/serialize/AbstractionAdapter.java
new file mode 100644 (file)
index 0000000..ab92c9e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.MJIEnv;
+
+/**
+ * (mostly) pass-through Abstraction
+ */
+public class AbstractionAdapter implements Abstraction {
+
+  @Override
+  public int getAbstractValue(int v) {
+    return v;
+  }
+
+  @Override
+  public int getAbstractValue(float v) {
+    return Float.floatToIntBits(v);
+  }
+
+  @Override
+  public int getAbstractValue(long v) {
+    return (int)(v^(v>>>32));  // Long.hashValue
+  }
+
+  @Override
+  public int getAbstractValue(double v) {
+    long l = Double.doubleToLongBits(v);
+    return (int)(l^(l>>>32)); // Double.hashValue
+  }
+
+  @Override
+  public int getAbstractObject(int ref) {
+    return ref;
+  }
+
+  @Override
+  public boolean traverseObject(int ref) {
+    return (ref != MJIEnv.NULL);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/AdaptiveSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/AdaptiveSerializer.java
new file mode 100644 (file)
index 0000000..3b3adba
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.ElementInfo;
+
+/**
+ * a CG type adaptive, canonicalizing & filtering serializer that is an
+ * under-approximation mostly aimed at finding data races and deadlocks in programs
+ * with a large number of scheduling points (= thread choices)
+ *
+ * This came to bear by accidentally discovering that JPF often seems to finds
+ * concurrency defects by just serializing the thread states, their topmost stack
+ * frames and the objects directly referenced from there.
+ * For non-scheduling points, we just fall back to serializing statics, all thread
+ * stacks and all the data reachable from there
+ */
+public class AdaptiveSerializer extends CFSerializer {
+
+  boolean traverseObjects;
+  boolean isSchedulingPoint;
+
+  @Override
+  protected void initReferenceQueue() {
+    super.initReferenceQueue();
+    traverseObjects = true;
+
+    ChoiceGenerator<?> nextCg = vm.getNextChoiceGenerator();
+    isSchedulingPoint = (nextCg != null) && nextCg.isSchedulingPoint();
+  }
+
+  @Override
+  protected void queueReference(ElementInfo ei){
+    if (traverseObjects){
+      refQueue.add(ei);
+    }
+  }
+
+  @Override
+  protected void processReferenceQueue() {
+    if (isSchedulingPoint){
+      traverseObjects = false;
+    }
+    refQueue.process(this);
+  }
+
+  //@Override
+  @Override
+  protected void serializeClassLoaders(){
+    // for thread CGs we skip this - assuming that this is only relevant if there is
+    // a class object lock, which is covered by the thread lock info
+    if (!isSchedulingPoint){
+      // <2do> this seems too conservative - we should only serialize what is
+      // used from this thread, which can be collected at class load time
+      // by looking at GET/PUTSTATIC targets (and their superclasses)
+      super.serializeClassLoaders();
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/AmmendableFilterConfiguration.java b/src/main/gov/nasa/jpf/vm/serialize/AmmendableFilterConfiguration.java
new file mode 100644 (file)
index 0000000..1f7befd
--- /dev/null
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.ObjVector;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.util.ArrayList;
+
+public class AmmendableFilterConfiguration implements FilterConfiguration {
+  public interface Ammendment {
+    public static final boolean POLICY_IGNORE = false;
+    public static final boolean POLICY_INCLUDE = true;
+  }
+  
+  public interface FrameAmmendment extends Ammendment {
+    FramePolicy ammendFramePolicy(MethodInfo mi, FramePolicy sofar);
+    
+    //BitArray ammendLocalInclusion(MethodInfo mi, BitArray sofar);
+  }
+  
+  public interface InstanceAmmendment extends Ammendment {
+    boolean ammendFieldInclusion(FieldInfo fi, boolean sofar);
+  }
+  
+  public interface StaticAmmendment extends Ammendment {
+    boolean ammendFieldInclusion(FieldInfo fi, boolean sofar);
+  }
+
+  // convience for implementing both
+  public interface FieldAmmendment extends InstanceAmmendment, StaticAmmendment {}
+  
+  public interface InstanceOverride {
+    boolean isOverriding(ClassInfo ci);
+    
+    Iterable<FieldInfo> getMatchedInstanceFields(ClassInfo ci,
+                                  Iterable<FieldInfo> preOverride);
+  }
+
+  
+  protected final ObjVector<FrameAmmendment> frameAmmendments =
+    new ObjVector<FrameAmmendment>(0); 
+  
+  protected final ObjVector<InstanceAmmendment> instanceAmmendments =
+    new ObjVector<InstanceAmmendment>(0); 
+  
+  protected final ObjVector<StaticAmmendment> staticAmmendments =
+    new ObjVector<StaticAmmendment>(0); 
+  
+  protected final ObjVector<InstanceOverride> instanceOverrides =
+    new ObjVector<InstanceOverride>(0);
+  
+  
+  // configuration stuff
+  
+  @Override
+  public void init(Config config)  {
+    appendConfiguredFrameAmmendments(config);
+    appendConfiguredInstanceAmmendments(config);
+    appendConfiguredStaticAmmendments(config);
+    appendConfiguredInstanceOverrides(config);
+  }
+
+  protected void appendConfiguredFrameAmmendments(Config config) {
+    ArrayList<FrameAmmendment> fas =
+      config.getInstances("filter.frame_ammendments", FrameAmmendment.class);
+    if (fas != null) frameAmmendments.append(fas);
+  }
+  
+  protected void appendConfiguredInstanceAmmendments(Config config) {
+    ArrayList<InstanceAmmendment> ias =
+      config.getInstances("filter.instance_ammendments", InstanceAmmendment.class);
+    if (ias != null) instanceAmmendments.append(ias);
+  }  
+  
+  protected void appendConfiguredStaticAmmendments(Config config) {
+    ArrayList<StaticAmmendment> sas =
+      config.getInstances("filter.static_ammendments", StaticAmmendment.class);
+    if (sas != null) staticAmmendments.append(sas);
+  }
+  
+  protected void appendConfiguredInstanceOverrides(Config config) {
+    ArrayList<InstanceOverride> ios =
+      config.getInstances("filter.instance_overrides", InstanceOverride.class);
+    if (ios != null) instanceOverrides.append(ios);
+  }
+  
+
+  // public API for adding to configuration
+  public void appendFrameAmmendment(FrameAmmendment fa) {
+    frameAmmendments.add(fa);
+  }
+  
+  public void appendInstanceAmmendment(InstanceAmmendment ia) {
+    instanceAmmendments.add(ia);
+  }
+  
+  public void appendStaticAmmendment(StaticAmmendment sa) {
+    staticAmmendments.add(sa);
+  }
+  
+  public void appendFieldAmmendment(FieldAmmendment fa) {
+    staticAmmendments.add(fa);
+    instanceAmmendments.add(fa);
+  }
+
+  public void appendInstanceOverride(InstanceOverride io) {
+    instanceOverrides.add(io);
+  }
+  
+  
+  // FilterConfiguration Stuff
+  @Override
+  public FramePolicy getFramePolicy(MethodInfo mi) {
+    FramePolicy policy = new FramePolicy();
+    for (FrameAmmendment fa : frameAmmendments) {
+      policy = fa.ammendFramePolicy(mi, policy);
+    }
+    return policy;
+  }
+  
+  // not implemented
+  /*public BitArray getFrameLocalInclusion(MethodInfo mi) {
+    BitArray cur = new BitArray(mi.getMaxLocals());
+    cur.setAll(); // by default include everything
+    for (FrameAmmendment fa : frameAmmendments) {
+      cur = fa.ammendLocalInclusion(mi, cur);
+    }
+    return cur;
+  }*/
+
+  @Override
+  public Iterable<FieldInfo> getMatchedInstanceFields(ClassInfo ci) {
+    int fieldCount = ci.getNumberOfInstanceFields();
+    ObjVector<FieldInfo> v = new ObjVector<FieldInfo>(fieldCount); 
+    for (int i = 0; i < fieldCount; i++) {
+      FieldInfo field = ci.getInstanceField(i);
+      boolean include = true;
+      for (InstanceAmmendment ia : instanceAmmendments) {
+        include = ia.ammendFieldInclusion(field, include);
+      }
+      if (include) {
+        v.add(field);
+      }
+    }
+    Iterable<FieldInfo> ret = v;
+    for (InstanceOverride io : instanceOverrides) {
+      if (io.isOverriding(ci)) {
+        ret = io.getMatchedInstanceFields(ci, ret);
+      }
+    }
+    return ret;
+  }
+
+  @Override
+  public Iterable<FieldInfo> getMatchedStaticFields(ClassInfo ci) {
+    int fieldCount = ci.getNumberOfStaticFields();
+    ObjVector<FieldInfo> v = new ObjVector<FieldInfo>(fieldCount); 
+    for (int i = 0; i < fieldCount; i++) {
+      FieldInfo field = ci.getStaticField(i);
+      boolean include = true;
+      for (StaticAmmendment sa : staticAmmendments) {
+        include = sa.ammendFieldInclusion(field, include);
+      }
+      if (include) {
+        v.add(field);
+      }
+    }
+    return v;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/CFSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/CFSerializer.java
new file mode 100644 (file)
index 0000000..895b94a
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import java.util.Iterator;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.ThreadList;
+
+/**
+ * a FilteringSerializer that performs on-the-fly heap canonicalization to
+ * achieve heap symmetry. It does so by storing the order in which
+ * objects are referenced, not the reference values themselves.
+ *
+ * Use this serializer if the Heap implementation does not provide
+ * sufficient symmetry, i.e. reference values depend on the order of
+ * thread scheduling.
+ *
+ * Ad hoc heap symmetry is hard to achieve in the heap because of static initialization.
+ * Each time a thread loads a class all the static init (at least the class object and
+ * its fields) are associated with this thread, hence thread reference
+ * values depend on which classes are already loaded by other threads. Associating
+ * all allocations from inside of clinits to one address range doesn't help either
+ * because then this range will experience scheduling dependent orders. A hybrid
+ * approach in which only this segment is canonicalized might work, but it is
+ * questionable if the overhead is worth the effort.
+ */
+public class CFSerializer extends FilteringSerializer {
+
+  // we flip this on every serialization, which helps us to avoid passes
+  // over the serialized objects to reset their sids. This works by resetting
+  // the sid to 0 upon backtrack, and counting either upwards from 1 or downwards
+  // from -1, but store the absolute value in the serialization stream
+  boolean positiveSid;
+
+  int sidCount;
+
+  @Override
+  protected void initReferenceQueue() {
+    super.initReferenceQueue();
+
+    if (positiveSid){
+      positiveSid = false;
+      sidCount = -1;
+    } else {
+      positiveSid = true;
+      sidCount = 1;
+    }
+  }
+
+  // might be overriden in subclasses to conditionally queue objects
+  protected void queueReference(ElementInfo ei){
+    refQueue.add(ei);
+  }
+
+  @Override
+  public void processReference(int objref) {
+    if (objref == MJIEnv.NULL) {
+      buf.add(MJIEnv.NULL);
+
+    } else {
+      ElementInfo ei = heap.get(objref);
+      int sid = ei.getSid();
+
+      if (positiveSid){ // count sid upwards from 1
+        if (sid <= 0){  // not seen before in this serialization run
+          sid = sidCount++;
+          ei.setSid(sid);
+          queueReference(ei);
+        }
+      } else { // count sid downwards from -1
+        if (sid >= 0){ // not seen before in this serialization run
+          sid = sidCount--;
+          ei.setSid(sid);
+          queueReference(ei);
+        }
+        sid = -sid;
+      }
+
+      // note that we always add the absolute sid value
+      buf.add(sid);
+    }
+  }
+  
+  @Override
+  protected void serializeStackFrames() {
+    ThreadList tl = ks.getThreadList();
+
+    for (Iterator<ThreadInfo> it = tl.canonicalLiveIterator(); it.hasNext(); ) {
+      serializeStackFrames(it.next());
+    }
+  }
+  
+  @Override
+  protected void serializeFrame(StackFrame frame){
+    buf.add(frame.getMethodInfo().getGlobalId());
+
+    Instruction pc = frame.getPC();
+    buf.add( pc != null ? pc.getInstructionIndex() : -1);
+
+    int len = frame.getTopPos()+1;
+    buf.add(len);
+
+    // unfortunately we can't do this as a block operation because that
+    // would use concrete reference values as hash data, i.e. break heap symmetry
+    int[] slots = frame.getSlots();
+    for (int i = 0; i < len; i++) {
+      if (frame.isReferenceSlot(i)) {
+        processReference(slots[i]);
+      } else {
+        buf.add(slots[i]);
+      }
+    }
+  }
+
+  @Override
+  protected void processReferenceQueue() {
+    refQueue.process(this);
+  }
+  
+  @Override
+  protected int getSerializedReferenceValue (ElementInfo ei){
+    return Math.abs(ei.getSid());
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/DebugCFSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/DebugCFSerializer.java
new file mode 100644 (file)
index 0000000..3dc70cd
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import java.io.OutputStream;
+
+import gov.nasa.jpf.util.FinalBitSet;
+import gov.nasa.jpf.vm.DebugStateSerializer;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.JPFOutputStream;
+import gov.nasa.jpf.vm.NativeStateHolder;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * a CFSerializer that stores the serialized program state in a 
+ * readable/diffable format.
+ * 
+ * Automatically used by Debug..StateSet if the configured vm.storage.class is .vm.DebugJenkinsStateSet 
+ */
+public class DebugCFSerializer extends CFSerializer implements DebugStateSerializer {
+
+  JPFOutputStream os;
+  
+  // this is for debugging purposes only
+  public DebugCFSerializer() {
+    os = new JPFOutputStream(System.out);
+  }
+  
+  @Override
+  public void setOutputStream (OutputStream s){
+    os = new JPFOutputStream(s);
+  }
+  
+  @Override
+  protected int[] computeStoringData() {    
+    os.printCommentLine("------------------------ serialized state");
+    return super.computeStoringData();
+  }
+  
+  @Override
+  protected void processReferenceQueue(){
+    os.println();
+    os.printCommentLine("--- heap");
+    os.println();
+    super.processReferenceQueue();
+  }
+  
+  @Override
+  public void process (ElementInfo ei) {
+    super.process( ei);
+    
+    FinalBitSet filtered = !ei.isArray() ? getInstanceFilterMask(ei.getClassInfo()) : null;
+    os.print(ei, filtered);
+    os.println();
+  }
+  
+  @Override
+  protected void serializeClassLoaders(){
+    os.println();
+    os.printCommentLine("--- classes");
+    os.println();
+    super.serializeClassLoaders();
+  }
+  
+  @Override
+  protected void serializeClass (StaticElementInfo sei){
+    super.serializeClass(sei);
+    
+    FinalBitSet filtered = getStaticFilterMask(sei.getClassInfo());
+    os.print(sei, filtered);
+    os.println();    
+  }
+  
+  @Override
+  protected void serializeStackFrames(){
+    os.println();
+    os.printCommentLine("--- threads");
+    os.println();
+    super.serializeStackFrames();
+  }
+  
+  @Override
+  protected void serializeStackFrames(ThreadInfo ti){
+    os.println();
+    os.print(ti);
+    os.println();
+    super.serializeStackFrames(ti);
+  }
+  
+  @Override
+  protected void serializeFrame(StackFrame frame){
+    os.print(frame);
+    os.println();
+    super.serializeFrame(frame);
+  }
+  
+  @Override
+  protected void serializeNativeStateHolders(){
+    os.println();
+    os.printCommentLine("--- native state holders");
+    os.println();
+    super.serializeNativeStateHolders();
+  }
+  
+  @Override
+  protected void serializeNativeStateHolder (NativeStateHolder nsh){
+    os.print(nsh);
+    os.println();
+    super.serializeNativeStateHolder(nsh);
+  }
+  
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/DebugFilteringSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/DebugFilteringSerializer.java
new file mode 100644 (file)
index 0000000..cc4960f
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import java.io.OutputStream;
+
+import gov.nasa.jpf.vm.DebugStateSerializer;
+
+/**
+ * a FilteringSerializer that stores the serialized program state in a 
+ * readable/diffable format.
+ * 
+ * Automatically used by Debug..StateSet if the configured vm.serializer.class is FilteringSerializer
+ */
+public class DebugFilteringSerializer extends FilteringSerializer implements DebugStateSerializer {
+
+  @Override
+  public void setOutputStream(OutputStream os) {
+    // TODO Auto-generated method stub
+
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/DefaultFilterConfiguration.java b/src/main/gov/nasa/jpf/vm/serialize/DefaultFilterConfiguration.java
new file mode 100644 (file)
index 0000000..92453eb
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+
+
+public class DefaultFilterConfiguration extends AmmendableFilterConfiguration {
+  @Override
+  public void init(Config config) {
+    // these built-in come first
+    appendStaticAmmendment(IgnoreConstants.instance);
+    appendInstanceAmmendment(IgnoreReflectiveNames.instance);
+    appendFieldAmmendment(IgnoreThreadNastiness.instance);
+    appendFieldAmmendment(IgnoreUtilSilliness.instance);
+    
+    // ignores (e.g. NoMatch) from annotations
+    IgnoresFromAnnotations ignores = new IgnoresFromAnnotations(config); 
+    appendFieldAmmendment(ignores);
+    appendFrameAmmendment(ignores);
+    
+    // configured via properties
+    super.init(config);
+    
+    // includes (e.g. ForceMatch) from annotations
+    IncludesFromAnnotations includes = new IncludesFromAnnotations(config); 
+    appendFieldAmmendment(includes);
+    //appendFrameAmmendment(includes);
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/DynamicAbstractionSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/DynamicAbstractionSerializer.java
new file mode 100644 (file)
index 0000000..a554e78
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.util.FieldSpec;
+import gov.nasa.jpf.util.FinalBitSet;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.StringSetMatcher;
+import gov.nasa.jpf.vm.ArrayFields;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ReferenceArrayFields;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.Statics;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * a serializer that uses Abstraction objects stored as field attributes to
+ * obtain the values to hash. 
+ */
+public class DynamicAbstractionSerializer extends FilteringSerializer {
+
+  static JPFLogger logger = JPF.getLogger("gov.nasa.jpf.vm.serialize.DynamicAbstractionSerializer");
+
+  static class FieldAbstraction {
+    FieldSpec fspec;
+    Abstraction abstraction;
+
+    FieldAbstraction(FieldSpec f, Abstraction a) {
+      fspec = f;
+      abstraction = a;
+    }
+  }
+
+  public class Attributor extends ListenerAdapter {
+
+    @Override
+    public void classLoaded(VM vm, ClassInfo loadedClass) {
+
+      if (!loadedClass.isArray() && !loadedClass.isPrimitive()) {
+
+        if (!fieldAbstractions.isEmpty()) {
+          for (FieldInfo fi : loadedClass.getDeclaredInstanceFields()) {
+            for (FieldAbstraction fabs : fieldAbstractions) {
+              if (fabs.fspec.matches(fi)) {
+                logger.info("setting instance field abstraction ", fabs.abstraction.getClass().getName(),
+                        " for field ", fi.getFullName());
+                fi.addAttr(fabs.abstraction);
+              }
+            }
+          }
+
+          for (FieldInfo fi : loadedClass.getDeclaredStaticFields()) {
+            for (FieldAbstraction fabs : fieldAbstractions) {
+              if (fabs.fspec.matches(fi)) {
+                logger.info("setting static field abstraction ", fabs.abstraction.getClass().getName(),
+                        " for field ", fi.getFullName());
+                fi.addAttr(fabs.abstraction);
+              }
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  protected StringSetMatcher includeClasses = null; //  means all
+  protected StringSetMatcher excludeClasses = null; //  means none
+  protected StringSetMatcher includeMethods = null;
+  protected StringSetMatcher excludeMethods = null;
+
+  List<FieldAbstraction> fieldAbstractions;
+
+  protected boolean processAllObjects;
+  protected boolean declaredFieldsOnly;
+
+  
+  public DynamicAbstractionSerializer(Config conf) {
+    processAllObjects = conf.getBoolean("das.all_objects", true);
+    declaredFieldsOnly = conf.getBoolean("das.declared_fields", false);
+
+    includeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.include"));
+    excludeClasses = StringSetMatcher.getNonEmpty(conf.getStringArray("das.classes.exclude"));
+
+    includeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.include"));
+    excludeMethods = StringSetMatcher.getNonEmpty(conf.getStringArray("das.methods.exclude"));
+
+    fieldAbstractions = getFieldAbstractions(conf);
+  }
+
+  
+  protected List<FieldAbstraction> getFieldAbstractions(Config conf){
+    List<FieldAbstraction> list = null;
+    
+    String[] fids = conf.getCompactTrimmedStringArray("das.fields");
+    for (String id : fids) {
+      String keyPrefix = "das." + id;
+      String fs = conf.getString(keyPrefix + ".field");
+      if (fs != null) {
+        FieldSpec fspec = FieldSpec.createFieldSpec(fs);
+        if (fspec != null) {
+          String aKey = keyPrefix + ".abstraction";
+          Abstraction abstraction = conf.getInstance(aKey, Abstraction.class);
+
+          logger.info("found field abstraction for ", fspec, " = ", abstraction.getClass().getName());
+          
+          if (list == null){
+            list = new LinkedList<FieldAbstraction>();
+          }
+          
+          list.add(new FieldAbstraction(fspec, abstraction));
+        }
+      } else {
+        logger.warning("no field spec for id: " + id);
+      }
+    }
+    
+    return list;
+  }
+  
+  @Override
+  public void attach (VM vm){
+    super.attach(vm);
+    
+    if (fieldAbstractions != null){
+      Attributor attributor = new Attributor();
+      vm.addListener(attributor);
+    }
+  }
+  
+  
+  // note that we don't add the reference value here
+  @Override
+  public void processReference(int objref) {
+    if (objref != MJIEnv.NULL) {
+      ElementInfo ei = heap.get(objref);
+      if (!ei.isMarked()) { // only add objects once
+        ei.setMarked();
+        refQueue.add(ei);
+      }
+    }
+    
+    // we DON'T add the reference value to the buffer here
+  }
+
+  protected void processField(Fields fields, int[] slotValues, FieldInfo fi, FinalBitSet filtered) {
+    int off = fi.getStorageOffset();
+    if (!filtered.get(off)) {
+      Abstraction a = fi.getAttr(Abstraction.class);
+      if (a != null) {
+        if (fi.is1SlotField()) {
+          if (fi.isReference()) {
+            int ref = fields.getReferenceValue(off);
+            buf.add(a.getAbstractObject(ref));
+
+            if (a.traverseObject(ref)) {
+              processReference(ref);
+            }
+
+          } else if (fi.isFloatField()) {
+            buf.add(a.getAbstractValue(fields.getFloatValue(off)));
+          } else {
+            buf.add(a.getAbstractValue(fields.getIntValue(off)));
+          }
+        } else { // double or long
+          if (fi.isLongField()) {
+            buf.add(a.getAbstractValue(fields.getLongValue(off)));
+          } else { // got to be double
+            buf.add(a.getAbstractValue(fields.getDoubleValue(off)));
+          }
+        }
+
+      } else { // no abstraction, fall back to concrete values
+        if (fi.is1SlotField()) {
+          if (fi.isReference()) {
+            int ref = slotValues[off];
+            buf.add(ref);
+            processReference(ref);
+
+          } else {
+            buf.add(slotValues[off]);
+          }
+
+        } else { // double or long
+          buf.add(slotValues[off]);
+          buf.add(slotValues[off + 1]);
+        }
+      }
+    }
+  }
+
+  // non-ignored class
+  @Override
+  protected void processArrayFields(ArrayFields fields) {
+    buf.add(fields.arrayLength());
+
+    if (fields.isReferenceArray()) {
+      int[] values = fields.asReferenceArray();
+      for (int i = 0; i < values.length; i++) {
+        processReference(values[i]);
+        buf.add(values[i]);
+      }
+    } else {
+      fields.appendTo(buf);
+    }
+  }
+
+  // for ignored class, to get all live objects
+  protected void processNamedInstanceReferenceFields(ClassInfo ci, Fields fields) {
+    FinalBitSet filtered = getInstanceFilterMask(ci);
+    FinalBitSet refs = getInstanceRefMask(ci);
+    int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
+
+    for (int i = 0; i < slotValues.length; i++) {
+      if (!filtered.get(i)) {
+        if (refs.get(i)) {
+          processReference(slotValues[i]);
+        }
+      }
+    }
+  }
+
+  // for ignored class, to get all live objects
+  protected void processNamedStaticReferenceFields(ClassInfo ci, Fields fields) {
+    FinalBitSet filtered = getStaticFilterMask(ci);
+    FinalBitSet refs = getStaticRefMask(ci);
+    int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
+
+    for (int i = 0; i < slotValues.length; i++) {
+      if (!filtered.get(i)) {
+        if (refs.get(i)) {
+          processReference(slotValues[i]);
+        }
+      }
+    }
+  }
+
+  // for ignored class, to get all live objects
+  protected void processReferenceArray(ReferenceArrayFields fields) {
+    int[] slotValues = fields.asReferenceArray();
+    for (int i = 0; i < slotValues.length; i++) {
+      processReference(slotValues[i]);
+    }
+  }
+
+  // non-ignored class
+  @Override
+  protected void processNamedFields(ClassInfo ci, Fields fields) {
+    FinalBitSet filtered = getInstanceFilterMask(ci);
+    int nFields = ci.getNumberOfInstanceFields();
+    int[] slotValues = fields.asFieldSlots(); // for non-attributed fields
+
+    for (int i = 0; i < nFields; i++) {
+      FieldInfo fi = ci.getInstanceField(i);
+
+      if (declaredFieldsOnly && fi.getClassInfo() != ci) {
+        continue;
+      }
+
+      processField(fields, slotValues, fi, filtered);
+    }
+  }
+
+  // <2do> this should also allow abstraction of whole objects, so that
+  // we can hash combinations/relations of field values
+  @Override
+  public void process (ElementInfo ei) {
+    Fields fields = ei.getFields();
+    ClassInfo ci = ei.getClassInfo();
+
+    if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) {
+      buf.add(ci.getUniqueId());
+
+      if (fields instanceof ArrayFields) { // not filtered
+        processArrayFields((ArrayFields) fields);
+      } else { // named fields, potentially filtered & abstracted via attributes
+        processNamedFields(ci, fields);
+      }
+
+    } else { // ignored class
+      // we check for live non-ignored objects along all stack frames, so we should do the same for all objects
+      if (fields instanceof ArrayFields) {
+        if (fields instanceof ReferenceArrayFields) {
+          processReferenceArray((ReferenceArrayFields) fields);
+        }
+      } else {
+        processNamedInstanceReferenceFields(ci, fields);
+      }
+    }
+  }
+
+  @Override
+  protected void serializeFrame (StackFrame frame) {
+    MethodInfo mi = frame.getMethodInfo();
+    
+    if (StringSetMatcher.isMatch(mi.getFullName(), includeMethods, excludeMethods)){
+      // <2do> should do frame abstraction here
+      super.serializeFrame(frame);
+
+    } else {
+      if (processAllObjects) {
+        frame.visitReferenceSlots(this);
+      }
+    }
+  }
+
+  @Override
+  protected void serializeClass (StaticElementInfo sei) {
+    ClassInfo ci = sei.getClassInfo();
+    Fields fields = sei.getFields();
+
+    if (StringSetMatcher.isMatch(ci.getName(), includeClasses, excludeClasses)) {
+      buf.add(sei.getStatus());
+
+      FinalBitSet filtered = getStaticFilterMask(ci);
+      int[] slotValues = fields.asFieldSlots();
+
+      for (FieldInfo fi : ci.getDeclaredStaticFields()) {
+        processField(fields, slotValues, fi, filtered);
+      }
+
+    } else {
+      // ignored class, but still process references to extract live objects
+      processNamedStaticReferenceFields(ci, fields);
+    }
+  }
+
+  @Override
+  protected void serializeClassLoaders(){
+    // we don't care about the number of classloaders
+    
+    for (ClassLoaderInfo cl : ks.classLoaders) {
+      if(cl.isAlive()) {
+        serializeStatics( cl.getStatics());
+      }
+    }
+  }
+
+  @Override
+  protected void serializeStatics(Statics statics){
+    // we don't care about the number of statics entries
+
+    for (StaticElementInfo sei : statics.liveStatics()) {
+      serializeClass(sei);
+    }
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/FieldAmmendmentByName.java b/src/main/gov/nasa/jpf/vm/serialize/FieldAmmendmentByName.java
new file mode 100644 (file)
index 0000000..9bbf9ff
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FieldAmmendment;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+public class FieldAmmendmentByName implements FieldAmmendment {
+  protected final Set<String> fullFieldNames;
+  protected final boolean policy;
+  
+  public FieldAmmendmentByName(String[] fieldNames, boolean policy) {
+    this(Arrays.asList(fieldNames), policy);
+  }
+  
+  public FieldAmmendmentByName(Collection<String> fullFieldNames, boolean policy) {
+    this.fullFieldNames = new HashSet<String>(fullFieldNames);
+    this.policy = policy;
+  }
+
+  public FieldAmmendmentByName(Iterable<String> fullFieldNames, boolean policy) {
+    this.fullFieldNames = new HashSet<String>();
+    for (String name : fullFieldNames) {
+      this.fullFieldNames.add(name);
+    }
+    this.policy = policy;
+  }
+  
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    if (fullFieldNames.contains(fi.getFullName())) {
+      return policy;
+    } else {
+      return sofar;
+    }
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/FilterConfiguration.java b/src/main/gov/nasa/jpf/vm/serialize/FilterConfiguration.java
new file mode 100644 (file)
index 0000000..c6a7965
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+public interface FilterConfiguration {
+  void init(Config config);
+  
+  Iterable<FieldInfo> getMatchedInstanceFields(ClassInfo ci); 
+
+  Iterable<FieldInfo> getMatchedStaticFields(ClassInfo ci); 
+
+  FramePolicy getFramePolicy(MethodInfo mi);
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/FilterFrame.java b/src/main/gov/nasa/jpf/vm/serialize/FilterFrame.java
new file mode 100644 (file)
index 0000000..f0a4cef
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * USE CAREFULLY - Indicates that the stack frame of a method should not,
+ * in specified ways, be considered during state matching.
+ * 
+ * This can easily cause the search to be cut off even though the VM has made
+ * progress, so USE WISELY!
+ * 
+ * @author peterd
+ */
+@Target({ElementType.METHOD})
+public @interface FilterFrame {
+  /**
+   * True means locals (incl. parameters) and operand stack will be filtered.
+   */
+  boolean filterData() default true;
+
+  /**
+   * True means the location of the next instruction will be filtered.
+   */
+  boolean filterPC() default true;
+
+  /**
+   * True means frames below this one will not appear at all in the abstracted
+   * state.
+   */
+  boolean filterSubframes() default true;
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/serialize/FilteringSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/FilteringSerializer.java
new file mode 100644 (file)
index 0000000..2a90765
--- /dev/null
@@ -0,0 +1,484 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+
+import gov.nasa.jpf.util.ArrayObjectQueue;
+import gov.nasa.jpf.util.BitArray;
+import gov.nasa.jpf.util.FinalBitSet;
+import gov.nasa.jpf.util.IntVector;
+import gov.nasa.jpf.util.ObjVector;
+import gov.nasa.jpf.util.ObjectQueue;
+import gov.nasa.jpf.util.Processor;
+import gov.nasa.jpf.vm.AbstractSerializer;
+import gov.nasa.jpf.vm.ArrayFields;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativeStateHolder;
+import gov.nasa.jpf.vm.ReferenceProcessor;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.StaticElementInfo;
+import gov.nasa.jpf.vm.Statics;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.ThreadList;
+
+import java.util.HashMap;
+import java.util.List;
+
+
+/**
+ * serializer that can ignore marked fields and stackframes for state matching
+ *
+ * <2do> rework filter policies
+ */
+public class FilteringSerializer extends AbstractSerializer implements ReferenceProcessor, Processor<ElementInfo> {
+
+  // indexed by method globalId
+  final ObjVector<FramePolicy> methodCache = new ObjVector<FramePolicy>();
+
+  //--- search global bitmask caches
+  final HashMap<ClassInfo,FinalBitSet> instanceRefMasks = new HashMap<ClassInfo,FinalBitSet>();
+  final HashMap<ClassInfo,FinalBitSet> staticRefMasks   = new HashMap<ClassInfo,FinalBitSet>();
+
+  final HashMap<ClassInfo,FinalBitSet> instanceFilterMasks = new HashMap<ClassInfo,FinalBitSet>();
+  final HashMap<ClassInfo,FinalBitSet> staticFilterMasks   = new HashMap<ClassInfo,FinalBitSet>();
+
+  protected FilterConfiguration filter;
+
+  protected transient IntVector buf = new IntVector(4096);
+
+  // the reference queue for heap traversal
+  protected ObjectQueue<ElementInfo> refQueue;
+  
+  Heap heap;
+
+
+  @Override
+  public void attach(VM vm) {
+    super.attach(vm);
+    
+    filter = vm.getConfig().getInstance("filter.class", FilterConfiguration.class);
+    if (filter == null) {
+      filter = new DefaultFilterConfiguration();
+    }
+    filter.init(vm.getConfig());
+  }
+
+  protected FramePolicy getFramePolicy(MethodInfo mi) {
+    FramePolicy p = null;
+
+    int mid = mi.getGlobalId();
+    if (mid >= 0){
+      p = methodCache.get(mid);
+    if (p == null) {
+      p = filter.getFramePolicy(mi);
+      methodCache.set(mid, p);
+    }
+    } else {
+      p = filter.getFramePolicy(mi);
+    }
+
+    return p;
+  }
+
+  protected FinalBitSet getInstanceRefMask(ClassInfo ci) {
+    FinalBitSet v = instanceRefMasks.get(ci);
+    if (v == null) {
+      BitArray b = new BitArray(ci.getInstanceDataSize());
+      for (FieldInfo fi : filter.getMatchedInstanceFields(ci)) {
+        if (fi.isReference()) {
+          b.set(fi.getStorageOffset());
+        }
+      }
+      v = FinalBitSet.create(b);
+      if (v == null) throw new IllegalStateException("Null BitArray returned.");
+      instanceRefMasks.put(ci, v);
+    }
+    return v;
+  }
+
+  protected FinalBitSet getStaticRefMask(ClassInfo ci) {
+    FinalBitSet v = staticRefMasks.get(ci);
+    if (v == null) {
+      BitArray b = new BitArray(ci.getStaticDataSize());
+      for (FieldInfo fi : filter.getMatchedStaticFields(ci)) {
+        if (fi.isReference()) {
+          b.set(fi.getStorageOffset());
+        }
+      }
+      v = FinalBitSet.create(b);
+      if (v == null) throw new IllegalStateException("Null BitArray returned.");
+      staticRefMasks.put(ci, v);
+    }
+    return v;
+  }
+
+  protected FinalBitSet getInstanceFilterMask(ClassInfo ci) {
+    FinalBitSet v = instanceFilterMasks.get(ci);
+    if (v == null) {
+      BitArray b = new BitArray(ci.getInstanceDataSize());
+      b.setAll();
+      for (FieldInfo fi : filter.getMatchedInstanceFields(ci)) {
+        int start = fi.getStorageOffset();
+        int end = start + fi.getStorageSize();
+        for (int i = start; i < end; i++) {
+          b.clear(i);
+        }
+      }
+      v = FinalBitSet.create(b);
+      if (v == null) throw new IllegalStateException("Null BitArray returned.");
+      instanceFilterMasks.put(ci, v);
+    }
+    return v;
+  }
+
+  protected FinalBitSet getStaticFilterMask(ClassInfo ci) {
+    FinalBitSet v = staticFilterMasks.get(ci);
+    if (v == null) {
+      BitArray b = new BitArray(ci.getStaticDataSize());
+      b.setAll();
+      for (FieldInfo fi : filter.getMatchedStaticFields(ci)) {
+        int start = fi.getStorageOffset();
+        int end = start + fi.getStorageSize();
+        for (int i = start; i < end; i++) {
+          b.clear(i);
+        }
+      }
+      v = FinalBitSet.create(b);
+      if (v == null) throw new IllegalStateException("Null BitArray returned.");
+      staticFilterMasks.put(ci, v);
+    }
+    return v;
+  }
+
+  protected void initReferenceQueue() {
+    // note - this assumes all heap objects are in an unmarked state, but this
+    // is true if we enter outside the gc
+
+    if (refQueue == null){
+      refQueue = new ArrayObjectQueue<ElementInfo>();
+    } else {
+      refQueue.clear();
+    }
+  }
+
+
+  //--- those are the methods that can be overridden by subclasses to implement abstractions
+
+  // needs to be public because of ReferenceProcessor interface
+  @Override
+  public void processReference(int objref) {
+    if (objref != MJIEnv.NULL) {
+      ElementInfo ei = heap.get(objref);
+      if (!ei.isMarked()) { // only add objects once
+        ei.setMarked();
+        refQueue.add(ei);
+      }
+    }
+
+    buf.add(objref);
+  }
+
+  
+  protected void processArrayFields (ArrayFields afields){
+    buf.add(afields.arrayLength());
+
+    if (afields.isReferenceArray()) {
+      int[] values = afields.asReferenceArray();
+      for (int i = 0; i < values.length; i++) {
+        processReference(values[i]);
+      }
+    } else {
+      afields.appendTo(buf);
+    }
+  }
+    
+  protected void processNamedFields (ClassInfo ci, Fields fields){
+    FinalBitSet filtered = getInstanceFilterMask(ci);
+    FinalBitSet refs = getInstanceRefMask(ci);
+
+    // using a block operation probably doesn't buy us much here since
+    // we would have to blank the filtered slots and then visit the
+    // non-filtered reference slots, i.e. do two iterations over
+    // the mask bit sets
+    int[] values = fields.asFieldSlots();
+    for (int i = 0; i < values.length; i++) {
+      if (!filtered.get(i)) {
+        int v = values[i];
+        if (refs.get(i)) {
+          processReference(v);
+        } else {
+          buf.add(v);
+        }
+      }
+    }
+  }
+
+  // needs to be public because of ElementInfoProcessor interface
+  // NOTE: we don't serialize the monitor state here since this is
+  // redundant to the thread locking state (which we will do after the heap).
+  // <2do> we don't strictly need the lockCount since this has to show in the
+  // stack frames. However, we should probably add monitor serialization to
+  // better support specialized subclasses
+  @Override
+  public void process (ElementInfo ei) {
+    Fields fields = ei.getFields();
+    ClassInfo ci = ei.getClassInfo();
+    buf.add(ci.getUniqueId());
+
+    if (fields instanceof ArrayFields) { // not filtered
+      processArrayFields((ArrayFields)fields);
+
+    } else { // named fields, filtered
+      processNamedFields(ci, fields);
+    }
+  }
+  
+  protected void processReferenceQueue () {
+    refQueue.process(this);
+    
+    // this sucks, but we can't do the 'isMarkedOrLive' trick used in gc here
+    // because gc depends on live bit integrity, and we only mark non-filtered live
+    // objects here, i.e. we can't just set the Heap liveBitValue subsequently.
+    heap.unmarkAll();
+  }
+
+  protected void serializeStackFrames() {
+    ThreadList tl = ks.getThreadList();
+
+    for (ThreadInfo ti : tl) {
+      if (ti.isAlive()) {
+        serializeStackFrames(ti);
+      }
+    }
+  }
+
+  protected void serializeStackFrames(ThreadInfo ti){
+    // we need to add the thread object itself as a root
+    processReference( ti.getThreadObjectRef());
+    
+    for (StackFrame frame = ti.getTopFrame(); frame != null; frame = frame.getPrevious()){
+      serializeFrame(frame);
+    }
+  }
+
+  /** more generic, but less efficient because it can't use block operations
+  protected void _serializeFrame(StackFrame frame){
+    buf.add(frame.getMethodInfo().getGlobalId());
+    buf.add(frame.getPC().getInstructionIndex());
+
+    int len = frame.getTopPos()+1;
+    buf.add(len);
+
+    // this looks like something we can push into the frame
+    int[] slots = frame.getSlots();
+    for (int i = 0; i < len; i++) {
+      if (frame.isReferenceSlot(i)) {
+        processReference(slots[i]);
+      } else {
+        buf.add(slots[i]);
+      }
+    }
+  }
+  **/
+
+  protected void serializeFrame(StackFrame frame){
+    buf.add(frame.getMethodInfo().getGlobalId());
+
+    // there can be (rare) cases where a listener sets a null nextPc in
+    // a frame that is still on the stack
+    Instruction pc = frame.getPC();
+    if (pc != null){
+      buf.add(pc.getInstructionIndex());
+    } else {
+      buf.add(-1);
+    }
+
+    int len = frame.getTopPos()+1;
+    buf.add(len);
+
+    int[] slots = frame.getSlots();
+    buf.append(slots,0,len);
+
+    frame.visitReferenceSlots(this);
+  }
+
+  // this is called after the heap got serialized, i.e. we should not use
+  // processReference() anymore. 
+  protected void serializeThreadState (ThreadInfo ti){
+    
+    buf.add( ti.getId());
+    buf.add( ti.getState().ordinal());
+    buf.add( ti.getStackDepth());
+    
+    //--- the lock state
+    // NOTE: both lockRef and lockedObjects can only refer to live objects
+    // which are already heap-processed at this point (i.e. have a valid 'sid'
+    // in case we don't want to directly serialize the reference values)
+    
+    // the object we are waiting for 
+    ElementInfo eiLock = ti.getLockObject();
+    if (eiLock != null){
+      buf.add(getSerializedReferenceValue( eiLock));
+    }
+    
+    // the objects we hold locks for
+    // NOTE: this should be independent of lockedObjects order, hence we
+    // have to factor this out
+    serializeLockedObjects( ti.getLockedObjects());
+  }
+
+  // NOTE: this should not be called before all live references have been processed
+  protected int getSerializedReferenceValue (ElementInfo ei){
+    return ei.getObjectRef();
+  }
+  
+  protected void serializeLockedObjects(List<ElementInfo> lockedObjects){
+    // lockedObjects are already a set since we don't have multiple entries
+    // (that would just increase the lock count), but our serialization should
+    // NOT produce different values depending on order of entry. We could achieve this by using
+    // a canonical order (based on reference or sid values), but this would require
+    // System.arraycopys and object allocation, which is too much overhead
+    // given that the number of lockedObjects is small for all but the most
+    // pathological systems under test. 
+    // We could spend all day to compute the perfect order-independent hash function,
+    // but since our StateSet isn't guaranteed to be collision free anyway, we
+    // rather shoot for something that can be nicely JITed
+    int n = lockedObjects.size();
+    buf.add(n);
+    
+    if (n > 0){
+      if (n == 1){ // no order involved
+        buf.add( getSerializedReferenceValue( lockedObjects.get(0)));
+        
+      } else {
+        // don't burn an iterator on this, 'n' is supposed to be small
+        int h = (n << 16) + (n % 3);
+        for (int i=0; i<n; i++){
+          int rot = (getSerializedReferenceValue( lockedObjects.get(i))) % 31;
+          h ^= (h << rot) | (h >>> (32 - rot)); // rotate left
+        }        
+        buf.add( h);
+      }
+    }
+  }
+  
+  protected void serializeThreadStates (){
+    ThreadList tl = ks.getThreadList();
+
+    for (ThreadInfo ti : tl) {
+      if (ti.isAlive()) {
+        serializeThreadState(ti);
+      }
+    }    
+  }
+  
+  protected void serializeClassLoaders(){
+    buf.add(ks.classLoaders.size());
+
+    for (ClassLoaderInfo cl : ks.classLoaders) {
+      if(cl.isAlive()) {
+        serializeStatics(cl.getStatics());
+      }
+    }
+  }
+
+  protected void serializeStatics(Statics statics){
+    buf.add(statics.size());
+
+    for (StaticElementInfo sei : statics.liveStatics()) {
+      serializeClass(sei);
+    }
+  }
+
+  protected void serializeClass (StaticElementInfo sei){
+    buf.add(sei.getStatus());
+
+    Fields fields = sei.getFields();
+    ClassInfo ci = sei.getClassInfo();
+    FinalBitSet filtered = getStaticFilterMask(ci);
+    FinalBitSet refs = getStaticRefMask(ci);
+    int max = ci.getStaticDataSize();
+    for (int i = 0; i < max; i++) {
+      if (!filtered.get(i)) {
+        int v = fields.getIntValue(i);
+        if (refs.get(i)) {
+          processReference(v);
+        } else {
+          buf.add(v);
+        }
+      }
+    }
+  }
+  
+  protected void serializeNativeStateHolders(){
+    for (NativeStateHolder nsh : nativeStateHolders){
+      serializeNativeStateHolder(nsh);
+    }
+  }
+  
+  protected void serializeNativeStateHolder (NativeStateHolder nsh){
+    buf.add(nsh.getHash());
+  }
+  
+  //--- our main purpose in life
+
+  @Override
+  protected int[] computeStoringData() {
+
+    buf.clear();
+    heap = ks.getHeap();
+    initReferenceQueue();
+
+    //--- serialize all live objects and loaded classes
+    serializeStackFrames();
+    serializeClassLoaders();
+    processReferenceQueue();
+    
+    //--- now serialize the thread states (which might refer to live objects)
+    // we do this last because threads contain some internal references
+    // (locked objects etc) that should NOT set the canonical reference serialization
+    // values (if they are encountered before their first explicit heap reference)
+    serializeThreadStates();
+
+    //--- last is serialization of native state holders
+    serializeNativeStateHolders();
+    
+    return buf.toArray();
+  }
+
+  protected void dumpData() {
+    int n = buf.size();
+    System.out.print("serialized data: [");
+    for (int i=0; i<n; i++) {
+      if (i>0) {
+        System.out.print(',');
+      }
+      System.out.print(buf.get(i));
+    }
+    System.out.println(']');
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/serialize/FramePolicy.java b/src/main/gov/nasa/jpf/vm/serialize/FramePolicy.java
new file mode 100644 (file)
index 0000000..4bdb595
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+
+public final class FramePolicy {
+  public FramePolicy() {
+    includeOps = true;
+    includeLocals = true;
+    includePC = true;
+    recurse = true;
+  }
+  
+  //May be migrated to BitArray or similar in the future.
+  public boolean includeLocals;
+  
+  //May be migrated to BitArray or similar in the future.
+  public boolean includeOps;
+  
+  /**
+   * Whether to include instruction offset.
+   */
+  public boolean includePC;
+  
+  /**
+   * Whether to considered frames "below" this one (called from here).
+   */
+  public boolean recurse;
+  
+  
+  
+  
+  public boolean isDefaultPolicy() {
+    return includeLocals && includeOps && includePC && recurse;
+  }
+  
+  
+  public void ignoreLocals() {
+    includeLocals = false;
+  }
+
+  public void ignoreOps() {
+    includeOps = false;
+  }
+  
+  public void includeLocals() {
+    includeLocals = true;
+  }
+
+  public void includeOps() {
+    includeOps = true;
+  }
+}
\ No newline at end of file
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IgnoreConstants.java b/src/main/gov/nasa/jpf/vm/serialize/IgnoreConstants.java
new file mode 100644 (file)
index 0000000..2a07761
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.StaticAmmendment;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+ * Marks static final field of primitive or known immutable type to be
+ * filtered.  In theory, these could be critical to state, but that would
+ * be highly irregular.
+ * 
+ * NOTE - final does not really mean constant, so we only ignore fields
+ * here that are initialized from lexical constants, i.e. a constpool entry.
+ * Others might involve data choice generators
+ * 
+ * <br><br>
+ * Ignoring constants probably isn't beneficial with the FilteringSerializer
+ * but could be a big win with AbstractingSerializer, which garbage-collects
+ * no-longer-reachable objects--that is, garbage collection in its
+ * representation, not in VM.
+ *
+ * @author peterd
+ */
+public class IgnoreConstants implements StaticAmmendment {
+  static final HashSet<String> knownImmutables =
+    new HashSet<String>(Arrays.asList(new String[] {
+        "boolean", "byte", "char", "double", "float", "int", "long", "short",
+        "java.lang.String",
+        "java.lang.Boolean",
+        "java.lang.Byte",
+        "java.lang.Character",
+        "java.lang.Double",
+        "java.lang.Float",
+        "java.lang.Integer",
+        "java.lang.Long",
+        "java.lang.Short",
+    }));
+  
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    assert fi.isStatic();
+    if (fi.isFinal() && fi.getConstantValue() != null) {
+      if (knownImmutables.contains(fi.getType())) {
+        return POLICY_IGNORE; 
+      }
+    }
+    // otherwise, delegate
+    return sofar; 
+  }
+
+  // must be at bottom
+  public static final IgnoreConstants instance = new IgnoreConstants();
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IgnoreReflectiveNames.java b/src/main/gov/nasa/jpf/vm/serialize/IgnoreReflectiveNames.java
new file mode 100644 (file)
index 0000000..7e7effa
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+
+public class IgnoreReflectiveNames extends FieldAmmendmentByName {
+  /**
+   * These are not critical to state because the objects that contain them
+   * also have int values that can be used to look up the name within the
+   * same VM execution.
+   */
+  static final String[] reflectiveNameFields = {
+    "java.lang.Class.name",
+    "java.lang.reflect.Method.name",
+    "java.lang.reflect.Field.name"
+  };
+  
+  public IgnoreReflectiveNames() {
+    super(reflectiveNameFields, POLICY_IGNORE);
+  }
+
+  // must be at bottom!
+  public static final IgnoreReflectiveNames instance = new IgnoreReflectiveNames();
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IgnoreThreadNastiness.java b/src/main/gov/nasa/jpf/vm/serialize/IgnoreThreadNastiness.java
new file mode 100644 (file)
index 0000000..8a09416
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FieldAmmendment;
+
+
+public class IgnoreThreadNastiness implements FieldAmmendment {
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    String cname = fi.getClassInfo().getName();
+    String fname = fi.getName();
+    if (cname.equals("java.lang.Thread")) {
+      if (!fname.equals("target")) {
+        return POLICY_IGNORE;  // nothing but perhaps `target' should be critical
+        // (that includes static fields)
+      }
+    } else if (cname.equals("java.lang.ThreadGroup")) {
+      return POLICY_IGNORE;  // hopefully none of it is critical
+      // (that includes static fields; not that there are any)
+    }
+    return sofar;
+  }
+  
+  
+  public static final IgnoreThreadNastiness instance = new IgnoreThreadNastiness();
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IgnoreUtilSilliness.java b/src/main/gov/nasa/jpf/vm/serialize/IgnoreUtilSilliness.java
new file mode 100644 (file)
index 0000000..85c9b24
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FieldAmmendment;
+
+
+public class IgnoreUtilSilliness implements FieldAmmendment {
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    String cname = fi.getClassInfo().getName();
+    String fname = fi.getName();
+    if (cname.startsWith("java.util.")) {
+      if (fname.endsWith("odCount")) {
+        // catches all this `modCount' business
+        return POLICY_IGNORE;
+      }
+    }
+    return sofar;
+  }
+  
+  
+  public static final IgnoreUtilSilliness instance = new IgnoreUtilSilliness();
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/Ignored.java b/src/main/gov/nasa/jpf/vm/serialize/Ignored.java
new file mode 100644 (file)
index 0000000..7eec58d
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+/**
+ * tag attribute to ignore elements (classes, objects, fields) from
+ * state matching
+ */
+public class Ignored {
+  
+  public static final Ignored IGNORED = new Ignored();
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IgnoresFromAnnotations.java b/src/main/gov/nasa/jpf/vm/serialize/IgnoresFromAnnotations.java
new file mode 100644 (file)
index 0000000..5ab54f6
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.FilterField;
+import gov.nasa.jpf.annotation.FilterFrame;
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FieldAmmendment;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FrameAmmendment;
+
+public class IgnoresFromAnnotations
+implements FieldAmmendment, FrameAmmendment {
+  protected Config config;
+  
+  public IgnoresFromAnnotations(Config config) {
+    this.config = config;
+  }
+  
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    AnnotationInfo ann = fi.getAnnotation(FilterField.class.getName());
+    if (ann != null){
+      String condition = ann.getValueAsString("condition");
+      boolean invert = ann.getValueAsBoolean("invert");
+      if ((condition == null) || condition.isEmpty() || (config.getBoolean(condition)) == !invert ) {
+        return POLICY_IGNORE;
+      }
+    }
+    
+    return sofar;
+  }
+
+  @Override
+  public FramePolicy ammendFramePolicy(MethodInfo mi, FramePolicy sofar) {
+    AnnotationInfo ann = mi.getAnnotation(FilterFrame.class.getName());
+    if (ann != null) {
+      if (ann.getValueAsBoolean("filterData")) {
+        sofar.includeLocals = false;
+        sofar.includeOps = false;
+      }
+      if (ann.getValueAsBoolean("filterPC")) {
+        sofar.includePC = false;
+      }
+      if (ann.getValueAsBoolean("filterSubframes")) {
+        sofar.recurse = false;
+      }
+    }
+    return sofar;
+  }
+
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/IncludesFromAnnotations.java b/src/main/gov/nasa/jpf/vm/serialize/IncludesFromAnnotations.java
new file mode 100644 (file)
index 0000000..15183c1
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.serialize.AmmendableFilterConfiguration.FieldAmmendment;
+
+public class IncludesFromAnnotations
+implements FieldAmmendment {
+  protected Config config;
+  
+  public IncludesFromAnnotations(Config config)  {
+    this.config = config;
+  }
+  
+  @Override
+  public boolean ammendFieldInclusion(FieldInfo fi, boolean sofar) {
+    AnnotationInfo ann = fi.getAnnotation("gov.nasa.jpf.annotation.UnfilterField");
+    if (ann != null) {
+      return POLICY_INCLUDE;
+    }
+    return sofar;
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/TopFrameSerializer.java b/src/main/gov/nasa/jpf/vm/serialize/TopFrameSerializer.java
new file mode 100644 (file)
index 0000000..8b39370
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * even more aggressive under-approximation than AdaptiveSerializer. This one
+ * only looks at the top frame of each thread, and only serializes objects
+ * referenced from there. It ignores static fields and deeper heap objects that
+ * are not directly referenced.
+ *
+ * While this seems too aggressive, it actually finds a lot of concurrency
+ * defects in real world applications. This is esp. true if there are
+ * a lot of field access CGs, i.e. shared objects. In this case, the
+ * TopFrameSerializer can behave an order of magnitude better than CFSerializer
+ */
+public class TopFrameSerializer extends CFSerializer {
+
+  boolean traverseObjects;
+
+  @Override
+  protected void initReferenceQueue() {
+    super.initReferenceQueue();
+
+    traverseObjects = true;
+  }
+
+  @Override
+  protected void serializeStackFrames(ThreadInfo ti){
+    // we just look at the top frame
+    serializeFrame(ti.getTopFrame());
+  }
+
+
+  @Override
+  protected void queueReference(ElementInfo ei){
+    if (traverseObjects){
+      refQueue.add(ei);
+    }
+  }
+
+  @Override
+  protected void processReferenceQueue() {
+    // we only go one level deep
+    traverseObjects = false;
+    refQueue.process(this);
+  }
+
+  @Override
+  protected void serializeClassLoaders(){
+    // totally ignore statics
+  }
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/UnfilterField.java b/src/main/gov/nasa/jpf/vm/serialize/UnfilterField.java
new file mode 100644 (file)
index 0000000..dddbbde
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Target;
+
+/**
+ * Indicates that a field in the model SHOULD BE considered during
+ * state matching, even if other (prior) configuration would filter it.
+ * @author peterd
+ */
+@Target({ElementType.FIELD})
+public @interface UnfilterField {
+  /**
+   * If not the empty string, specifies a property that must be "true" to
+   * activate unfiltering--unless <code>invert</code> is set.
+   */
+  String condition() default "";
+  
+  /**
+   * If set to <code>true</code>, property must be "false" to activate
+   * unfiltering. Does nothing if <code>condition</code> is empty string.
+   */
+  boolean invert() default false;
+}
diff --git a/src/main/gov/nasa/jpf/vm/serialize/UnknownJPFClass.java b/src/main/gov/nasa/jpf/vm/serialize/UnknownJPFClass.java
new file mode 100644 (file)
index 0000000..96978f5
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.serialize;
+
+/**
+ * exception to indicate that JPF can't find a (model) class of a given name
+ * For native clients directly or indirectly cause class resolution
+ *
+ * <2do> we might turn this into an handled exception at some point
+ */
+public class UnknownJPFClass extends RuntimeException {
+  protected String clsName;
+
+  public UnknownJPFClass(String clsName){
+    this.clsName = clsName;
+  }
+
+  String getRequestedClassName() {
+    return clsName;
+  }
+
+  @Override
+  public String getMessage(){
+    return clsName;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/AtomicFieldUpdater.java b/src/peers/gov/nasa/jpf/vm/AtomicFieldUpdater.java
new file mode 100644 (file)
index 0000000..c83fd9e
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+/**
+ * base class for atomic field updaters
+ */
+public class AtomicFieldUpdater extends NativePeer {
+  
+  
+  protected FieldInfo getFieldInfo (ElementInfo eiUpdater, ElementInfo eiTarget){
+    int fidx = eiUpdater.getIntField("fieldId");
+    return eiTarget.getClassInfo().getInstanceField(fidx);
+  }
+  
+  /**
+   * note - we are not interested in sharedness/interleaving of the AtomicUpdater object 
+   * but in the object that is accessed by the updater
+   */
+  protected boolean reschedulesAccess (ThreadInfo ti, ElementInfo ei, FieldInfo fi){
+    Scheduler scheduler = ti.getScheduler();
+    Instruction insn = ti.getPC();
+    
+    if (scheduler.canHaveSharedObjectCG( ti, insn, ei, fi)){
+      ei = scheduler.updateObjectSharedness( ti, ei, fi);
+      return scheduler.setsSharedObjectCG( ti, insn, ei, fi);
+    }
+    
+    return false;
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_AnnotationProxyBase.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_AnnotationProxyBase.java
new file mode 100644 (file)
index 0000000..fdaaa85
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+/**
+ * native peer for Annotation Proxies
+ * (saves us some bytecode interpretation shoe leather)
+ */
+public class JPF_gov_nasa_jpf_AnnotationProxyBase extends NativePeer {
+
+  @MJI
+  public int annotationType____Ljava_lang_Class_2 (MJIEnv env, int objref) {
+    ClassInfo ciProxy = env.getClassInfo(objref);  // this would be the proxy
+    
+    // we could also pull it out from the interfaces, but we know the naming scheme
+    String proxyName = ciProxy.getName();
+    String annotation = proxyName.substring(0, proxyName.length() - 6); // "...$Proxy"
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(annotation);
+    
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objref){
+    StringBuffer sb = new StringBuffer();
+    
+    ClassInfo ci = env.getClassInfo(objref);
+    String cname = ci.getName();
+    int idx = cname.lastIndexOf('$');
+    
+    sb.append('@');
+    sb.append(cname.substring(0,idx));
+    
+    FieldInfo[] fields = ci.getDeclaredInstanceFields();
+    if (fields.length > 0){
+      sb.append('(');
+      for (int i=0; i<fields.length; i++){
+        String fn = fields[i].getName();
+        String ft = fields[i].getType();
+        
+        if (i>0){
+          sb.append(',');
+        }
+        sb.append(fn);
+        sb.append('=');
+        
+        if (ft.equals("int")){
+          sb.append(env.getIntField(objref,fn));
+
+        } else if (ft.equals("long")){
+          sb.append(env.getLongField(objref,fn));
+          
+        } else if (ft.equals("double")){
+          sb.append(env.getDoubleField(objref,fn));
+
+        } else if (ft.equals("boolean")){
+          sb.append(env.getBooleanField(objref,fn));
+          
+        } else if (ft.equals("java.lang.String")){
+          sb.append(env.getStringObject(env.getReferenceField(objref, fn)));
+          
+        } else if (ft.equals("java.lang.Class")){
+          int cref = env.getReferenceField(objref, fn);
+          if (cref != MJIEnv.NULL){
+            int nref = env.getReferenceField(cref, "name");
+            String cn = env.getStringObject(nref);
+          
+            sb.append("class ");
+            sb.append(cn);
+          } else {
+            sb.append("class ?");
+          }
+            
+        } else if (ft.endsWith("[]")){
+          int ar = env.getReferenceField(objref, fn);
+          int n = env.getArrayLength((ar));
+
+          sb.append('[');
+          
+          if (ft.equals("java.lang.String[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+              sb.append(env.getStringObject(env.getReferenceArrayElement(ar,j)));
+            }
+            
+          } else if (ft.equals("int[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+              sb.append(env.getIntArrayElement(ar,j));
+            }
+
+          } else if (ft.equals("long[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+              sb.append(env.getLongArrayElement(ar,j));
+            }
+            
+          } else if (ft.equals("double[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+              sb.append(env.getDoubleArrayElement(ar,j));
+            }
+            
+          } else if (ft.equals("boolean[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+              sb.append(env.getBooleanArrayElement(ar,j));
+            }
+          } else if (ft.equals("java.lang.Class[]")){
+            for (int j=0; j<n; j++){
+              if (j>0) sb.append(',');
+
+              int cref = env.getReferenceArrayElement(ar,j);
+              if (cref != MJIEnv.NULL){
+                int nref = env.getReferenceField(cref, "name");
+                String cn = env.getStringObject(nref);
+              
+                sb.append("class ");
+                sb.append(cn);
+              } else {
+                sb.append("class ?");
+              }
+
+            }            
+          }
+          
+          sb.append(']');
+          
+        } else { // arbitrary type name, must be a reference
+          int eref = env.getReferenceField(objref, fn);
+          if (eref != MJIEnv.NULL){
+            ClassInfo eci = env.getClassInfo(eref);
+            if (eci.isEnum()){
+              int nref = env.getReferenceField(eref, "name");
+              String en = env.getStringObject(nref);
+              
+              sb.append(eci.getName());
+              sb.append('.');
+              sb.append(en);
+            }
+          }
+        }
+      }
+      sb.append(')');
+    }
+    
+    
+    return env.newString(sb.toString());
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_CachedROHttpConnection.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_CachedROHttpConnection.java
new file mode 100644 (file)
index 0000000..d42e682
--- /dev/null
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashMap;
+
+/**
+ * very simple URLConnection model that can be used for reading static URL contents
+ *
+ * The data can be stored in/read from the file system, and is cached
+ * to avoid DOS by means of model checking
+ */
+public class JPF_gov_nasa_jpf_CachedROHttpConnection extends NativePeer {
+
+  static JPFLogger logger = JPF.getLogger("http");
+
+  File cacheDir;
+  HashMap<String,byte[]> dataCache;
+  
+  public JPF_gov_nasa_jpf_CachedROHttpConnection (Config conf){
+    String cacheDirPath = conf.getString("http.cache_dir");
+    if (cacheDirPath != null){
+      cacheDir = new File(cacheDirPath);
+
+      if (!cacheDir.exists()){
+        cacheDir.mkdir();
+      }
+      if (!cacheDir.isDirectory()){
+        throw new JPFConfigException("illegal http.cache_dir entry: " + cacheDirPath);
+      }
+    }
+
+    dataCache = new HashMap<String,byte[]>();
+  }
+
+  private static String getCacheFileName( String url){
+    String fn = url.replace('/', '^');
+    fn = fn.replace(':', '%');
+
+    return fn;
+  }
+
+  private byte[] getDataFromCachedFile (String url){
+    byte[] data = null;
+    String cacheFileName = getCacheFileName(url);
+    File cacheFile = new File(cacheDir, cacheFileName);
+    if (cacheFile.isFile()) {
+      try {
+        data = FileUtils.getContents(cacheFile);
+      } catch (IOException iox) {
+        logger.warning("can't read http data from cached file ", cacheFile.getPath());
+      }
+
+      if (data != null) {
+        logger.info("reading contents of ", url, " from file ", cacheFile.getPath());
+        dataCache.put(url, data);
+      }
+    }
+
+    return data;
+  }
+
+  private byte[] getDataFromURL (String surl){
+    byte[] data = null;
+
+    try {
+      URL url = new URL(surl);
+      InputStream is = url.openStream();
+
+      if (is != null) {
+        ByteArrayOutputStream os = new ByteArrayOutputStream(is.available());
+        byte[] buf = new byte[1024];
+
+        for (int n = is.read(buf); n >= 0; n = is.read(buf)) {
+          os.write(buf, 0, n);
+        }
+        is.close();
+
+        data = os.toByteArray();
+        dataCache.put(surl, data);
+
+        logger.info("reading contents of ", surl, " from server");
+
+        if (cacheDir != null) {
+          String cacheFileName = getCacheFileName(surl);
+          File cacheFile = new File(cacheDir, cacheFileName);
+          try {
+            FileUtils.setContents(cacheFile, data);
+            logger.info("storing contents of ", surl, " to file ", cacheFile.getPath());
+          } catch (IOException iox) {
+            logger.warning("can't store to cache directory ", cacheFile.getPath());
+          }
+        }
+
+        return data;
+      }
+    } catch (MalformedURLException mux){
+      logger.warning("mallformed URL ", surl);
+    } catch (IOException ex) {
+      logger.warning("reading URL data ", surl, " failed with ", ex.getMessage());
+    }
+
+    return data;
+  }
+
+  @MJI
+  public int getContents__Ljava_lang_String_2___3B (MJIEnv env, int objRef, int surlRef){
+    String url = env.getStringObject(surlRef);
+
+    // first we check if it's already cached in memory
+    byte[] data = dataCache.get(url);
+
+    if (data != null){
+      logger.info("using cached contents of ", url);
+
+    } else {
+      // see if we can get it from the cacheDir
+      if (cacheDir != null){
+        data = getDataFromCachedFile(url);
+      }
+
+      // if that didn't produce anything, we have to reach out to the net
+      if (data == null){
+        data = getDataFromURL( url);
+      }
+    }
+
+    if (data != null){
+      return env.newByteArray(data);
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_ConsoleOutputStream.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_ConsoleOutputStream.java
new file mode 100644 (file)
index 0000000..a814f23
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class to intercept all System.out and System.err
+ * printing. We handle all of this native, since it's already slow enough
+ */
+public class JPF_gov_nasa_jpf_ConsoleOutputStream extends NativePeer {
+  
+  /****************************************************************************
+   * these are the native methods we intercept
+   */
+
+  @MJI
+  public void $init____V (MJIEnv env, int objref) {
+    // that's just a dummy because we have no OutputStream, which would cause
+    // an exception in the PrintStream ctor
+  }
+  
+  @MJI
+  public void print__C__V (MJIEnv env, int objref, char c) {
+    env.getVM().print(c);
+  }
+
+  @MJI
+  public void print__D__V (MJIEnv env, int objref, double d) {
+    env.getVM().print(d);
+  }
+
+  @MJI
+  public void print__F__V (MJIEnv env, int objref, float f) {
+    env.getVM().print(f);
+  }
+
+  @MJI
+  public void print__I__V (MJIEnv env, int objref, int i) {
+    env.getVM().print(i);
+  }
+
+  @MJI
+  public void print__J__V (MJIEnv env, int objref, long j) {
+    env.getVM().print(j);
+  }
+
+  @MJI
+  public void print__Ljava_lang_String_2__V (MJIEnv env, int objRef,
+                                                 int strRef) {
+    env.getVM().print(env.getStringObject(strRef));
+  }
+
+  @MJI
+  public void print__Z__V (MJIEnv env, int objref, boolean z) {
+    env.getVM().print(z);
+  }
+
+  @MJI
+  public void println____V (MJIEnv env, int objRef) {
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__C__V (MJIEnv env, int objref, char c) {
+    env.getVM().print(c);
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__D__V (MJIEnv env, int objref, double d) {
+    env.getVM().print(d);
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__F__V (MJIEnv env, int objref, float f) {
+    env.getVM().print(f);
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__I__V (MJIEnv env, int objref, int i) {
+    env.getVM().print(i);
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__J__V (MJIEnv env, int objref, long j) {
+    env.getVM().print(j);
+    env.getVM().println();
+  }
+
+  @MJI
+  public void println__Ljava_lang_String_2__V (MJIEnv env, int objRef,
+                                                   int strRef) {
+    env.getVM().println(env.getStringObject(strRef));
+  }
+
+  @MJI
+  public void write__I__V (MJIEnv env, int objRef, int b){
+    env.getVM().print((char)(byte)b);
+  }
+  
+  @MJI
+  public void write___3BII__V (MJIEnv env, int objRef,
+                                      int bufRef, int off, int len){
+    
+  }
+
+  @MJI
+  public void println__Z__V (MJIEnv env, int objref, boolean z) {
+    env.getVM().print(z);
+    env.getVM().println();
+  }
+
+  @MJI
+  public int printf__Ljava_lang_String_2_3Ljava_lang_Object_2__Ljava_io_PrintStream_2
+                   (MJIEnv env, int objref, int fmtRef, int argRef) {
+    env.getVM().print(env.format(fmtRef,argRef));
+    return objref;
+  }
+  
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_DelegatingTimeZone.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_DelegatingTimeZone.java
new file mode 100644 (file)
index 0000000..168b8c4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * native peer for JPFs concrete TimeZone class, which is just delegating to the
+ * host VM so that we are independent of Java versions 
+ */
+public class JPF_gov_nasa_jpf_DelegatingTimeZone extends NativePeer {
+
+  //--- internals
+  TimeZone tz; // only used within (atomic) peer methods
+  
+  private TimeZone getTimeZone (MJIEnv env, int objRef){
+    int rawOffset = env.getIntField(objRef, "rawOffset");
+    tz.setRawOffset(rawOffset);
+    return tz;
+  }
+
+  //--- native methods
+  @MJI
+  public void setID__Ljava_lang_String_2__V (MJIEnv env, int objRef, int idRef){
+    String id = env.getStringObject(idRef);
+    TimeZone tz = TimeZone.getTimeZone(id);
+    int rawOffset = tz.getRawOffset();
+    
+    env.setReferenceField(objRef, "ID", idRef);
+    env.setIntField(objRef, "rawOffset", rawOffset);
+  }
+  
+  @MJI
+  public int getOffset__IIIIII__I (MJIEnv env, int objRef,
+      int era, int year, int month, int day, int dayOfWeek, int milliseconds){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.getOffset(era, year, month, day, dayOfWeek, milliseconds);
+  }
+
+  @MJI
+  public boolean inDaylightTime__J__Z (MJIEnv env, int objRef, long time){
+    Date date = new Date(time);
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.inDaylightTime(date);
+  }
+  
+  @MJI
+  public boolean useDaylightTime____Z (MJIEnv env, int objRef){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.useDaylightTime();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_EventProducer.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_EventProducer.java
new file mode 100644 (file)
index 0000000..bddc982
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.event.CheckEvent;
+import gov.nasa.jpf.util.event.EventContext;
+import gov.nasa.jpf.util.event.Event;
+import gov.nasa.jpf.util.event.EventChoiceGenerator;
+import gov.nasa.jpf.util.event.EventTree;
+import gov.nasa.jpf.util.event.NoEvent;
+
+/**
+ * native peer for EventProducer
+ */
+public class JPF_gov_nasa_jpf_EventProducer extends NativePeer {
+
+  static final String CG_NAME = "processNextEvent";
+  
+  public static JPFLogger log = JPF.getLogger("event");
+  
+  protected EventTree eventTree;
+  protected Event event;
+  
+  protected EventContext ctx;  // optional
+  
+  public JPF_gov_nasa_jpf_EventProducer (Config config){
+    eventTree = config.getEssentialInstance(EventTree.CONFIG_KEY, EventTree.class);
+    ctx = config.getInstance("event.context.class", EventContext.class);
+    
+    logger.info("event tree generated by: ", eventTree.getClass().getName());
+    if (ctx != null){
+      logger.info("using context event: ", ctx.getClass().getName());
+    }
+  }
+  
+  //--- those can be used by subclasses to add additional processing steps during processNextEvent
+  
+  /**
+   * override this if the event processing results in direct calls, i.e. there is no change in the model method calling
+   * processNextEvent
+   */
+  protected boolean hasReturnedFormDirectCall (MJIEnv env,  int objRef){
+    return false;
+  }
+  
+  /**
+   * nothing here, to be overridden in case processing has to happen in the native peer
+   * this is only called from within generateNextEvent()
+   */
+  protected void processEvent (MJIEnv env, int objRef){
+  }
+  
+  /**
+   * evaluate a pseudo event that checks properties 
+   */
+  protected boolean checkEvent (MJIEnv env, int objRef){
+    return ((CheckEvent)event).check(env, objRef);
+  }
+  
+  /**
+   * nothing here, to be overridden by subclasses that have to force states, modify the CG on the fly etc.
+   */
+  protected EventChoiceGenerator processNextCG (MJIEnv env, int objRef, EventChoiceGenerator cg){
+    return cg;
+  }
+  
+  /**
+   * this is our main purpose in life - processing events
+   * 
+   * @return true if there was another event, false if there isn't any event left on this path
+   */
+  @MJI
+  public boolean processNextEvent____Z (MJIEnv env, int objRef){
+    ThreadInfo ti = env.getThreadInfo();
+    
+    if (hasReturnedFormDirectCall( env, objRef)){
+      return true;
+    }
+    
+    SystemState ss = env.getSystemState();
+    EventChoiceGenerator cg;
+
+    event = null;
+    
+    if (!ti.isFirstStepInsn()){      
+      EventChoiceGenerator cgPrev = ss.getLastChoiceGeneratorOfType(EventChoiceGenerator.class);
+      if (cgPrev != null){
+        cg = cgPrev.getSuccessor(CG_NAME, ctx);        
+      } else {
+        cg = new EventChoiceGenerator( CG_NAME, eventTree.getRoot(), ctx);
+      }
+      
+      if ((cg = processNextCG(env, objRef, cg)) != null){
+        if (log.isInfoLogged()){
+          log.info("next event generator: ", cg.toString());
+        }
+        ss.setNextChoiceGenerator(cg);
+        env.repeatInvocation();
+        return true; // does not matter
+        
+      } else {
+        log.info("no more events");        
+        return false;
+      }
+      
+    } else { // re-execution
+      cg = ss.getCurrentChoiceGenerator(CG_NAME, EventChoiceGenerator.class);
+      event = cg.getNextChoice();
+      
+      if (event != null) {
+        if (!(event instanceof NoEvent)) {
+          if (event instanceof CheckEvent) {
+            CheckEvent ce = (CheckEvent) event;
+            if (log.isInfoLogged()) {
+              log.info("checking: ", ce.getExpression());
+            }
+            if (!checkEvent(env, objRef)) {
+              env.throwAssertion("checking " + ce.getExpression() + " failed");
+            }
+
+          } else {
+            if (log.isInfoLogged()) {
+              log.info("processing event: ", event.toString());
+            }
+            processEvent(env, objRef);
+          }
+        }
+
+        return true;
+        
+      } else {
+        return false;
+      }
+    }
+  }
+  
+  @MJI(noOrphanWarning=true)
+  public int getEventName____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    if (event != null && !(event instanceof NoEvent)){
+      return env.newString( event.getName());
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+
+  //--- for testing and debugging purposes (requires special EventTree implementations e.g. derived from TestEventTree)
+  // <2do> should be moved to subclass
+  
+  @MJI(noOrphanWarning=true)
+  public boolean checkPath____Z (MJIEnv env, int objRef){
+    SystemState ss = env.getSystemState();
+    EventChoiceGenerator cg = ss.getLastChoiceGeneratorOfType(EventChoiceGenerator.class);
+
+    if (cg != null){
+      Event lastEvent = cg.getNextChoice();      
+      if (eventTree.checkPath(lastEvent)){
+        return true;
+      } else {
+        log.warning("trace check for event ", lastEvent.toString(), " failed");
+        return false;
+      }
+      
+    } else {
+      return false; // there should have been one
+    }
+  }
+  
+  @MJI(noOrphanWarning=true)
+  public boolean isCompletelyCovered____Z (MJIEnv env, int objRef){
+    return eventTree.isCompletelyCovered();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_FinalizerThread.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_FinalizerThread.java
new file mode 100644 (file)
index 0000000..9f479f3
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.Predicate;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * A native peer for FinalizerThread. This is also an interface between the FinalizerThread
+ * object at the SUT level and it corresponding FinalizerThreadInfo object at the JPF
+ * level. 
+ */
+public class JPF_gov_nasa_jpf_FinalizerThread extends NativePeer {
+  
+  @MJI
+  public void runFinalizer__Ljava_lang_Object_2__V (MJIEnv env, int tiRef, int objRef) {
+    int queueRef = env.getReferenceField(tiRef, "finalizeQueue");
+    int[] elements = env.getReferenceArrayObject(queueRef);
+
+    if(elements.length>0 && elements[0]==objRef) {
+      ThreadInfo finalizerTi = env.getThreadInfo();
+      ClassInfo objCi = env.getElementInfo(objRef).getClassInfo();
+      MethodInfo mi = objCi.getMethod("finalize()V", false);
+    
+      // create and push a stack frame for finalize()V
+      DirectCallStackFrame frame = mi.createDirectCallStackFrame(finalizerTi, 0);
+      frame.setReferenceArgument(0, objRef, frame);
+      finalizerTi.pushFrame(frame);
+    
+      removeElement(env, tiRef, objRef);
+    }
+  }
+  
+  // removes the very first element in the list, which is the last finalizable objects processed
+  void removeElement(MJIEnv env, int tiRef, int objRef) {
+    int queueRef = env.getReferenceField(tiRef, "finalizeQueue");
+    ThreadInfo ti = env.getThreadInfo();
+    int[] oldValues = env.getReferenceArrayObject(queueRef);
+    
+    assert (objRef == oldValues[0]);
+    
+    assert (oldValues.length>0);
+    
+    int len = oldValues.length - 1;
+    ElementInfo newQueue = env.getHeap().newArray("Ljava/lang/Object;", len, ti);
+    int[] newValues = newQueue.asReferenceArray();
+    
+    System.arraycopy(oldValues, 1, newValues, 0, len);
+    env.getModifiableElementInfo(tiRef).setReferenceField("finalizeQueue", newQueue.getObjectRef());
+  }
+  
+  // a predicate to obtain all alive, non-finalizer threads within the ti process 
+  Predicate<ThreadInfo> getAppAliveUserPredicate (final ThreadInfo ti){
+    return new Predicate<ThreadInfo>(){
+      @Override
+       public boolean isTrue (ThreadInfo t){
+        return (t.isAlive() && !t.isSystemThread() && t.appCtx == ti.appCtx);
+      }
+    };
+  }
+  
+  @MJI
+  public void manageState____V (MJIEnv env, int objref){
+    ApplicationContext appCtx = env.getVM().getApplicationContext(objref);
+    FinalizerThreadInfo tiFinalizer = appCtx.getFinalizerThread();
+    VM vm = env.getVM();
+    
+    // check for termination - Note that the finalizer thread has to be the last alive thread
+    // of the process
+    if(!vm.getThreadList().hasAnyMatching(getAppAliveUserPredicate(tiFinalizer))) {
+      shutdown(env, objref);
+    }
+    // make the thread wait until more objects are added to finalizerQueue
+    else {
+      tiFinalizer.waitOnSemaphore();
+      
+      assert tiFinalizer.isWaiting();
+      
+      // this one has to consult the syncPolicy since there might be scheduling choices
+      if (!tiFinalizer.getScheduler().setsPostFinalizeCG(tiFinalizer)){
+        throw new JPFException("no transition break after finalization");
+      }
+    }
+  }
+  
+  protected void shutdown(MJIEnv env, int objRef) {
+    env.getModifiableElementInfo(objRef).setBooleanField("done", true);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_SerializationConstructor.java
new file mode 100644 (file)
index 0000000..0c41650
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+
+public class JPF_gov_nasa_jpf_SerializationConstructor extends NativePeer {
+
+  /**
+   * create a new instance, but only call the ctor of the first
+   * non-serializable superclass
+   */
+  @MJI
+  public int newInstance___3Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int mthRef, int argsRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+    
+    int superCtorRef = env.getReferenceField(mthRef, "firstNonSerializableCtor"); 
+    MethodInfo miCtor = JPF_java_lang_reflect_Constructor.getMethodInfo(env,superCtorRef);
+
+    if (frame == null){ // first time
+      int clsRef = env.getReferenceField(mthRef, "mdc");
+      ClassInfo ci = env.getReferredClassInfo( clsRef);
+
+      if (ci.isAbstract()){
+        env.throwException("java.lang.InstantiationException");
+        return MJIEnv.NULL;
+      }
+
+      int objRef = env.newObjectOfUncheckedClass(ci);
+      frame = miCtor.createDirectCallStackFrame(ti, 1); 
+      frame.setReferenceArgument( 0, objRef, null);
+      frame.setLocalReferenceVariable(0, objRef); // (1) we store the reference as a local var for retrieval during reexec      
+      ti.pushFrame(frame);
+      
+      // check for & push required clinits
+      ci.initializeClass(ti);
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+      
+    } else { // re-execution, 
+      int objRef = frame.getLocalVariable(0); // that's the object ref we stored in (1)
+      return objRef;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_test_MemoryGoal.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_test_MemoryGoal.java
new file mode 100644 (file)
index 0000000..e6c03e9
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.jvm.bytecode.JVMReturnInstruction;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for MemoryGoal tests
+ */
+public class JPF_gov_nasa_jpf_test_MemoryGoal extends NativePeer {
+
+  Listener listener;
+  
+  // <2do> that's too simple, because we should only measure what is
+  // allocated from the invoked method, not the MethodTester. Needs a listener
+  
+  static class Listener extends ListenerAdapter {
+    
+    MethodInfo mi;
+    boolean active;
+    
+    long nAllocBytes;
+    long nFreeBytes;
+    long nAlloc;
+    long nFree;
+    
+    Listener (MethodInfo mi){
+      this.mi = mi;
+    }
+    
+    @Override
+    public void objectCreated (VM vm, ThreadInfo ti, ElementInfo ei){
+      if (active){        
+        nAlloc++;
+        nAllocBytes += ei.getHeapSize(); // just an approximation
+      }
+    }
+    
+    @Override
+    public void objectReleased (VM vm, ThreadInfo ti, ElementInfo ei){
+      if (active){
+        nFree++;
+        nFreeBytes += ei.getHeapSize(); // just an approximation
+      }      
+    }
+
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      if (!active) {
+        if (executedInsn.getMethodInfo() == mi){
+          active = true;
+        }
+      } else {
+        if ((executedInsn instanceof JVMReturnInstruction) && (executedInsn.getMethodInfo() == mi)){
+          active = false;
+        }
+      }
+    }
+    
+    long totalAllocBytes() {
+      return nAllocBytes - nFreeBytes;
+    }
+  }
+  
+  @MJI
+  public boolean preCheck__Lgov_nasa_jpf_test_TestContext_2Ljava_lang_reflect_Method_2__Z
+                      (MJIEnv env, int objRef, int testContextRef, int methodRef){
+    MethodInfo mi = JPF_java_lang_reflect_Method.getMethodInfo(env, methodRef);
+    
+    listener = new Listener(mi);
+    env.addListener(listener);
+    return true;
+  }
+  
+  // what a terrible name!
+  @MJI
+  public boolean postCheck__Lgov_nasa_jpf_test_TestContext_2Ljava_lang_reflect_Method_2Ljava_lang_Object_2Ljava_lang_Throwable_2__Z 
+           (MJIEnv env, int objRef, int testContextRef, int methdRef, int resultRef, int exRef){
+
+    long nMax = env.getLongField(objRef, "maxGrowth");
+
+    Listener l = listener;
+    env.removeListener(l);
+    listener = null;
+    
+    return (l.totalAllocBytes() <= nMax);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_tools_MethodTester.java b/src/peers/gov/nasa/jpf/vm/JPF_gov_nasa_jpf_tools_MethodTester.java
new file mode 100644 (file)
index 0000000..cb1d1c0
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for the MethodTester tool
+ */
+public class JPF_gov_nasa_jpf_tools_MethodTester extends NativePeer {
+
+  @MJI
+  public void log__Ljava_lang_String_2__V (MJIEnv env, int objRef, int msgRef){
+    String msg = env.getStringObject(msgRef);
+    System.out.println("@ " + msg);
+  }
+  
+  @MJI
+  public void error__Ljava_lang_String_2__V (MJIEnv env, int objRef, int msgRef){
+    String msg = env.getStringObject(msgRef);
+    System.err.println(msg);    
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_File.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_File.java
new file mode 100644 (file)
index 0000000..a20a9e9
--- /dev/null
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URL;
+
+/**
+ * intercept and forward some of the filesystem access methods. This is very
+ * slow, if a program uses this heavily we should keep the forwarding File
+ * object around and modify the model class accordingly
+ */
+public class JPF_java_io_File extends NativePeer {
+
+  static File getFile(MJIEnv env, int objref) {
+    int fnref = env.getReferenceField(objref, "filename");
+    String fname = env.getStringObject(fnref);
+    return new File(fname);
+  }
+
+  static int createJPFFile(MJIEnv env, File file) {
+    int newFileRef = env.newObject("java.io.File");
+    ElementInfo fileEI = env.getModifiableElementInfo(newFileRef);
+
+    int fileNameRef = env.newString(file.getPath());
+    fileEI.setReferenceField("filename", fileNameRef);
+
+    return newFileRef;
+  }
+
+  @MJI
+  public int getParentFile____Ljava_io_File_2(MJIEnv env, int objref) {
+    File thisFile = getFile(env, objref);
+    File parent = thisFile.getParentFile();
+
+    return createJPFFile(env, parent);
+  }
+  
+  @MJI
+  public int getAbsolutePath____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    String pn = getFile(env,objref).getAbsolutePath();
+    return env.newString(pn);
+  }
+
+  @MJI
+  public int getAbsoluteFile____Ljava_io_File_2 (MJIEnv env, int objref) {
+    File absoluteFile = getFile(env, objref).getAbsoluteFile();
+    return createJPFFile(env, absoluteFile);
+  }
+
+  @MJI
+  public int getCanonicalPath____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    try {
+      String pn = getFile(env,objref).getCanonicalPath();
+      return env.newString(pn);
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public int getCanonicalFile____Ljava_io_File_2(MJIEnv env, int objref) {
+    try {
+      File file = getFile(env, objref);
+      File canonicalFile = file.getCanonicalFile();
+      return createJPFFile(env, canonicalFile);
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+  
+  // internal helper
+  @SuppressWarnings("deprecation")
+  @MJI
+  public int getURLSpec____Ljava_lang_String_2 (MJIEnv env, int objref){
+    try {
+      File f = getFile(env,objref);
+      URL url = f.toURL();
+      return env.newString(url.toString());
+    } catch (MalformedURLException mfux) {
+      env.throwException("java.net.MalformedURLException", mfux.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public int getURISpec____Ljava_lang_String_2 (MJIEnv env, int objref){
+    File f = getFile(env, objref);
+    URI uri = f.toURI();
+    return env.newString(uri.toString());
+  }
+
+  @MJI
+  public boolean isAbsolute____Z (MJIEnv env, int objref) {
+    return getFile(env, objref).isAbsolute();
+  }
+
+  @MJI
+  public boolean isDirectory____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).isDirectory();
+  }
+
+  @MJI
+  public boolean isFile____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).isFile();
+  }
+  
+  @MJI
+  public boolean delete____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).delete();
+  }
+  
+  @MJI
+  public long length____J (MJIEnv env, int objref) {
+    return getFile(env,objref).length();
+  }
+  
+  @MJI
+  public boolean canRead____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).canRead();
+  }
+
+  @MJI
+  public boolean canWrite____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).canWrite();
+  }
+
+  @MJI
+  public boolean exists____Z (MJIEnv env, int objref) {
+    return getFile(env,objref).exists();
+  }
+
+  @MJI
+  public boolean createNewFile____Z(MJIEnv env, int objref) {
+    File fileToCreate = getFile(env, objref);
+    try {
+      return fileToCreate.createNewFile();
+
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return false;
+    }
+  }
+
+  @MJI
+  public int list_____3Ljava_lang_String_2(MJIEnv env, int objref){
+         File f=getFile(env,objref);
+    if (f.isDirectory()){
+      String[] farr=f.list();
+      return env.newStringArray(farr);
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public int listRoots_____3Ljava_io_File_2(MJIEnv env, int classRef) {
+    File[] roots = File.listRoots();
+    int rootResultRef = env.newObjectArray("java.io.File", roots.length);
+    ElementInfo rootsEI = env.getModifiableElementInfo(rootResultRef);
+
+    for (int i = 0; i < roots.length; i++) {
+      int rootFileRef = createJPFFile(env, roots[i]);
+      rootsEI.setReferenceElement(i, rootFileRef);
+    }
+
+    return rootResultRef;
+  }
+  // <2do> ..and lots more
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_FileDescriptor.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_FileDescriptor.java
new file mode 100644 (file)
index 0000000..454311c
--- /dev/null
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.DynamicObjectArray;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+
+/**
+ * native peer for file descriptors, which are our basic interface to
+ * access file contents. The implementation used here just forwards
+ * to FileInputStreams, which is terribly inefficient for frequent
+ * restores (in which case a simple byte[] buffer would be more efficient)
+ */
+public class JPF_java_io_FileDescriptor extends NativePeer {
+
+  static JPFLogger logger = JPF.getLogger("java.io.FileDescriptor");
+
+
+  // NOTE: keep those in sync with the model class
+  static final int FD_READ = 0;
+  static final int FD_WRITE = 1;
+  
+  static final int FD_NEW = 0;
+  static final int FD_OPENED = 1;
+  static final int FD_CLOSED = 2;
+
+  
+  int count=2;  // count out std handles
+  DynamicObjectArray<Object> content;
+  
+  public JPF_java_io_FileDescriptor (Config conf){
+    content = new DynamicObjectArray<Object>();
+    count = 2;
+  }
+  
+  @MJI
+  public int open__Ljava_lang_String_2I__I (MJIEnv env, int objref,
+                                                   int fnameRef, int mode){
+    String fname = env.getStringObject(fnameRef);
+    if (mode == FD_READ){
+      return openRead(fname);
+    } else if (mode == FD_WRITE){
+      return openWrite(fname);
+    } else {
+      env.throwException("java.io.IOException", "illegal open mode: " + mode);
+      return -1;
+    }
+  }
+
+  @MJI
+  public int openRead (String fname) {
+    File file = new File(fname);
+    if (file.exists()) {
+      try {
+        FileInputStream fis = new FileInputStream(file);
+        fis.getChannel(); // just to allocate one
+
+        count++;
+        content.set(count, fis);
+
+        logger.info("opening ", fname, " (read) => ", count);
+
+        return count;
+        
+      } catch (IOException x) {
+        logger.warning("failed to open ", fname, " (read) : ", x);
+      }
+    } else {
+      logger.info("cannot open ", fname, " (read) : file not found");
+    }
+    
+    return -1;
+  }
+  
+  @MJI
+  public int openWrite (String fname){
+    File file = new File(fname);
+    try {
+      FileOutputStream fos = new FileOutputStream(file);
+      fos.getChannel(); // just to allocate one
+                
+      count++;
+      content.set(count, fos);
+
+      logger.info("opening ", fname, " (write) => ", count);
+
+      return count;
+        
+    } catch (IOException x) {
+      logger.warning("failed to open ", fname, " (write) : ", x);
+    }
+    
+    return -1;    
+  }
+
+  @MJI
+  public void close0 (MJIEnv env, int objref) {
+    int fd = env.getIntField(objref, "fd");
+    
+    try {
+      Object fs = content.get(fd);
+      
+      if (fs != null){
+        logger.info("closing ", fd);
+
+        if (fs instanceof FileInputStream){
+          ((FileInputStream)fs).close();
+        } else {
+          ((FileOutputStream)fs).close();          
+        }
+      } else {
+        logger.warning("cannot close ", fd, " : no such stream");
+      }
+      content.set(fd, null);
+      
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");      
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+    }
+  }
+  
+  // that's a JPF specific thing - we backrack into
+  // a state where the file was still open, and hence don't want to
+  // change the FileDescriptor identify
+  void reopen (MJIEnv env, int objref) throws IOException {
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+    
+    if (content.get(fd) == null){
+      int mode = env.getIntField(objref, "mode");
+      int fnRef = env.getReferenceField(objref, "fileName");
+      String fname = env.getStringObject(fnRef);
+      
+      if (mode == FD_READ){
+        FileInputStream fis = new FileInputStream(fname);
+        FileChannel fc = fis.getChannel(); // just to allocate one
+        fc.position(off);
+        content.set(fd, fis);
+        
+      } else if (mode == FD_WRITE){
+        FileOutputStream fos = new FileOutputStream(fname);
+        FileChannel fc = fos.getChannel(); // just to allocate one
+        fc.position(off);
+        content.set(fd, fos);
+        
+      } else {
+        env.throwException("java.io.IOException", "illegal mode: " + mode);
+      }
+    }
+  }
+  
+  @MJI
+  public void write__I__ (MJIEnv env, int objref, int b){
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+    
+    try {
+      // this is terrible overhead
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileOutputStream){
+          FileOutputStream fos = (FileOutputStream)fs;
+          FileChannel fc = fos.getChannel();
+          fc.position(off);
+          fos.write(b);
+          env.setLongField(objref, "off", fc.position());
+          
+        } else {
+          env.throwException("java.io.IOException", "write attempt on file opened for read access");
+        }
+        
+      } else {
+        if (env.getIntField(objref, "state") == FD_OPENED){ // backtracked
+          reopen(env,objref);
+          write__I__(env,objref,b); // try again
+        } else {
+          env.throwException("java.io.IOException", "write attempt on closed file");
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");      
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+    }    
+  }
+  
+  @MJI
+  public void write___3BII__ (MJIEnv env, int objref,
+                                     int bref, int offset, int len){
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+    
+    try {
+      // this is terrible overhead
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileOutputStream){
+          FileOutputStream fos = (FileOutputStream)fs;
+          FileChannel fc = fos.getChannel();
+          fc.position(off);
+          
+          byte[] buf = new byte[len]; // <2do> make this a permanent buffer
+          for (int i=0, j=offset; i<len; i++, j++){
+            buf[i] = env.getByteArrayElement(bref, j);
+          }
+          fos.write(buf);
+          
+          env.setLongField(objref, "off", fc.position());
+          
+        } else {
+          env.throwException("java.io.IOException", "write attempt on file opened for read access");
+        }
+        
+      } else {
+        if (env.getIntField(objref, "state") == FD_OPENED){ // backtracked
+          reopen(env,objref);
+          write___3BII__(env,objref,bref,offset,len); // try again
+        } else {
+          env.throwException("java.io.IOException", "write attempt on closed file");
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");      
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+    }        
+  }
+  
+  @MJI
+  public int read____I (MJIEnv env, int objref) {
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+        
+    try {
+      // this is terrible overhead
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileInputStream){
+          FileInputStream fis = (FileInputStream)fs;
+          FileChannel fc = fis.getChannel();
+          fc.position(off);
+          int r = fis.read();
+          env.setLongField(objref, "off", fc.position());
+          return r;
+          
+        } else {
+          env.throwException("java.io.IOException", "read attempt on file opened for write access");
+          return -1;                  
+        }
+        
+      } else {
+        if (env.getIntField(objref, "state") == FD_OPENED){ // backtracked
+          reopen(env,objref);
+          return read____I(env,objref); // try again
+        } else {
+          env.throwException("java.io.IOException", "read attempt on closed file");
+          return -1;
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");
+      return -1;
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return -1;
+    }
+  }
+  
+  @MJI
+  public int read___3BII__I (MJIEnv env, int objref, int bufref, int offset, int len) {
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+        
+    try {
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileInputStream){
+          FileInputStream fis = (FileInputStream)fs;
+          FileChannel fc = fis.getChannel();
+          fc.position(off);
+      
+          byte[] buf = new byte[len]; // <2do> make this a permanent buffer
+          
+          int r = fis.read(buf);
+          for (int i=0, j=offset; i<len; i++, j++) {
+            env.setByteArrayElement(bufref, j, buf[i]);
+          }
+          env.setLongField(objref, "off", fc.position());
+          return r;
+          
+        } else {
+          env.throwException("java.io.IOException", "read attempt on file opened for write access");
+          return -1;                  
+        }
+        
+      } else {
+        if (env.getIntField(objref, "state") == FD_OPENED){ // backtracked
+          reopen(env,objref);
+          return read___3BII__I(env,objref,bufref,offset,len); // try again
+        } else {
+          env.throwException("java.io.IOException", "read attempt on closed file");
+          return -1;        
+        }
+      }
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");
+      return -1;
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return -1;
+    }
+  }
+  
+  @MJI
+  public long skip__J__J (MJIEnv env, int objref, long nBytes) {
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+        
+    try {
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileInputStream){
+          FileInputStream fis = (FileInputStream)fs;
+          FileChannel fc = fis.getChannel();
+          fc.position(off);
+
+          long r = fis.skip(nBytes);
+          env.setLongField(objref, "off", fc.position());
+          return r;
+          
+        } else {
+          env.throwException("java.io.IOException", "skip attempt on file opened for write access");
+          return -1;                  
+        }
+        
+      } else {
+        env.throwException("java.io.IOException", "skip attempt on closed file");
+        return -1;        
+      }
+          
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");
+      return -1;
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return -1;
+    }    
+  }
+  
+  @MJI
+  public void sync____ (MJIEnv env, int objref){
+    int fd = env.getIntField(objref, "fd");
+
+    try {
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileOutputStream){
+          ((FileOutputStream)fs).flush();
+        } else {
+          // nothing
+        }
+        
+      } else {
+        env.throwException("java.io.IOException", "sync attempt on closed file");
+      }
+          
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");      
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+    }        
+  }
+  
+  @MJI
+  public int available____I (MJIEnv env, int objref) {
+    int fd = env.getIntField(objref, "fd");
+    long off = env.getLongField(objref,"off");
+    
+    try {
+      Object fs = content.get(fd);
+      if (fs != null){
+        if (fs instanceof FileInputStream){
+          FileInputStream fis = (FileInputStream)fs;
+          FileChannel fc = fis.getChannel();
+          fc.position(off);
+          return fis.available();
+          
+        } else {
+          env.throwException("java.io.IOException", "available() on file opened for write access");
+          return -1;                  
+        }
+        
+      } else {
+        env.throwException("java.io.IOException", "available() on closed file");
+        return -1;        
+      }
+          
+    } catch (ArrayIndexOutOfBoundsException aobx){
+      env.throwException("java.io.IOException", "file not open");
+      return -1;
+    } catch (IOException iox) {
+      env.throwException("java.io.IOException", iox.getMessage());
+      return -1;
+    }    
+    
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_InputStreamReader.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_InputStreamReader.java
new file mode 100644 (file)
index 0000000..0e47754
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetDecoder;
+
+public class JPF_java_io_InputStreamReader extends NativePeer {
+
+  static final int BUF_SIZE = 128;
+  
+  // <2do> decoder should be stored on a per-reader basis, since Charsets
+  // might differ
+  CharsetDecoder decoder;
+  
+  // ..same here - that's a shared resource with state! Only works for now
+  // since all InputStreamReader decoding is protected by the same lock
+  ByteBuffer in = ByteBuffer.allocate(BUF_SIZE);
+  CharBuffer out = CharBuffer.allocate(BUF_SIZE);
+  public JPF_java_io_InputStreamReader() {
+    decoder = Charset.defaultCharset().newDecoder();
+  }
+  
+  @MJI
+  public int decode___3BI_3CIZ__I (MJIEnv env, int objref,
+                                         int bref, int len, int cref, int off,
+                                         boolean endOfInput){
+    int c = -1;
+    int lim = in.limit();
+    
+    if (lim < in.capacity()){ // left-over bytes
+      in.clear();
+      in.position(lim);
+    } else {
+      decoder.reset();
+    }
+    for (int i=0; i<len; i++){
+      in.put(env.getByteArrayElement(bref,i));
+    }
+    in.flip();
+    
+    decoder.decode(in,out,endOfInput);
+    
+    int n = out.position();
+    for (int i=0, j=off; i<n; i++,j++){
+      env.setCharArrayElement(cref,j, out.get(i));
+    }
+    out.clear();
+    if (n == len){
+      in.clear();
+    }
+    
+    return n;
+  }
+  
+  
+  // <2do> - that fails if we have a multi byte char and there is a backtrack
+  // between decode() calls. Granted, that seems strange, but there is an
+  // InputStream.read() in the loop which might just branch into user code
+  @MJI
+  public int decode__IZ__I (MJIEnv env, int objref, int b, boolean endOfInput){
+    int c = -1;
+    int lim = in.limit();
+    
+    // this is terrible overhead to get a single char, I must be doing something wrong..
+    
+    if (lim < in.capacity()){ // left-over bytes
+      in.clear();
+      in.position(lim);
+    } else {
+      decoder.reset();
+    }
+    
+    in.put((byte)b);
+    in.flip();
+    
+    decoder.decode(in,out,endOfInput);
+
+    if (out.position() == 1){
+      c = out.get(0);
+      out.clear();
+      in.clear();
+    }
+    
+    return c;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectInputStream.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectInputStream.java
new file mode 100644 (file)
index 0000000..e0972f2
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_io_ObjectInputStream extends NativePeer {
+
+  @MJI
+  public int latestUserDefinedLoader____Ljava_lang_ClassLoader_2 (MJIEnv env, int clsRef){
+    // class loaders are not yet supported
+    return MJIEnv.NULL;
+  }
+  
+  @MJI
+  public void bytesToDoubles___3BI_3DII__ (MJIEnv env, int clsRef,
+                                                  int baRef, int bOff,
+                                                  int daRef, int dOff,
+                                                  int nDoubles){
+    int imax = dOff + nDoubles;
+    int j=bOff;
+    
+    for (int i=dOff; i<imax; i++){
+      byte b0 = env.getByteArrayElement(baRef, j++);
+      byte b1 = env.getByteArrayElement(baRef, j++);
+      byte b2 = env.getByteArrayElement(baRef, j++);
+      byte b3 = env.getByteArrayElement(baRef, j++);
+      byte b4 = env.getByteArrayElement(baRef, j++);
+      byte b5 = env.getByteArrayElement(baRef, j++);
+      byte b6 = env.getByteArrayElement(baRef, j++);
+      byte b7 = env.getByteArrayElement(baRef, j++);
+      
+      long bits = 0x00000000000000ff & b7;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b6;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b5;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b4;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b3;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b2;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b1;
+      bits <<= 8;
+      bits |= 0x00000000000000ff & b0;
+      
+      double d = Double.longBitsToDouble(bits);
+      env.setDoubleArrayElement(daRef, i, d);
+    }
+  }
+
+  @MJI
+  public void bytesToFloats___3BI_3FII__ (MJIEnv env, int clsRef,
+                                                 int baRef, int bOff,
+                                                 int faRef, int fOff,
+                                                 int nFloats){
+    int imax = fOff + nFloats;
+    int j=bOff;
+
+    for (int i=fOff; i<imax; i++){
+      byte b0 = env.getByteArrayElement(baRef, j++);
+      byte b1 = env.getByteArrayElement(baRef, j++);
+      byte b2 = env.getByteArrayElement(baRef, j++);
+      byte b3 = env.getByteArrayElement(baRef, j++);
+
+      int bits = 0x000000ff & b3;
+      bits <<= 8;
+      bits |= 0x000000ff & b2;
+      bits <<= 8;
+      bits |= 0x000000ff & b1;
+      bits <<= 8;
+      bits |= 0x000000ff & b0;
+
+      float f = Float.intBitsToFloat(bits);
+      env.setFloatArrayElement(faRef, i, f);
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectOutputStream.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectOutputStream.java
new file mode 100644 (file)
index 0000000..94f3f7a
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_io_ObjectOutputStream extends NativePeer {
+
+  @MJI
+  public void doublesToBytes___3DI_3BII__ (MJIEnv env, int clsRef,
+                                                  int daRef, int dOff,
+                                                  int baRef, int bOff,
+                                                  int nDoubles){
+    int imax = dOff + nDoubles;
+    for (int i=dOff, j=bOff; i<imax; i++){
+      double d = env.getDoubleArrayElement(daRef, i);
+      long l = Double.doubleToLongBits(d);
+      for (int k=0; k<8; k++){
+        env.setByteArrayElement(baRef, j++, (byte)l);
+        l >>= 8;
+      }
+    }
+  }
+  
+  
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectStreamClass.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_ObjectStreamClass.java
new file mode 100644 (file)
index 0000000..9725b47
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_io_ObjectStreamClass extends NativePeer {
+  @MJI
+  public void initNative____V (MJIEnv env, int clsObjRef) {
+    // cut off
+  }
+  
+  // why is this here??
+  @MJI
+  public boolean hasStaticInitializer__Ljava_lang_Class_2__Z (MJIEnv env, int objRef, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    MethodInfo mi = ci.getMethod("<clinit>()V", false);          
+    return (mi != null);
+  }
+
+  // just a little accelerator
+  @MJI
+  public int getDeclaredSUID__Ljava_lang_Class_2__Ljava_lang_Long_2 (MJIEnv env, int objRef, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    FieldInfo fi = ci.getDeclaredStaticField("serialVersionUID");
+    if (fi != null){
+      ElementInfo ei = ci.getStaticElementInfo();
+      long l = ei.getLongField(fi);
+      return env.newLong(l);
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_OutputStreamWriter.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_OutputStreamWriter.java
new file mode 100644 (file)
index 0000000..c80a94a
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.nio.ByteBuffer;
+import java.nio.CharBuffer;
+import java.nio.charset.Charset;
+import java.nio.charset.CharsetEncoder;
+
+/**
+ * native peer for OutputStreamWriter, to avoid that we have the
+ * char-to-byte conversion in JPF
+ *
+ * <2do> this needs to be de-staticed (see model class)
+ */
+public class JPF_java_io_OutputStreamWriter extends NativePeer {
+
+  static final int BUF_SIZE=128; // needs to be the same as in the model class!
+  static CharsetEncoder encoder;
+  
+  static CharBuffer in = CharBuffer.allocate(BUF_SIZE);
+  static ByteBuffer out = ByteBuffer.allocate(BUF_SIZE*6); // worst case UTF-8
+
+  public JPF_java_io_OutputStreamWriter() {
+    encoder = Charset.defaultCharset().newEncoder();
+  }
+
+  @MJI
+  public int encode___3CII_3B__I (MJIEnv env, int objref,
+                                         int cref, int off, int len,
+                                         int bref){
+    if (len > BUF_SIZE){ // check for buffer overflow
+      len = BUF_SIZE;
+    }
+    int imax = off+len;
+
+    out.clear();
+    in.clear();
+    
+    for (int i=off; i<imax; i++){
+      in.put(env.getCharArrayElement(cref, i));
+    }
+
+    in.flip();
+    encoder.encode(in,out,true);
+    
+    int n = out.position();
+    for (int i=0; i<n; i++){
+      env.setByteArrayElement(bref,i,out.get(i));
+    }
+    
+    return n;
+  }
+  
+  @MJI
+  public int encode__Ljava_lang_String_2II_3B__I (MJIEnv env, int objref,
+                                         int sref, int off, int len,
+                                         int bref){
+    int cref = env.getReferenceField(sref, "value");
+    
+    return encode___3CII_3B__I(env,objref,cref,off,len,bref);
+  }
+  
+  @MJI
+  public int encode__C_3B__I (MJIEnv env, int objref, char c, int bufref) {
+    out.clear();
+    
+    in.clear();
+    in.put(c);
+    in.flip();
+
+    encoder.encode(in,out,true);
+    
+    int n = out.position();
+    for (int i=0; i<n; i++){
+      env.setByteArrayElement(bufref,i,out.get(i));
+    }
+    
+    return n;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java b/src/peers/gov/nasa/jpf/vm/JPF_java_io_RandomAccessFile.java
new file mode 100644 (file)
index 0000000..da863e2
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+import java.util.HashMap;
+
+import gov.nasa.jpf.Config;
+
+/**
+ * MJI NativePeer class for java.io.RandomAccessFile library abstraction
+ *
+ * @author Owen O'Malley
+ */
+public class JPF_java_io_RandomAccessFile extends NativePeer {
+
+       // need to see whether the file is already in use
+       // if so, then we'll update the file data and length in the original file
+       // we do update the length in the local object, but not the data
+               
+       static HashMap<Integer, Integer> File2DataMap;
+       
+  public static boolean init (Config conf) {
+    File2DataMap = new HashMap<Integer, Integer>();
+    return (File2DataMap != null);
+  } 
+
+       // get the mapped object if one exists
+       private static int getMapping(MJIEnv env, int this_ptr) {
+               int fn_ptr = env.getReferenceField(this_ptr,"filename");
+               Object o = File2DataMap.get(new Integer(fn_ptr));
+               if (o == null)
+                       return this_ptr;
+               return ((Integer)o).intValue();
+       }
+       
+       // set the mapping during the constructor call
+  @MJI
+       public void setDataMap____V (MJIEnv env, int this_ptr) {
+               int fn_ptr = env.getReferenceField(this_ptr,"filename");
+               if (!File2DataMap.containsKey(new Integer(fn_ptr))) 
+                       File2DataMap.put(new Integer(fn_ptr),new Integer(this_ptr));
+       }
+       
+  static ClassInfo getDataRepresentationClassInfo (MJIEnv env) {
+    ThreadInfo ti = env.getThreadInfo();
+    Instruction insn = ti.getPC();
+    
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(DataRepresentation);
+    if (ci.initializeClass(ti)){
+      env.repeatInvocation();
+      return null;
+    }
+    
+    return ci;
+  }
+
+  @MJI
+  public void writeByte__I__V (MJIEnv env, int this_ptr, int data) {
+    
+    
+    long current_posn = env.getLongField(this_ptr, current_position);
+    long current_len = env.getLongField(this_ptr, current_length);
+    int chunk_size = env.getStaticIntField(RandomAccessFile, CHUNK_SIZE);
+    int chunk = findDataChunk(env, this_ptr, current_posn,
+                              chunk_size);
+    setDataValue(env, chunk, current_posn, (byte) data, chunk_size);
+    current_posn += 1;
+    env.setLongField(this_ptr, current_position, current_posn);
+    if (current_posn >= current_len) {
+      env.setLongField(this_ptr, current_length, current_posn + 1);
+      // update length in the mapped object if it exists
+      env.setLongField(getMapping(env,this_ptr), current_length, current_posn + 1);
+    }
+  }
+
+  /**
+   * This is a bit lame doing it this way, but it is easy.
+   */
+  @MJI
+  public void write___3BII__V (MJIEnv env, int this_ptr, int data_array,
+                           int start, int len) {
+    byte[] data_values = env.getByteArrayObject(data_array);
+    for(int i=start; i < len; ++i) {
+      writeByte__I__V(env, this_ptr, data_values[i]);
+    }
+  }
+
+  @MJI
+  public void setLength__J__V(MJIEnv env, int this_ptr, long len) {
+    long current_posn = env.getLongField(this_ptr, current_position);
+    long current_len = env.getLongField(this_ptr, current_length);
+    if (current_posn >= len && len < current_len) {
+      env.setLongField(this_ptr, current_position, len);
+    }
+    env.setLongField(this_ptr, current_length, len);
+    // update length in the mapped object if it exists
+    env.setLongField(getMapping(env,this_ptr), current_length, current_posn + 1);
+  }
+
+  @MJI
+  public int read___3BII__I (MJIEnv env, int this_ptr, int data_array,
+                         int start, int len) {
+    int i = 0;
+    long current_posn = env.getLongField(this_ptr, current_position);
+    long current_len = env.getLongField(this_ptr, current_length);
+    while (i < len && current_posn < current_len) {
+      env.setByteArrayElement(data_array, start + i, readByte____B(env, this_ptr));
+      i += 1;
+      current_posn += 1;
+    }
+    if (i == 0) {
+      return -1;
+    }
+    return i;
+  }
+
+  @MJI
+  public byte readByte____B (MJIEnv env, int this_ptr) {
+    long current_posn = env.getLongField(this_ptr, current_position);
+    long current_len = env.getLongField(this_ptr, current_length);
+    int chunk_size = env.getStaticIntField(RandomAccessFile, CHUNK_SIZE);
+    if (current_posn >= current_len) {
+      env.throwException(EOFException);
+    }
+    int chunk = findDataChunk(env, this_ptr, current_posn,
+                              chunk_size);
+    byte result = getDataValue(env, chunk, current_posn, chunk_size);
+    env.setLongField(this_ptr, current_position, current_posn + 1);
+    return result;
+  }
+
+  private static final int INT_SIZE = 4;
+  private static final String data_root = "data_root";
+  private static final String current_position = "currentPosition";
+  private static final String current_length = "currentLength";
+  private static final String CHUNK_SIZE = "CHUNK_SIZE";
+  private static final String chunk_index = "chunk_index";
+  private static final String next = "next";
+  private static final String data = "data";
+  private static final String EOFException = "java.io.EOFException";
+  private static final String RandomAccessFile = "java.io.RandomAccessFile";
+  private static final String DataRepresentation =
+    RandomAccessFile + "$DataRepresentation";
+
+  private static int findDataChunk(MJIEnv env, int this_ptr, long position,
+                                   int chunk_size) {
+       
+    ClassInfo dataRep = getDataRepresentationClassInfo(env);
+    if (dataRep == null) {
+      // will be reexecuted
+      return 0;
+    }
+    
+       //check if the file data is mapped, use mapped this_ptr if it exists
+       this_ptr = getMapping(env,this_ptr);    
+    int prev_obj = MJIEnv.NULL;
+    int cur_obj = env.getReferenceField(this_ptr, data_root);
+    long chunk_idx = position/chunk_size;
+    while (cur_obj != MJIEnv.NULL &&
+           env.getLongField(cur_obj, chunk_index) < chunk_idx) {
+      prev_obj = cur_obj;
+      cur_obj = env.getReferenceField(cur_obj, next);
+    }
+    if (cur_obj != MJIEnv.NULL &&
+        env.getLongField(cur_obj, chunk_index) == chunk_idx) {
+      return cur_obj;
+    }
+    int result = env.newObject(dataRep);
+    int int_array = env.newIntArray(chunk_size/INT_SIZE);
+    env.setReferenceField(result, data, int_array);
+    env.setLongField(result, chunk_index, chunk_idx);
+    env.setReferenceField(result, next, cur_obj);
+    if (prev_obj == MJIEnv.NULL) {
+      env.setReferenceField(this_ptr, data_root, result);
+    } else {
+      env.setReferenceField(prev_obj, next, result);
+    }
+    return result;
+  }
+
+  private static void setDataValue(MJIEnv env, int chunk_obj, long position,
+                                   byte data_value, int chunk_size) {
+    int offset = (int) (position % chunk_size);
+    int index = offset / INT_SIZE;
+    int bit_shift = 8 * (offset % INT_SIZE);
+    int int_array = env.getReferenceField(chunk_obj, data);
+    int old_value = env.getIntArrayElement(int_array, index);
+    env.setIntArrayElement(int_array, index,
+                             (old_value & ~(0xff << bit_shift)) |
+                             data_value << bit_shift);
+  }
+
+  private static byte getDataValue(MJIEnv env, int chunk_obj, long position,
+                                   int chunk_size) {
+    int offset = (int) (position % chunk_size);
+    int index = offset / INT_SIZE;
+    int bit_shift = 8 * (offset % INT_SIZE);
+    int int_array = env.getReferenceField(chunk_obj, data);
+    return (byte) (env.getIntArrayElement(int_array, index) >> bit_shift);
+
+  }
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Boolean.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Boolean.java
new file mode 100644 (file)
index 0000000..6f7b6fd
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_lang_Boolean extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public int valueOf__Z__Ljava_lang_Boolean_2 (MJIEnv env, int clsRef, boolean val) {
+    return env.valueOfBoolean(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Byte.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Byte.java
new file mode 100644 (file)
index 0000000..5ddbe20
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_lang_Byte extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public int valueOf__B__Ljava_lang_Byte_2 (MJIEnv env, int clsRef, byte val) {
+    return env.valueOfByte(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Character.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Character.java
new file mode 100644 (file)
index 0000000..fe011fa
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Character library abstraction
+ * Whoever is using this seriously is definitely screwed, performance-wise
+ */
+public class JPF_java_lang_Character extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public boolean isDefined__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isDefined(c);
+  }
+
+  @MJI
+  public boolean isDigit__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isDigit(c);
+  }
+
+  @MJI
+  public boolean isISOControl__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isISOControl(c);
+  }
+
+  @MJI
+  public boolean isIdentifierIgnorable__C__Z (MJIEnv env, int clsObjRef, 
+                                                  char c) {
+    return Character.isIdentifierIgnorable(c);
+  }
+
+  @MJI
+  public boolean isJavaIdentifierPart__C__Z (MJIEnv env, int clsObjRef, 
+                                                 char c) {
+    return Character.isJavaIdentifierPart(c);
+  }
+
+  @MJI
+  public boolean isJavaIdentifierStart__C__Z (MJIEnv env, int clsObjRef, 
+                                                  char c) {
+    return Character.isJavaIdentifierStart(c);
+  }
+
+  @MJI
+  public boolean isJavaLetterOrDigit__C__Z (MJIEnv env, int clsObjRef, 
+                                                char c) {
+    return Character.isJavaIdentifierPart(c);
+  }
+
+  @MJI
+  public boolean isJavaLetter__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isJavaIdentifierStart(c);
+  }
+
+  @MJI
+  public boolean isLetterOrDigit__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isLetterOrDigit(c);
+  }
+
+  @MJI
+  public boolean isLetter__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isLetter(c);
+  }
+
+  @MJI
+  public boolean isLowerCase__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isLowerCase(c);
+  }
+
+  @MJI
+  public int getNumericValue__C__I (MJIEnv env, int clsObjRef, char c) {
+    return Character.getNumericValue(c);
+  }
+
+  @MJI
+  public boolean isSpaceChar__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isSpaceChar(c);
+  }
+
+  @MJI
+  public boolean isSpace__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isWhitespace(c);
+  }
+
+  @MJI
+  public boolean isTitleCase__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isTitleCase(c);
+  }
+
+  @MJI
+  public int getType__C__I (MJIEnv env, int clsObjRef, char c) {
+    return Character.getType(c);
+  }
+
+  @MJI
+  public boolean isUnicodeIdentifierPart__C__Z (MJIEnv env, int clsObjRef, 
+                                                    char c) {
+    return Character.isUnicodeIdentifierPart(c);
+  }
+
+  @MJI
+  public boolean isUnicodeIdentifierStart__C__Z (MJIEnv env, int clsObjRef, 
+                                                     char c) {
+    return Character.isUnicodeIdentifierStart(c);
+  }
+
+  @MJI
+  public boolean isUpperCase__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isUpperCase(c);
+  }
+
+  @MJI
+  public boolean isWhitespace__C__Z (MJIEnv env, int clsObjRef, char c) {
+    return Character.isWhitespace(c);
+  }
+
+  // pcm - we keep this in here to avoid the potentially expensive
+  // real clinit. This has changed a lot in Java 1.4.2 (deferred init, i.e.
+  // we could actually use it now), but in <= 1.4.1 it executes some
+  // 200,000 insns, and some people who didn't grew up with Java might
+  // deduce that JPF is hanging. It's not, it just shows why a real VM has to
+  // be fast.
+  // It is actually Ok to bypass the real clinit if we turn all the
+  // important methods into native ones, i.e. delegate to the real thing.
+  @MJI
+  public void $clinit____V (MJIEnv env, int clsObjRef) {
+    env.setStaticByteField("java.lang.Character", "UNASSIGNED", (byte) 0);
+    env.setStaticByteField("java.lang.Character", "UPPERCASE_LETTER", (byte) 1);
+    env.setStaticByteField("java.lang.Character", "LOWERCASE_LETTER", (byte) 2);
+    env.setStaticByteField("java.lang.Character", "TITLECASE_LETTER", (byte) 3);
+    env.setStaticByteField("java.lang.Character", "MODIFIER_LETTER", (byte) 4);
+    env.setStaticByteField("java.lang.Character", "OTHER_LETTER", (byte) 5);
+    env.setStaticByteField("java.lang.Character", "NON_SPACING_MARK", (byte) 6);
+    env.setStaticByteField("java.lang.Character", "ENCLOSING_MARK", (byte) 7);
+    env.setStaticByteField("java.lang.Character", "COMBINING_SPACING_MARK", (byte) 8);
+    env.setStaticByteField("java.lang.Character", "DECIMAL_DIGIT_NUMBER", (byte) 9);
+    env.setStaticByteField("java.lang.Character", "LETTER_NUMBER", (byte) 10);
+    env.setStaticByteField("java.lang.Character", "OTHER_NUMBER", (byte) 11);
+    env.setStaticByteField("java.lang.Character", "SPACE_SEPARATOR", (byte) 12);
+    env.setStaticByteField("java.lang.Character", "LINE_SEPARATOR", (byte) 13);
+    env.setStaticByteField("java.lang.Character", "PARAGRAPH_SEPARATOR", (byte) 14);
+    env.setStaticByteField("java.lang.Character", "CONTROL", (byte) 15);
+    env.setStaticByteField("java.lang.Character", "FORMAT", (byte) 16);
+    env.setStaticByteField("java.lang.Character", "PRIVATE_USE", (byte) 18);
+    env.setStaticByteField("java.lang.Character", "SURROGATE", (byte) 19);
+    env.setStaticByteField("java.lang.Character", "DASH_PUNCTUATION", (byte) 20);
+    env.setStaticByteField("java.lang.Character", "START_PUNCTUATION", (byte) 21);
+    env.setStaticByteField("java.lang.Character", "END_PUNCTUATION", (byte) 22);
+    env.setStaticByteField("java.lang.Character", "CONNECTOR_PUNCTUATION", (byte) 23);
+    env.setStaticByteField("java.lang.Character", "OTHER_PUNCTUATION", (byte) 24);
+    env.setStaticByteField("java.lang.Character", "MATH_SYMBOL", (byte) 25);
+    env.setStaticByteField("java.lang.Character", "CURRENCY_SYMBOL", (byte) 26);
+    env.setStaticByteField("java.lang.Character", "MODIFIER_SYMBOL", (byte) 27);
+    env.setStaticByteField("java.lang.Character", "OTHER_SYMBOL", (byte) 28);
+    env.setStaticIntField("java.lang.Character", "MIN_RADIX", 2);
+    env.setStaticIntField("java.lang.Character", "MAX_RADIX", 36);
+    env.setStaticCharField("java.lang.Character", "MIN_VALUE", '\u0000');
+    env.setStaticCharField("java.lang.Character", "MAX_VALUE", '\uffff');
+
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("char");
+    env.setStaticReferenceField("java.lang.Character", "TYPE", 
+                             ci.getClassObjectRef());
+  }
+
+  @MJI
+  public int digit__CI__I (MJIEnv env, int clsObjRef, char c, int radix) {
+    return Character.digit(c, radix);
+  }
+
+  @MJI
+  public char forDigit__II__C (MJIEnv env, int clsObjRef, int digit, 
+                                   int radix) {
+    return Character.forDigit(digit, radix);
+  }
+
+  @MJI
+  public char toLowerCase__C__C (MJIEnv env, int clsObjRef, char c) {
+    return Character.toLowerCase(c);
+  }
+
+  @MJI
+  public char toTitleCase__C__C (MJIEnv env, int clsObjRef, char c) {
+    return Character.toTitleCase(c);
+  }
+
+  @MJI
+  public char toUpperCase__C__C (MJIEnv env, int clsObjRef, char c) {
+    return Character.toUpperCase(c);
+  }
+
+  @MJI
+  public int valueOf__C__Ljava_lang_Character_2 (MJIEnv env, int clsRef, char val) {
+    return env.valueOfCharacter(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Class.java
new file mode 100644 (file)
index 0000000..90a5dcf
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Set;
+
+
+/**
+ * MJI NativePeer class for java.lang.Class library abstraction
+ */
+public class JPF_java_lang_Class extends NativePeer {
+  
+  static final String FIELD_CLASSNAME = "java.lang.reflect.Field";
+  static final String METHOD_CLASSNAME = "java.lang.reflect.Method";
+  static final String CONSTRUCTOR_CLASSNAME = "java.lang.reflect.Constructor";
+  
+  public static boolean init (Config conf){
+    // we create Method and Constructor objects, so we better make sure these
+    // classes are initialized (they already might be)
+    JPF_java_lang_reflect_Method.init(conf);
+    JPF_java_lang_reflect_Constructor.init(conf);
+    return true;
+  }
+  
+  @MJI
+  public boolean isArray____Z (MJIEnv env, int robj) {
+    ClassInfo ci = env.getReferredClassInfo( robj);
+    return ci.isArray();
+  }
+
+  @MJI
+  public int getComponentType____Ljava_lang_Class_2 (MJIEnv env, int robj) {
+    if (isArray____Z(env, robj)) {
+      ThreadInfo ti = env.getThreadInfo();
+      Instruction insn = ti.getPC();
+      ClassInfo ci = env.getReferredClassInfo( robj).getComponentClassInfo();
+
+    if (ci.initializeClass(ti)){
+        env.repeatInvocation();
+        return MJIEnv.NULL;
+      }
+
+      return ci.getClassObjectRef();
+    }
+
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean isInstance__Ljava_lang_Object_2__Z (MJIEnv env, int robj,
+                                                         int r1) {
+    ElementInfo sei = env.getStaticElementInfo(robj);
+    ClassInfo   ci = sei.getClassInfo();
+    ClassInfo   ciOther = env.getClassInfo(r1);
+    return (ciOther.isInstanceOf(ci));
+  }
+
+  @MJI
+  public boolean isInterface____Z (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo( robj);
+    return ci.isInterface();
+  }
+  
+  @MJI
+  public boolean isAssignableFrom__Ljava_lang_Class_2__Z (MJIEnv env, int rcls,
+                                                              int r1) {
+    ElementInfo sei1 = env.getStaticElementInfo(rcls);
+    ClassInfo   ci1 = sei1.getClassInfo();
+
+    ElementInfo sei2 = env.getStaticElementInfo(r1);
+    ClassInfo   ci2 = sei2.getClassInfo();
+
+    return ci2.isInstanceOf( ci1);
+  }
+  
+  @MJI
+  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){    
+    ClassInfo ci = env.getReferredClassInfo( robj);
+    AnnotationInfo[] ai = ci.getAnnotations();
+
+    try {
+      return env.newAnnotationProxies(ai);
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }
+  }
+  
+  @MJI
+  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj,
+                                                                                int annoClsRef){
+    ClassInfo ci = env.getReferredClassInfo( robj);
+    ClassInfo aci = env.getReferredClassInfo(annoClsRef);
+    
+    AnnotationInfo ai = ci.getAnnotation(aci.getName());
+    if (ai != null){
+      ClassInfo aciProxy = aci.getAnnotationProxy();
+      
+      try {
+        return env.newAnnotationProxy(aciProxy, ai);
+      } catch (ClinitRequired x){
+        env.handleClinitRequest(x.getRequiredClassInfo());
+        return MJIEnv.NULL;
+      }
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+  
+  @MJI
+  public int getPrimitiveClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
+                                                            int rcls, int stringRef) {
+    // we don't really have to check for a valid class name here, since
+    // this is a package default method that just gets called from
+    // the clinit of box classes
+    // note this does NOT return the box class (e.g. java.lang.Integer), which
+    // is a normal, functional class, but a primitive class (e.g. 'int') that
+    // is rather a strange beast (not even Object derived)
+    
+    ClassLoaderInfo scli = env.getSystemClassLoaderInfo(); // this is the one responsible for builtin classes
+    String primClsName = env.getStringObject(stringRef); // always initialized
+    
+    ClassInfo ci = scli.getResolvedClassInfo(primClsName);
+    return ci.getClassObjectRef();
+  }
+
+  @MJI
+  public boolean desiredAssertionStatus____Z (MJIEnv env, int robj) {
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    return ci.desiredAssertionStatus();
+  }
+
+  public static int getClassObject (MJIEnv env, ClassInfo ci){
+    ThreadInfo ti = env.getThreadInfo();
+    Instruction insn = ti.getPC();
+
+    if (ci.initializeClass(ti)){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+
+    StaticElementInfo ei = ci.getStaticElementInfo();
+    int ref = ei.getClassObjectRef();
+
+    return ref;
+  }
+  
+  @MJI
+  public int forName__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env,
+                                                                       int rcls,
+                                                                       int clsNameRef) {
+    if (clsNameRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "no class name provided");
+      return MJIEnv.NULL;
+    }
+    
+    String clsName = env.getStringObject(clsNameRef);
+    
+    if (clsName.isEmpty()){
+      env.throwException("java.lang.ClassNotFoundException", "empty class name");
+      return MJIEnv.NULL;  
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
+    // class of the method that includes the invocation of Class.forName() 
+    ClassInfo cls = mi.getClassInfo();
+
+    String name;
+    // for array type, the component terminal must be resolved
+    if(clsName.charAt(0)=='[') {
+      name = Types.getComponentTerminal(clsName);
+    } else{
+      name = clsName;
+    }
+
+    // make the classloader of the class including the invocation of 
+    // Class.forName() resolve the class with the given name
+    try {
+      cls.resolveReferencedClass(name);
+    } catch(LoadOnJPFRequired lre) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+
+    // The class obtained here is the same as the resolved one, except
+    // if it represents an array type
+    ClassInfo ci = cls.getClassLoaderInfo().getResolvedClassInfo(clsName);
+
+    return getClassObject(env, ci);
+  }
+
+  /**
+   * this is an example of a native method issuing direct calls - otherwise known
+   * as a round trip.
+   * We don't have to deal with class init here anymore, since this is called
+   * via the class object of the class to instantiate
+   */
+  @MJI
+  public int newInstance____Ljava_lang_Object_2 (MJIEnv env, int robj) {
+    ThreadInfo ti = env.getThreadInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+    
+    ClassInfo ci = env.getReferredClassInfo(robj);   // what are we
+    MethodInfo miCtor = ci.getMethod("<init>()V", true); // note there always is one since something needs to call Object()
+
+    if (frame == null){ // first time around
+      if(ci.isAbstract()){ // not allowed to instantiate
+        env.throwException("java.lang.InstantiationException");
+        return MJIEnv.NULL;
+      }
+
+      // <2do> - still need to handle protected
+      if (miCtor.isPrivate()) {
+        env.throwException("java.lang.IllegalAccessException", "cannot access non-public member of class " + ci.getName());
+        return MJIEnv.NULL;
+      }
+
+      int objRef = env.newObjectOfUncheckedClass(ci);  // create the thing
+
+      frame = miCtor.createDirectCallStackFrame(ti, 1);
+      // note that we don't set this as a reflection call since it is supposed to propagate exceptions
+      frame.setReferenceArgument(0, objRef, null);
+      frame.setLocalReferenceVariable(0, objRef);        // (1) store ref for retrieval during re-exec
+      ti.pushFrame(frame);
+      
+      // check if we have to push clinits
+      ci.initializeClass(ti);
+      
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+      
+    } else { // re-execution
+      int objRef = frame.getLocalVariable(0); // that's the object ref we set in (1)
+      return objRef;
+    }      
+  }
+  
+  @MJI
+  public int getSuperclass____Ljava_lang_Class_2 (MJIEnv env, int robj) {
+    ClassInfo ci = env.getReferredClassInfo( robj);
+    ClassInfo sci = ci.getSuperClass();
+    if (sci != null) {
+      return sci.getClassObjectRef();
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+
+  int getMethod (MJIEnv env, int clsRef, ClassInfo ciMethod, String mname, int argTypesRef,
+                        boolean isRecursiveLookup, boolean publicOnly) {
+
+    ClassInfo ci = env.getReferredClassInfo( clsRef);
+    
+    StringBuilder sb = new StringBuilder(mname);
+    sb.append('(');
+    int nParams = argTypesRef != MJIEnv.NULL ? env.getArrayLength(argTypesRef) : 0;
+    for (int i=0; i<nParams; i++) {
+      int cRef = env.getReferenceArrayElement(argTypesRef, i);
+      ClassInfo cit = env.getReferredClassInfo( cRef);
+      String tname = cit.getName();
+      String tcode = tname;
+      tcode = Types.getTypeSignature(tcode, false);
+      sb.append(tcode);
+    }
+    sb.append(')');
+    String fullMthName = sb.toString();
+
+    MethodInfo mi = ci.getReflectionMethod(fullMthName, isRecursiveLookup);
+    if (mi == null || (publicOnly && !mi.isPublic())) {
+      env.throwException("java.lang.NoSuchMethodException", ci.getName() + '.' + fullMthName);
+      return MJIEnv.NULL;
+      
+    } else {
+      return createMethodObject(env, ciMethod, mi);      
+    }
+  }
+
+  int createMethodObject (MJIEnv env, ClassInfo objectCi, MethodInfo mi) {
+    // NOTE - we rely on Constructor and Method peers being initialized
+    if (mi.isCtor()){
+      return JPF_java_lang_reflect_Constructor.createConstructorObject(env, objectCi, mi);
+    } else {
+      return JPF_java_lang_reflect_Method.createMethodObject(env, objectCi, mi);      
+    }
+  }
+  
+  @MJI
+  public int getDeclaredMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
+                                                                                                     int nameRef, int argTypesRef) {
+    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    String mname = env.getStringObject(nameRef);
+    return getMethod(env, clsRef, mci, mname, argTypesRef, false, false);
+  }
+
+  @MJI
+  public int getDeclaredConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env,
+                                                                                               int clsRef,
+                                                                                               int argTypesRef){
+    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    int ctorRef =  getMethod(env,clsRef, mci, "<init>",argTypesRef,false, false);
+    return ctorRef;
+  }
+  
+  @MJI
+  public int getMethod__Ljava_lang_String_2_3Ljava_lang_Class_2__Ljava_lang_reflect_Method_2 (MJIEnv env, int clsRef,
+                                                                                                     int nameRef, int argTypesRef) {
+    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    String mname = env.getStringObject(nameRef);
+    return getMethod( env, clsRef, mci, mname, argTypesRef, true, true);
+  }
+
+  private void addDeclaredMethodsRec (boolean includeSuperClasses, HashMap<String,MethodInfo>methods, ClassInfo ci){
+    
+    if (includeSuperClasses){ // do NOT include Object methods for interfaces
+      ClassInfo sci = ci.getSuperClass();
+      if (sci != null){
+        addDeclaredMethodsRec( includeSuperClasses, methods,sci);
+      }
+    }
+
+    ClassLoaderInfo cl = ci.getClassLoaderInfo();
+    for (String ifcName : ci.getDirectInterfaceNames()){
+      ClassInfo ici = cl.getResolvedClassInfo(ifcName); // has to be already defined, so no exception
+      addDeclaredMethodsRec( includeSuperClasses, methods,ici);
+    }
+
+    for (MethodInfo mi : ci.getDeclaredMethodInfos()) {
+      // filter out non-public, <clinit> and <init>
+      if (mi.isPublic() && (mi.getName().charAt(0) != '<')) {
+        String mname = mi.getUniqueName();
+
+        if (!(ci.isInterface() && methods.containsKey(mname))){
+          methods.put(mname, mi);
+        }
+      }
+    }
+  }
+
+  @MJI
+  public int getMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
+    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    ClassInfo ci = env.getReferredClassInfo(objref);
+
+    // collect all the public, non-ctor instance methods
+    if (!ci.isPrimitive()) {
+      HashMap<String,MethodInfo> methods = new HashMap<String,MethodInfo>();
+      addDeclaredMethodsRec( !ci.isInterface(), methods,ci);
+      
+      int n = methods.size();
+      int aref = env.newObjectArray("Ljava/lang/reflect/Method;", n);
+      int i=0;
+
+      for (MethodInfo mi : methods.values()){
+        int mref = createMethodObject(env, mci, mi);
+        env.setReferenceArrayElement(aref,i++,mref);
+      }
+
+      return aref;
+
+    } else {
+      return env.newObjectArray("Ljava/lang/reflect/Method;", 0);
+    }
+  }
+  
+  @MJI
+  public int getDeclaredMethods_____3Ljava_lang_reflect_Method_2 (MJIEnv env, int objref) {
+    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    ClassInfo ci = env.getReferredClassInfo(objref);
+    MethodInfo[] methodInfos = ci.getDeclaredMethodInfos();
+    
+    // we have to filter out the ctors and the static init
+    int nMth = methodInfos.length;
+    for (int i=0; i<methodInfos.length; i++){
+      if (methodInfos[i].getName().charAt(0) == '<'){
+        methodInfos[i] = null;
+        nMth--;
+      }
+    }
+    
+    int aref = env.newObjectArray("Ljava/lang/reflect/Method;", nMth);
+    
+    for (int i=0, j=0; i<methodInfos.length; i++) {
+      if (methodInfos[i] != null){
+        int mref = createMethodObject(env, mci, methodInfos[i]);
+        env.setReferenceArrayElement(aref,j++,mref);
+      }
+    }
+    
+    return aref;
+  }
+  
+  int getConstructors (MJIEnv env, int objref, boolean publicOnly){
+    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    ClassInfo ci = env.getReferredClassInfo(objref);
+    ArrayList<MethodInfo> ctors = new ArrayList<MethodInfo>();
+    
+    // we have to filter out the ctors and the static init
+    for (MethodInfo mi : ci.getDeclaredMethodInfos()){
+      if (mi.getName().equals("<init>")){
+        if (!publicOnly || mi.isPublic()) {
+          ctors.add(mi);
+        }
+      }
+    }
+    
+    int nCtors = ctors.size();
+    int aref = env.newObjectArray("Ljava/lang/reflect/Constructor;", nCtors);
+    
+    for (int i=0; i<nCtors; i++){
+      env.setReferenceArrayElement(aref, i, createMethodObject(env, mci, ctors.get(i)));
+    }
+    
+    return aref;
+  }
+  
+  @MJI
+  public int getConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
+    return getConstructors(env, objref, true);
+  }  
+  
+  @MJI
+  public int getDeclaredConstructors_____3Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objref){
+    return getConstructors(env, objref, false);
+  }
+  
+  @MJI
+  public int getConstructor___3Ljava_lang_Class_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env, int clsRef,
+                                                                                       int argTypesRef){
+    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
+    if (mci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    // <2do> should only return a public ctor 
+    return getMethod(env,clsRef, mci, "<init>",argTypesRef,false,true);
+  }
+  
+  // this is only used for system classes such as java.lang.reflect.Method
+  ClassInfo getInitializedClassInfo (MJIEnv env, String clsName){
+    ThreadInfo ti = env.getThreadInfo();
+    Instruction insn = ti.getPC();
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo( clsName);
+    
+    if (ci.initializeClass(ti)){
+      return null;
+    } else {
+      return ci;
+    }    
+  }
+  
+  @MJI
+  public void initialize0____V (MJIEnv env, int clsObjRef){
+    ClassInfo ci = env.getReferredClassInfo( clsObjRef);
+    ci.initializeClass(ThreadInfo.currentThread);
+  }
+
+  Set<ClassInfo> getInitializedInterfaces (MJIEnv env, ClassInfo ci){
+    ThreadInfo ti = env.getThreadInfo();
+    Instruction insn = ti.getPC();
+
+    Set<ClassInfo> ifcs = ci.getAllInterfaceClassInfos();
+    for (ClassInfo ciIfc : ifcs){
+    if (ciIfc.initializeClass(ti)){
+        return null;
+      } 
+    }
+
+    return ifcs;
+  }
+  
+  static int createFieldObject (MJIEnv env, FieldInfo fi, ClassInfo fci){
+    int regIdx = JPF_java_lang_reflect_Field.registerFieldInfo(fi);
+    
+    int eidx = env.newObject(fci);
+    ElementInfo ei = env.getModifiableElementInfo(eidx);    
+    ei.setIntField("regIdx", regIdx);
+    
+    return eidx;
+  }
+  
+  @MJI
+  public int getDeclaredFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int objRef) {
+    ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
+    if (fci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+
+    ClassInfo ci = env.getReferredClassInfo(objRef);
+    int nInstance = ci.getNumberOfDeclaredInstanceFields();
+    int nStatic = ci.getNumberOfStaticFields();
+    int aref = env.newObjectArray("Ljava/lang/reflect/Field;", nInstance + nStatic);
+    int i, j=0;
+    
+    for (i=0; i<nStatic; i++) {
+      FieldInfo fi = ci.getStaticField(i);
+      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
+    }    
+    
+    for (i=0; i<nInstance; i++) {
+      FieldInfo fi = ci.getDeclaredInstanceField(i);
+      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
+    }
+    
+    return aref;
+  }
+  
+  @MJI
+  public int getFields_____3Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef){
+    ClassInfo fci = getInitializedClassInfo(env, FIELD_CLASSNAME);
+    if (fci == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+        
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    // interfaces might not be initialized yet, so we have to check first
+    Set<ClassInfo> ifcs = getInitializedInterfaces( env, ci);
+    if (ifcs == null) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    ArrayList<FieldInfo> fiList = new ArrayList<FieldInfo>();
+    for (; ci != null; ci = ci.getSuperClass()){
+      // the host VM returns them in order of declaration, but the spec says there is no guaranteed order so we keep it simple
+      for (FieldInfo fi : ci.getDeclaredInstanceFields()){
+        if (fi.isPublic()){
+          fiList.add(fi);
+        }
+      }
+      for (FieldInfo fi : ci.getDeclaredStaticFields()){
+        if (fi.isPublic()){
+          fiList.add(fi);
+        }
+      }
+    }
+    
+    for (ClassInfo ciIfc : ifcs){
+      for (FieldInfo fi : ciIfc.getDeclaredStaticFields()){
+        fiList.add(fi); // there are no non-public fields in interfaces
+      }      
+    }
+
+    int aref = env.newObjectArray("Ljava/lang/reflect/Field;", fiList.size());
+    int j=0;
+    for (FieldInfo fi : fiList){
+      env.setReferenceArrayElement(aref, j++, createFieldObject(env, fi, fci));
+    }
+    
+    return aref;
+  }
+    
+  int getField (MJIEnv env, int clsRef, int nameRef, boolean isRecursiveLookup) {    
+    ClassInfo ci = env.getReferredClassInfo( clsRef);
+    String fname = env.getStringObject(nameRef);
+    FieldInfo fi = null;
+    
+    if (isRecursiveLookup) {
+      fi = ci.getInstanceField(fname);
+      if (fi == null) {
+        fi = ci.getStaticField(fname);
+      }      
+    } else {
+        fi = ci.getDeclaredInstanceField(fname);
+        if (fi == null) {
+          fi = ci.getDeclaredStaticField(fname);
+        }
+    }
+    
+    if (fi == null) {      
+      env.throwException("java.lang.NoSuchFieldException", fname);
+      return MJIEnv.NULL;
+      
+    } else {
+      // don't do a Field clinit before we know there is such a field
+      ClassInfo fci = getInitializedClassInfo( env, FIELD_CLASSNAME);
+      if (fci == null) {
+        env.repeatInvocation();
+        return MJIEnv.NULL;
+      }
+      
+      return createFieldObject( env, fi, fci);
+    }
+  }
+  
+  @MJI
+  public int getDeclaredField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
+    return getField(env,clsRef,nameRef, false);
+  }  
+  @MJI
+  public int getField__Ljava_lang_String_2__Ljava_lang_reflect_Field_2 (MJIEnv env, int clsRef, int nameRef) {
+    return getField(env,clsRef,nameRef, true);    
+  }
+
+  @MJI
+  public int getModifiers____I (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    return ci.getModifiers();
+  }
+
+  @MJI
+  public int getEnumConstants_____3Ljava_lang_Object_2 (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    
+    if (env.requiresClinitExecution(ci)){
+      env.repeatInvocation();
+      return 0;
+    }
+
+    if (ci.getSuperClass().getName().equals("java.lang.Enum")) {      
+      ArrayList<FieldInfo> list = new ArrayList<FieldInfo>();
+      String cName = ci.getName();
+      
+      for (FieldInfo fi : ci.getDeclaredStaticFields()) {
+        if (fi.isFinal() && cName.equals(fi.getType())){
+          list.add(fi);
+        }
+      }
+      
+      int aRef = env.newObjectArray(cName, list.size());      
+      StaticElementInfo sei = ci.getStaticElementInfo();
+      int i=0;
+      for (FieldInfo fi : list){
+        env.setReferenceArrayElement( aRef, i++, sei.getReferenceField(fi));
+      }
+      return aRef;
+    }
+    
+    return MJIEnv.NULL;
+  }
+    
+  @MJI
+  public int getInterfaces_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    int aref = MJIEnv.NULL;
+    ThreadInfo ti = env.getThreadInfo();
+    
+    // contrary to the API doc, this only returns the interfaces directly
+    // implemented by this class, not it's bases
+    // <2do> this is not exactly correct, since the interfaces should be ordered
+    Set<ClassInfo> interfaces = ci.getInterfaceClassInfos();
+    aref = env.newObjectArray("Ljava/lang/Class;", interfaces.size());
+
+    int i=0;
+    for (ClassInfo ifc: interfaces){
+      env.setReferenceArrayElement(aref, i++, ifc.getClassObjectRef());
+    }
+    
+    return aref;
+  }
+
+
+  /**
+   * <2do> needs to load from the classfile location, NOT the MJIEnv (native) class
+   *
+   * @author Sebastian Gfeller (sebastian.gfeller@gmail.com)
+   * @author Tihomir Gvero (tihomir.gvero@gmail.com)
+   */
+  @MJI
+  public int getByteArrayFromResourceStream__Ljava_lang_String_2___3B(MJIEnv env, int clsRef, int nameRef) {
+    String name = env.getStringObject(nameRef);
+
+    // <2do> this is not loading from the classfile location! fix it
+    InputStream is = env.getClass().getResourceAsStream(name);
+    if (is == null){
+      return MJIEnv.NULL;
+    }
+    // We assume that the entire input stream can be read at the moment,
+    // although this could break.
+    byte[] content = null;
+    try {
+      content = new byte[is.available()];
+      is.read(content);
+    } catch (IOException e) {
+      throw new RuntimeException(e);
+    }
+    // Now if everything worked, the content should be in the byte buffer.
+    // We put this buffer into the JPF VM.
+    return env.newByteArray(content);
+  }
+
+  @MJI
+  public int getEnclosingClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef) {
+    ClassInfo ciEncl = env.getReferredClassInfo( clsRef).getEnclosingClassInfo();
+    
+    if (ciEncl == null){
+      return MJIEnv.NULL;
+    }
+
+    if (ciEncl.initializeClass(env.getThreadInfo())) {
+      env.repeatInvocation();
+      return 0;
+    }
+
+    return ciEncl.getClassObjectRef();
+  }
+
+  @MJI
+  public int getDeclaredClasses_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    String[] innerClassNames =  ci.getInnerClasses();
+    int aref = MJIEnv.NULL;
+    ThreadInfo ti = env.getThreadInfo();
+    
+    MethodInfo mi = ti.getTopFrame().getPrevious().getMethodInfo();
+    // class of the method that includes the invocation of Class.getDeclaredClasses 
+    ClassInfo cls = mi.getClassInfo();
+
+    // first resolve all the inner classes
+    int length = innerClassNames.length;
+    ClassInfo[] resolvedInnerClass = new ClassInfo[length];
+    for(int i=0; i<length; i++) {
+      try {
+        resolvedInnerClass[i] = cls.resolveReferencedClass(innerClassNames[i]);
+      } catch(LoadOnJPFRequired lre) {
+        env.repeatInvocation();
+        return MJIEnv.NULL;
+      }
+    }
+
+    aref = env.newObjectArray("Ljava/lang/Class;", innerClassNames.length);
+    for (int i=0; i<length; i++){
+      ClassInfo ici = resolvedInnerClass[i];
+      if (!ici.isRegistered()) {
+        ici.registerClass(ti);
+      }
+      env.setReferenceArrayElement(aref, i, ici.getClassObjectRef());
+    }
+    
+    return aref;
+  }
+
+  private String getCanonicalName (ClassInfo ci){
+    if (ci.isArray()){
+      String canonicalName = getCanonicalName(ci.getComponentClassInfo());
+      if (canonicalName != null){
+        return canonicalName + "[]";
+      } else{
+        return null;
+      }
+    }
+    if (isLocalOrAnonymousClass(ci)) {
+      return null;
+    }
+    if (ci.getEnclosingClassInfo() == null){
+      return ci.getName();
+    } else{
+      String enclosingName = getCanonicalName(ci.getEnclosingClassInfo());
+      if (enclosingName == null){ return null; }
+      return enclosingName + "." + ci.getSimpleName();
+    }
+  }
+
+  @MJI
+  public int getCanonicalName____Ljava_lang_String_2 (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    return env.newString(getCanonicalName(ci));
+  }
+
+  @MJI
+  public boolean isAnnotation____Z (MJIEnv env, int clsObjRef){
+    ClassInfo ci = env.getReferredClassInfo(clsObjRef);
+    return (ci.getModifiers() & 0x2000) != 0;
+  }
+  
+  @MJI
+  public boolean isAnnotationPresent__Ljava_lang_Class_2__Z (MJIEnv env, int clsObjRef, int annoClsObjRef){
+    ClassInfo ci = env.getReferredClassInfo(clsObjRef);
+    ClassInfo ciAnno = env.getReferredClassInfo(annoClsObjRef);
+    
+    return ci.getAnnotation( ciAnno.getName()) != null;    
+  }
+  
+  @MJI
+  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo(robj);
+
+    try{
+      return env.newAnnotationProxies(ci.getDeclaredAnnotations());
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public int getEnclosingConstructor____Ljava_lang_reflect_Constructor_2 (MJIEnv env, int robj){
+    ClassInfo mci = getInitializedClassInfo(env, CONSTRUCTOR_CLASSNAME);
+    if (mci == null){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
+
+    if ((enclosingMethod != null) && enclosingMethod.isCtor()){ 
+      return createMethodObject(env, mci, enclosingMethod); 
+    }
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public int getEnclosingMethod____Ljava_lang_reflect_Method_2 (MJIEnv env, int robj){
+    ClassInfo mci = getInitializedClassInfo(env, METHOD_CLASSNAME);
+    if (mci == null){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    MethodInfo enclosingMethod = ci.getEnclosingMethodInfo();
+
+    if ((enclosingMethod != null) && !enclosingMethod.isCtor()){ 
+      return createMethodObject(env, mci, enclosingMethod); 
+    }
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public boolean isAnonymousClass____Z (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    String cname = null;
+    if (ci.getName().contains("$")){
+      cname = ci.getName().substring(ci.getName().lastIndexOf('$') + 1);
+    }
+    return (cname == null) ? false : cname.matches("\\d+?");
+  }
+
+  @MJI
+  public boolean isEnum____Z (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    return ci.isEnum();
+  }
+
+  // Similar to getEnclosingClass() except it returns null for the case of
+  // anonymous class.
+  @MJI
+  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int clsRef){
+    ClassInfo ci = env.getReferredClassInfo(clsRef);
+    if (isLocalOrAnonymousClass(ci)){
+      return MJIEnv.NULL;
+    } else{
+      return getEnclosingClass____Ljava_lang_Class_2(env, clsRef);
+    }
+  }
+
+  @MJI
+  public boolean isLocalClass____Z (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    return isLocalOrAnonymousClass(ci) && !isAnonymousClass____Z(env, robj);
+  }
+
+  private boolean isLocalOrAnonymousClass (ClassInfo ci){
+    return (ci.getEnclosingMethodInfo() != null);
+  }
+
+  @MJI
+  public boolean isMemberClass____Z (MJIEnv env, int robj){
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    return (ci.getEnclosingClassInfo() != null) && !isLocalOrAnonymousClass(ci);
+  }
+
+  /**
+   * Append the package name prefix of the class represented by robj, if the name is not 
+   * absolute. OW, remove leading "/". 
+   */
+  @MJI
+  public int getResolvedName__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int robj, int resRef){
+    String rname = env.getStringObject(resRef);
+    ClassInfo ci = env.getReferredClassInfo(robj);
+    if (rname == null) {
+      return MJIEnv.NULL;
+    }
+    if (!rname.startsWith("/")) {
+      ClassInfo c = ci;
+      while (c.isArray()) {
+          c = c.getComponentClassInfo();
+      }
+      String baseName = c.getName();
+      int index = baseName.lastIndexOf('.');
+      if (index != -1) {
+        rname = baseName.substring(0, index).replace('.', '/')
+            +"/"+rname;
+      }
+    } else {
+        rname = rname.substring(1);
+    }
+
+    return env.newString(rname);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_ClassLoader.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_ClassLoader.java
new file mode 100644 (file)
index 0000000..4fd6a25
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.util.Map;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassPath;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ClassInfoException;
+import gov.nasa.jpf.vm.ClinitRequired;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Native peer for java.lang.ClassLoader
+ */
+public class JPF_java_lang_ClassLoader extends NativePeer {
+
+  @MJI
+  public void $init____V (MJIEnv env, int objRef) {
+    ClassLoaderInfo systemCl = ClassLoaderInfo.getCurrentSystemClassLoader();
+    $init__Ljava_lang_ClassLoader_2__V (env, objRef, systemCl.getClassLoaderObjectRef());
+  }
+
+  @MJI
+  public void $init__Ljava_lang_ClassLoader_2__V (MJIEnv env, int objRef, int parentRef) {
+    Heap heap = env.getHeap();
+
+    //--- Retrieve the parent ClassLoaderInfo
+    ClassLoaderInfo parent = env.getClassLoaderInfo(parentRef);
+
+    //--- create the internal representation of the classloader
+    ClassLoaderInfo cl = new ClassLoaderInfo(env.getVM(), objRef, new ClassPath(), parent);
+
+    //--- initialize the java.lang.ClassLoader object
+    ElementInfo ei = heap.getModifiable(objRef);
+    ei.setIntField( ClassLoaderInfo.ID_FIELD, cl.getId());
+
+    // we should use the following block if we ever decide to make systemClassLoader 
+    // unavailable if(parent.isSystemClassLoader) {
+    //  // we don't want to make the systemCLassLoader available through SUT
+    //  parentRef = MJIEnv.NULL;
+    // }
+
+    ei.setReferenceField("parent", parentRef);
+  }
+
+  @MJI
+  public int getSystemClassLoader____Ljava_lang_ClassLoader_2 (MJIEnv env, int clsObjRef) {
+    return ClassLoaderInfo.getCurrentSystemClassLoader().getClassLoaderObjectRef();
+  }
+
+  @MJI
+  public int getResource0__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef){
+    String rname = env.getStringObject(resRef);
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    String resourcePath = cl.findResource(rname);
+
+    return env.newString(resourcePath);
+  }
+
+  @MJI
+  public int getResources0__Ljava_lang_String_2___3Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef) {
+    String rname = env.getStringObject(resRef);
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    String[] resources = cl.findResources(rname);
+
+    return env.newStringArray(resources);
+  }
+
+  @MJI
+  public int findLoadedClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objRef, int nameRef) {
+    String cname = env.getStringObject(nameRef);
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    ClassInfo ci = cl.getAlreadyResolvedClassInfo(cname);
+    if(ci != null) {
+      return ci.getClassObjectRef();
+    }
+
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public int findSystemClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objRef, int nameRef) {
+    String cname = env.getStringObject(nameRef);
+
+    checkForIllegalName(env, cname);
+    if(env.hasException()) {
+      return MJIEnv.NULL;
+    }
+
+    ClassLoaderInfo cl = ClassLoaderInfo.getCurrentSystemClassLoader();
+
+    ClassInfo ci = cl.getResolvedClassInfo(cname);
+
+    if(!ci.isRegistered()) {
+      ci.registerClass(env.getThreadInfo());
+    }
+
+    return ci.getClassObjectRef();
+  }
+
+  @MJI
+  public int defineClass0__Ljava_lang_String_2_3BII__Ljava_lang_Class_2 
+                                      (MJIEnv env, int objRef, int nameRef, int bufferRef, int offset, int length) {
+    String cname = env.getStringObject(nameRef);
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    // determine whether that the corresponding class is already defined by this 
+    // classloader, if so, this attempt is invalid, and loading throws a LinkageError
+    if (cl.getDefinedClassInfo(cname) != null) {  // attempt to define twice
+      env.throwException("java.lang.LinkageError"); 
+      return MJIEnv.NULL;
+    }
+        
+    byte[] buffer = env.getByteArrayObject(bufferRef);
+    
+    try {
+      ClassInfo ci = cl.getResolvedClassInfo( cname, buffer, offset, length);
+
+      // Note: if the representation is not of a supported major or minor version, loading 
+      // throws an UnsupportedClassVersionError. But for now, we do not check for this here 
+      // since we don't do much with minor and major versions
+
+      ThreadInfo ti = env.getThreadInfo();
+      ci.registerClass(ti);
+
+      return ci.getClassObjectRef();
+      
+    } catch (ClassInfoException cix){
+      env.throwException("java.lang.ClassFormatError");
+      return MJIEnv.NULL;
+    }
+  }
+
+
+  protected static boolean check(MJIEnv env, String cname, byte[] buffer, int offset, int length) {
+    // throw SecurityExcpetion if the package prefix is java
+    checkForProhibitedPkg(env, cname);
+
+    // throw NoClassDefFoundError if the given class does name might 
+    // not be a valid binary name
+    checkForIllegalName(env, cname);
+
+    // throw IndexOutOfBoundsException if buffer length is not consistent
+    // with offset
+    checkData(env, buffer, offset, length);
+
+    return !env.hasException();
+  }
+
+  protected static void checkForProhibitedPkg(MJIEnv env, String name) {
+    if(name != null && name.startsWith("java.")) {
+      env.throwException("java.lang.SecurityException", "Prohibited package name: " + name);
+    }
+  }
+
+  protected static void checkForIllegalName(MJIEnv env, String name) {
+    if((name == null) || (name.length() == 0)) {
+      return;
+    }
+
+    if((name.indexOf('/') != -1) || (name.charAt(0) == '[')) {
+      env.throwException("java.lang.NoClassDefFoundError", "IllegalName: " + name);
+    }
+  }
+
+  protected static void checkData(MJIEnv env, byte[] buffer, int offset, int length) {
+    if(offset<0 || length<0 || offset+length > buffer.length) {
+      env.throwException("java.lang.IndexOutOfBoundsException");
+    }
+  }
+
+  static String pkg_class_name = "java.lang.Package";
+
+  @MJI
+  public int getPackages_____3Ljava_lang_Package_2 (MJIEnv env, int objRef) {
+    ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
+    ClassInfo pkgClass = null; 
+    try {
+      pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    // Returns all of the Packages defined by this class loader and its ancestors
+    Map<String, ClassLoaderInfo> pkgs = cl.getPackages();
+    int size = pkgs.size();
+    // create an array of type java.lang.Package
+    int pkgArr = env.newObjectArray(pkg_class_name, size);
+
+    int i = 0;
+    for(String name: cl.getPackages().keySet()) {
+      int pkgRef = createPackageObject(env, pkgClass, name, cl);
+      // place the object into the array
+      env.setReferenceArrayElement(pkgArr, i++, pkgRef);
+    }
+
+    return pkgArr;
+  }
+
+  @MJI
+  public int getPackage__Ljava_lang_String_2__Ljava_lang_Package_2 (MJIEnv env, int objRef, int nameRef) {
+    ClassLoaderInfo sysLoader = ClassLoaderInfo.getCurrentSystemClassLoader();
+
+    ClassInfo pkgClass = null; 
+    try {
+      pkgClass = sysLoader.getInitializedClassInfo(pkg_class_name, env.getThreadInfo());
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    String pkgName = env.getStringObject(nameRef);
+    if(cl.getPackages().get(pkgName)!=null) {
+      return createPackageObject(env, pkgClass, pkgName, cl);
+    } else {
+      return MJIEnv.NULL;
+    }
+  }
+
+  public static int createPackageObject(MJIEnv env, ClassInfo pkgClass, String pkgName, ClassLoaderInfo cl) {
+    int pkgRef = env.newObject(pkgClass);
+    ElementInfo ei = env.getModifiableElementInfo(pkgRef);
+
+    ei.setReferenceField("pkgName", env.newString(pkgName));
+    ei.setReferenceField("loader", cl.getClassLoaderObjectRef());
+    // the rest of the fields set to some dummy value
+    ei.setReferenceField("specTitle", env.newString("spectitle"));
+    ei.setReferenceField("specVersion", env.newString("specversion"));
+    ei.setReferenceField("specVendor", env.newString("specvendor"));
+    ei.setReferenceField("implTitle", env.newString("impltitle"));
+    ei.setReferenceField("implVersion", env.newString("implversion"));
+    ei.setReferenceField("sealBase", MJIEnv.NULL);
+
+    return pkgRef;
+  }
+
+  @MJI
+  public void setDefaultAssertionStatus__Z__V (MJIEnv env, int objRef, boolean enabled) {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    cl.setDefaultAssertionStatus(enabled);
+  }
+
+  @MJI
+  public void setPackageAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    String pkgName = env.getStringObject(strRef);
+    cl.setPackageAssertionStatus(pkgName, enabled);
+  }
+
+  @MJI
+  public void setClassAssertionStatus__Ljava_lang_String_2Z__V (MJIEnv env, int objRef, int strRef, boolean enabled) {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    String clsName = env.getStringObject(strRef);
+    cl.setClassAssertionStatus(clsName, enabled);
+  }
+
+  @MJI
+  public void clearAssertionStatus____V (MJIEnv env, int objRef) {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    cl.clearAssertionStatus();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Double.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Double.java
new file mode 100644 (file)
index 0000000..c106848
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Double library abstraction
+ */
+public class JPF_java_lang_Double extends NativePeer {
+  @MJI
+  public long doubleToLongBits__D__J (MJIEnv env, int rcls, double v0) {
+    return Double.doubleToLongBits(v0);
+  }
+
+  @MJI
+  public long doubleToRawLongBits__D__J (MJIEnv env, int rcls, double v0) {
+    return Double.doubleToRawLongBits(v0);
+  }
+
+  @MJI
+  public double longBitsToDouble__J__D (MJIEnv env, int rcls, long v0) {
+    return Double.longBitsToDouble(v0);
+  }
+
+  @MJI
+  public int toString__D__Ljava_lang_String_2 (MJIEnv env, int objref, double d) {
+    return env.newString(Double.toString(d));
+  }
+  
+  // we need to intercept this because it compares double values, which might
+  // cause an ArithmeticException to be raised if -check-fp-compare is set (default)
+  // but -check-fp isn't, and Double.isInfinit is used to handle the cases
+  // explicitly in the program (which is supposed to be the right way)
+  @MJI
+  public boolean isInfinite__D__Z (MJIEnv env, int rcls, double v) {
+    return Double.isInfinite(v);
+  }
+  
+  // ditto (see isInfinite)
+  @MJI
+  public boolean isNaN__D__Z (MJIEnv env, int rcls, double v) {
+    return Double.isNaN(v);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Float.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Float.java
new file mode 100644 (file)
index 0000000..fa378f0
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Float library abstraction
+ */
+public class JPF_java_lang_Float extends NativePeer {
+  @MJI
+  public int floatToIntBits__F__I (MJIEnv env, int rcls, float v0) {
+    return Float.floatToIntBits(v0);
+  }
+
+  @MJI
+  public int floatToRawIntBits__F__I (MJIEnv env, int rcls, float v0) {
+    return Float.floatToRawIntBits(v0);
+  }
+
+  @MJI
+  public float intBitsToFloat__I__F (MJIEnv env, int rcls, int v0) {
+    return Float.intBitsToFloat(v0);
+  }
+  
+  // we need to intercept this because it compares double values, which might
+  // cause an ArithmeticException to be raised if -check-fp-compare is set (default)
+  // but -check-fp isn't, and Double.isInfinit is used to handle the cases
+  // explicitly in the program (which is supposed to be the right way)
+  @MJI
+  public boolean isInfinite__F__Z (MJIEnv env, int rcls, float v) {
+    return Float.isInfinite(v);
+  }
+  
+  // ditto (see isInfinite)
+  @MJI
+  public boolean isNaN__F__Z (MJIEnv env, int rcls, float v) {
+    return Float.isNaN(v);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Integer.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Integer.java
new file mode 100644 (file)
index 0000000..225d152
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Integer library abstraction
+ */
+public class JPF_java_lang_Integer extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public int parseInt__Ljava_lang_String_2__I (MJIEnv env, int clsObjRef, 
+                                                   int strRef) {
+    try {
+      return Integer.parseInt(env.getStringObject(strRef));
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public int parseInt__Ljava_lang_String_2I__I (MJIEnv env, int clsObjRef, 
+                                                    int strRef, int radix) {
+    try {
+      return Integer.parseInt(env.getStringObject(strRef), radix);
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public int toBinaryString__I__Ljava_lang_String_2 (MJIEnv env, int objref, int val) {
+    return env.newString(Integer.toBinaryString(val));
+  }
+
+  @MJI
+  public int toHexString__I__Ljava_lang_String_2 (MJIEnv env, int objref, int val) {
+    return env.newString(Integer.toHexString(val));
+  }
+
+  @MJI
+  public int toOctalString__I__Ljava_lang_String_2 (MJIEnv env, int objref, int val) {
+    return env.newString(Integer.toOctalString(val));
+  }
+
+  @MJI
+  public int toString__I__Ljava_lang_String_2 (MJIEnv env, int objref, int val) {
+    return env.newString(Integer.toString(val));
+  }
+
+  @MJI
+  public int toString__II__Ljava_lang_String_2 (MJIEnv env, int objref, int val, int radix) {
+    return env.newString(Integer.toString(val, radix));
+  }
+
+  @MJI
+  public int valueOf__I__Ljava_lang_Integer_2 (MJIEnv env, int clsRef, int val) {
+    return env.valueOfInteger(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Long.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Long.java
new file mode 100644 (file)
index 0000000..2f4209c
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Long library abstraction
+ */
+public class JPF_java_lang_Long extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public long parseLong__Ljava_lang_String_2I__J (MJIEnv env, int clsObjRef, 
+                                                     int strRef, int radix) {
+    try {
+      return Long.parseLong(env.getStringObject(strRef), radix);
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public long parseLong__Ljava_lang_String_2__J (MJIEnv env, int clsObjRef, 
+                                                     int strRef) {
+    try {
+      return Long.parseLong(env.getStringObject(strRef));
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public int toBinaryString__J__Ljava_lang_String_2 (MJIEnv env, int objref, long val) {
+    return env.newString(Long.toBinaryString(val));
+  }
+
+  @MJI
+  public int toHexString__J__Ljava_lang_String_2 (MJIEnv env, int objref, long val) {
+    return env.newString(Long.toHexString(val));
+  }
+
+  @MJI
+  public int toOctalString__J__Ljava_lang_String_2 (MJIEnv env, int objref, long val) {
+    return env.newString(Long.toOctalString(val));
+  }
+
+  @MJI
+  public int toString__J__Ljava_lang_String_2 (MJIEnv env, int objref, long val) {
+    return env.newString(Long.toString(val));
+  }
+
+  @MJI
+  public int toString__JI__Ljava_lang_String_2 (MJIEnv env, int objref, long val, int radix) {
+    return env.newString(Long.toString(val, radix));
+  }
+
+  @MJI
+  public int valueOf__J__Ljava_lang_Long_2 (MJIEnv env, int clsRef, long val) {
+    return env.valueOfLong(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Math.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Math.java
new file mode 100644 (file)
index 0000000..ca80021
--- /dev/null
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Math library abstraction
+ */
+public class JPF_java_lang_Math extends NativePeer {
+  
+  // <2do> those are here to hide their implementation from traces, not to
+  // increase performance. If we want to do that, we should probably inline
+  // their real implementation here, instead of delegating (just a compromise)
+  @MJI
+  public double abs__D__D (MJIEnv env, int clsObjRef, double a) {
+    // return Math.abs(a);
+    
+    return (a <= .0) ? (.0 - a) : a;
+  }
+
+  @MJI
+  public float abs__F__F (MJIEnv env, int clsObjRef, float a) {
+    return Math.abs(a);
+  }
+
+  @MJI
+  public int abs__I__I (MJIEnv env, int clsObjRef, int a) {
+    //return Math.abs(a);
+    return (a < 0) ? -a : a; // that's probably slightly faster
+  }
+
+  @MJI
+  public long abs__J__J (MJIEnv env, int clsObjRef, long a) {
+    //return Math.abs(a);
+    
+    return (a < 0) ? -a : a;
+  }
+
+  @MJI
+  public double max__DD__D (MJIEnv env, int clsObjRef, double a, double b) {
+    // that one has to handle inexact numbers, so it's probably not worth the hassle
+    // to inline it
+    return Math.max(a, b);
+  }
+
+  @MJI
+  public float max__FF__F (MJIEnv env, int clsObjRef, float a, float b) {
+    return Math.max(a, b);
+  }
+
+  @MJI
+  public int max__II__I (MJIEnv env, int clsObjRef, int a, int b) {
+    //return Math.max(a, b);
+    
+    return (a >= b) ? a : b;
+  }
+
+  @MJI
+  public long max__JJ__J (MJIEnv env, int clsObjRef, long a, long b) {
+    //return Math.max(a, b);
+    return (a >= b) ? a : b;
+  }
+
+  @MJI
+  public double min__DD__D (MJIEnv env, int clsObjRef, double a, double b) {
+    return Math.min(a, b);
+  }
+
+  @MJI
+  public float min__FF__F (MJIEnv env, int clsObjRef, float a, float b) {
+    return Math.min(a, b);
+  }
+
+  @MJI
+  public int min__II__I (MJIEnv env, int clsObjRef, int a, int b) {
+    return Math.min(a, b);
+  }
+
+  @MJI
+  public long min__JJ__J (MJIEnv env, int clsObjRef, long a, long b) {
+    return Math.min(a, b);
+  }
+
+  @MJI
+  public double pow__DD__D (MJIEnv env, int clsObjRef, double a, double b) {
+    return Math.pow(a, b);
+  }
+
+  @MJI
+  public double sqrt__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.sqrt(a);
+  }
+  
+  @MJI
+  public double random____D (MJIEnv env, int clsObjRef) {
+    return Math.random();
+  }
+  
+  @MJI
+  public long round__D__J (MJIEnv env, int clsObjRef, double a){
+    return Math.round(a);
+  }
+  
+  @MJI
+  public double exp__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.exp(a);
+  }
+  
+  @MJI
+  public double asin__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.asin(a);
+  }
+
+  @MJI
+  public double acos__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.acos(a);
+  }
+  
+  @MJI
+  public double atan__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.atan(a);
+  }
+  
+  @MJI
+  public double atan2__DD__D (MJIEnv env, int clsObjRef, double a, double b) {
+    return Math.atan2(a,b);
+  }
+  
+  @MJI
+  public double ceil__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.ceil(a);
+  }
+  
+  @MJI
+  public double cos__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.cos(a);
+  }
+  
+  @MJI
+  public double floor__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.floor(a);
+  }
+  
+  @MJI
+  public double log10__D__D (MJIEnv env, int clsObjRef, double a) {
+       return Math.log10(a);
+  }  
+  
+  @MJI
+  public double log__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.log(a);
+  }
+  
+  @MJI
+  public double rint__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.rint(a);
+  }
+  
+  @MJI
+  public double sin__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.sin(a);
+  }
+  
+  @MJI
+  public double tan__D__D (MJIEnv env, int clsObjRef, double a) {
+    return Math.tan(a);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Object.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Object.java
new file mode 100644 (file)
index 0000000..4f2f9ea
--- /dev/null
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Types;
+
+
+/**
+ * MJI NativePeer class for java.lang.Object library abstraction
+ */
+public class JPF_java_lang_Object extends NativePeer {
+  
+  @MJI
+  public int getClass____Ljava_lang_Class_2 (MJIEnv env, int objref) {
+    ClassInfo oci = env.getClassInfo(objref);
+
+    return oci.getClassObjectRef();
+  }
+
+  @MJI
+  public int clone____Ljava_lang_Object_2 (MJIEnv env, int objref) {
+    Heap heap = env.getHeap();
+    ElementInfo ei = heap.get(objref);
+    ClassInfo ci = ei.getClassInfo();
+    ElementInfo eiClone = null;
+    
+    if (!ci.isInstanceOf("java.lang.Cloneable")) {
+      env.throwException("java.lang.CloneNotSupportedException",
+          ci.getName() + " does not implement java.lang.Cloneable.");
+      return MJIEnv.NULL;  // meaningless
+      
+    } else {
+      int newref;
+      if (ci.isArray()) {
+        ClassInfo cci = ci.getComponentClassInfo();
+        
+        String componentType;
+        if (cci.isPrimitive()){
+          componentType = Types.getTypeSignature(cci.getName(),false);
+        } else {
+          componentType = cci.getType();
+        }
+
+        eiClone = heap.newArray(componentType, ei.arrayLength(), env.getThreadInfo());
+        
+      } else {
+        eiClone = heap.newObject(ci, env.getThreadInfo());
+      }
+      
+      // Ok, this is nasty but efficient
+      eiClone.fields = ei.getFields().clone();
+
+      return eiClone.getObjectRef();
+    }
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objref) {
+    return (objref ^ 0xABCD);
+  }
+
+  protected void wait0 (MJIEnv env, int objref, long timeout) {
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = env.getModifiableElementInfo(objref);
+    
+    if (!ti.isFirstStepInsn()) {
+      if (!ei.isLockedBy(ti)) {
+        env.throwException("java.lang.IllegalMonitorStateException", "wait() without holding lock");
+        return;
+      }
+
+      if (ti.isInterrupted(true)) {
+        env.throwException("java.lang.InterruptedException");
+      } else {
+        ei.wait(ti, timeout); // block
+      }
+    }
+    
+    // scheduling point
+    if (ti.getScheduler().setsWaitCG(ti, timeout)) {
+      env.repeatInvocation();
+      return;
+    }
+    
+    // bottom half, unblock
+    switch (ti.getState()) {
+      case WAITING:
+      case TIMEOUT_WAITING:
+        throw new JPFException("blocking wait() without transition break");      
+      
+      // we can get here by direct call from ...Unsafe.park__ZJ__V()
+      // which aquires the park lock and waits natively
+      case RUNNING:
+
+      // note that we can't get here if we are in NOTIFIED or INTERRUPTED state,
+      // since we still have to reacquire the lock
+      case UNBLOCKED:
+      case TIMEDOUT: // nobody else acquired the lock
+        // thread status set by explicit notify() call
+        env.lockNotified(objref);
+
+        if (ti.isInterrupted(true)) {
+          env.throwException("java.lang.InterruptedException");
+        }
+        break;
+
+      default:
+        throw new JPFException("invalid thread state of: " + ti.getName() + " is " + ti.getStateName()
+                + " while waiting on " + ei);
+    }
+  }
+  
+  // we intercept them both so that we don't get the java.lang.Object.wait() location
+  // as the blocking insn
+  @MJI
+  public void wait____V (MJIEnv env, int objref){
+    wait0(env,objref,0);
+  }
+  
+  @MJI
+  public void wait__J__V (MJIEnv env, int objref, long timeout) {
+    wait0(env,objref,timeout);
+  }
+
+  @MJI
+  public void wait__JI__V (MJIEnv env, int objref, long timeout, int nanos) {
+    wait0(env,objref,timeout);
+  }
+
+  
+  @MJI
+  public void notify____V (MJIEnv env, int objref) {
+    boolean didNotify = false;
+    ThreadInfo ti = env.getThreadInfo();
+    
+    if (!ti.isFirstStepInsn()) {
+      ElementInfo ei = env.getModifiableElementInfo(objref);
+      if (!ei.isLockedBy(ti)) {
+        env.throwException("java.lang.IllegalMonitorStateException", "notify() without holding lock");
+        return;
+      }
+      
+      didNotify = env.notify(ei);
+    }
+    
+    if (ti.getScheduler().setsNotifyCG(ti, didNotify)){
+      env.repeatInvocation();
+      return;
+    }
+  }
+
+  @MJI
+  public void notifyAll____V (MJIEnv env, int objref) {
+    boolean didNotify = false;
+    ThreadInfo ti = env.getThreadInfo();
+    
+    if (!ti.isFirstStepInsn()) {
+      ElementInfo ei = env.getModifiableElementInfo(objref);
+      if (!ei.isLockedBy(ti)) {
+        env.throwException("java.lang.IllegalMonitorStateException", "notifyAll() without holding lock");
+        return;
+      }
+      
+      didNotify = env.notifyAll(ei);
+    }
+    
+    if (ti.getScheduler().setsNotifyCG(ti, didNotify)){
+      env.repeatInvocation();
+      return;
+    }
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    ClassInfo ci = env.getClassInfo(objref);
+    int hc = hashCode____I(env,objref);
+    
+    String s = ci.getName() + '@' + hc;
+    int sref = env.newString(s);
+    return sref;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Runtime.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Runtime.java
new file mode 100644 (file)
index 0000000..8b4ea51
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.JPF_gov_nasa_jpf_vm_Verify;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * just a dummy for now, to avoid UnsatisfiedLinkErrors
+ */
+public class JPF_java_lang_Runtime extends NativePeer {
+
+  @MJI
+  public void addShutdownHook__Ljava_lang_Thread_2__V (MJIEnv env, int objref, int threadRef) {
+    // ignored for now
+  }
+
+  @MJI
+  public long totalMemory____J (MJIEnv env, int objref) {
+    // not really sure what to return here, since in reality this
+    // value can be non-deterministic
+    return 50000000;
+  }
+
+  @MJI
+  public long maxMemory____J (MJIEnv env, int objref) {
+    // yet another cut
+    return 70000000;
+  }
+
+  @MJI
+  public long freeMemory____J (MJIEnv env, int objref) {
+    // we don't have an upper limit for our heap space, and we don't
+    // keep track how much is used, so we just return a dummy
+    
+    // we could loop over the areas calling getHeapSize() on
+    // ElementInfos, but since we don't have a max, what would
+    // that be good for?
+    
+    return 10000000;
+  }
+
+  @MJI
+  public void gc____V (MJIEnv env, int objref){
+    env.gc();
+  }
+  
+  @MJI
+  public int availableProcessors____I (MJIEnv env, int objref){
+    // this is what all these Runtime data acquisition APIs should look like
+    // - since the value is oing to be used by the application, there should
+    // be a way to vary it with a CG
+    
+    Config conf = env.getConfig();
+    int maxProcessors = conf.getInt("cg.max_processors", 1);
+    
+    if (maxProcessors == 1) {
+      return 1;
+    } else {
+      return JPF_gov_nasa_jpf_vm_Verify.getInt__II__I(env,-1, 1,maxProcessors);
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Short.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Short.java
new file mode 100644 (file)
index 0000000..932882e
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * JPF_java_lang_Shortjava.java
+ *
+ * @author Created by Omnicore CodeGuide
+ */
+package gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * MJI NativePeer class for java.lang.Short library abstraction
+ */
+public class JPF_java_lang_Short extends NativePeer {
+  // <2do> at this point we deliberately do not override clinit
+
+  @MJI
+  public short parseShort__Ljava_lang_String_2__S (MJIEnv env, 
+                                                          int clsObjRef, 
+                                                          int strRef) {
+    try {
+      return Short.parseShort(env.getStringObject(strRef));
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public short parseShort__Ljava_lang_String_2I__S (MJIEnv env, 
+                                                            int clsObjRef, 
+                                                            int strRef, int radix) {
+    try {
+      return Short.parseShort(env.getStringObject(strRef), radix);
+    } catch (NumberFormatException e) {
+      env.throwException("java.lang.NumberFormatException");
+
+      return 0;
+    }
+  }
+
+  @MJI
+  public int toString__S__Ljava_lang_String_2 (MJIEnv env, int objref, short val) {
+    return env.newString(Short.toString(val));
+  }
+
+  @MJI
+  public int valueOf__S__Ljava_lang_Short_2 (MJIEnv env, int clsRef, short val) {
+    return env.valueOfShort(val);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_String.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_String.java
new file mode 100644 (file)
index 0000000..ba84851
--- /dev/null
@@ -0,0 +1,619 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.CharArrayFields;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Fields;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Locale;
+
+/**
+ * MJI NativePeer class for java.lang.String library abstraction
+ */
+public class JPF_java_lang_String extends NativePeer {
+
+  
+  @MJI
+  public int init___3CII__Ljava_lang_String_2 (MJIEnv env, int objRef, int valueRef, int offset, int count) {
+    char[] value = env.getCharArrayObject(valueRef);
+    String result = new String(value, offset, count);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int init___3III__Ljava_lang_String_2 (MJIEnv env, int objRef, int codePointsRef, int offset, int count) {
+    int[] codePoints = env.getIntArrayObject(codePointsRef);
+    String result = new String(codePoints, offset, count);
+    return env.newString(result);
+  }
+
+  @SuppressWarnings("deprecation")
+  @MJI
+  public int init___3BIII__Ljava_lang_String_2 (MJIEnv env, int objRef, int asciiRef, int hibyte, int offset, int count) {
+    byte[] ascii = env.getByteArrayObject(asciiRef);
+    String result = new String(ascii, hibyte, offset, count);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int init___3BIILjava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int bytesRef, int offset, int length, int charsetNameRef) throws UnsupportedEncodingException {
+    byte[] bytes = env.getByteArrayObject(bytesRef);
+    String charsetName = env.getStringObject(charsetNameRef);
+    String result = new String(bytes, offset, length, charsetName);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int init___3BII__Ljava_lang_String_2 (MJIEnv env, int objRef, int bytesRef, int offset, int length) {
+    byte[] bytes = env.getByteArrayObject(bytesRef);
+    String result = new String(bytes, offset, length);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int codePointAt__I__I (MJIEnv env, int objRef, int index) {
+    String obj = env.getStringObject(objRef);
+    return obj.codePointAt(index);
+  }
+
+  @MJI
+  public int codePointBefore__I__I (MJIEnv env, int objRef, int index) {
+    String obj = env.getStringObject(objRef);
+    return obj.codePointBefore(index);
+  }
+
+  @MJI
+  public int codePointCount__II__I (MJIEnv env, int objRef, int beginIndex, int endIndex) {
+    String obj = env.getStringObject(objRef);
+    return obj.codePointCount(beginIndex, endIndex);
+  }
+
+  @MJI
+  public int offsetByCodePoints__II__I (MJIEnv env, int objRef, int index, int codePointOffset) {
+    String obj = env.getStringObject(objRef);
+    return obj.offsetByCodePoints(index, codePointOffset);
+  }
+
+  @MJI
+  public void getChars__II_3CI__V (MJIEnv env, int objRef, int srcBegin, int srcEnd, int dstRef, int dstBegin) {
+    String obj = env.getStringObject(objRef);
+    char[] dst = env.getCharArrayObject(dstRef);
+    obj.getChars(srcBegin, srcEnd, dst, dstBegin);
+  }
+
+  @MJI
+  public void getChars___3CI__V(MJIEnv env, int objRef, int dstRef, int dstBegin) {
+    String obj = env.getStringObject(objRef);
+    char[] dst = env.getCharArrayObject(dstRef);
+    obj.getChars(0, obj.length(), dst, dstBegin);
+  }
+  
+  @SuppressWarnings("deprecation")
+  @MJI
+  public void getBytes__II_3BI__V (MJIEnv env, int objRef, int srcBegin, int srcEnd, int dstRef, int dstBegin) {
+    String obj = env.getStringObject(objRef);
+    byte[] dst = env.getByteArrayObject(dstRef);
+    obj.getBytes(srcBegin, srcEnd, dst, dstBegin);
+  }
+
+  @MJI
+  public int getBytes__Ljava_lang_String_2___3B (MJIEnv env, int objRef, int charSetRef) {
+    String string = env.getStringObject(objRef);
+    String charset = env.getStringObject(charSetRef);
+
+    try {
+      byte[] b = string.getBytes(charset);
+      return env.newByteArray(b);
+
+    } catch (UnsupportedEncodingException uex) {
+      env.throwException(uex.getClass().getName(), uex.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public int getBytes_____3B (MJIEnv env, int objRef) {
+    String obj = env.getStringObject(objRef);
+    byte[] bytes = obj.getBytes();
+    return env.newByteArray(bytes);
+  }
+
+  @MJI
+  public char charAt__I__C (MJIEnv env, int objRef, int index){
+    char[] data = env.getStringChars(objRef);
+    return data[index];
+  }
+
+  
+  @MJI
+  public boolean equals0___3C_3CI__Z (MJIEnv env, int clsObjRef, int charsRef1, int charsRef2, int len) {
+
+    if ((charsRef1 == MJIEnv.NULL) || (charsRef2 == MJIEnv.NULL)) { return false; }
+
+    char[] a = env.getCharArrayObject(charsRef1);
+    char[] b = env.getCharArrayObject(charsRef2);
+
+    if (a.length < len || b.length < len) { return false; }
+
+    for (int i = 0; i < len; i++) {
+      if (a[i] != b[i]) { return false; }
+    }
+
+    return true;
+  }
+
+  @MJI
+  public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int argRef) {
+    if (argRef == MJIEnv.NULL) { 
+      return false; 
+    }
+
+    Heap heap = env.getHeap();
+    ElementInfo s1 = heap.get(objRef);
+    ClassInfo ci1 = s1.getClassInfo();
+    
+    ElementInfo s2 = heap.get(argRef);
+    ClassInfo ci2 = s2.getClassInfo();
+   
+    if (!ci2.isInstanceOf(ci1)) { 
+      return false;
+    }
+
+    Fields f1 = heap.get(s1.getReferenceField("value")).getFields();
+    Fields f2 = heap.get(s2.getReferenceField("value")).getFields();
+
+    char[] c1 = ((CharArrayFields) f1).asCharArray();
+    char[] c2 = ((CharArrayFields) f2).asCharArray();
+
+    if (c1.length != c2.length) { 
+      return false; 
+    }
+
+    for (int i = 0; i < c1.length; i++) {
+      if (c1[i] != c2[i]) { 
+        return false; 
+      }
+    }
+
+    return true;
+  }
+
+  @MJI
+  public boolean equalsIgnoreCase__Ljava_lang_String_2__Z (MJIEnv env, int objref, int anotherString) {
+    String thisString = env.getStringObject(objref);
+    if (anotherString != MJIEnv.NULL) {
+      return thisString.equalsIgnoreCase(env.getStringObject(anotherString));
+    } else {
+      return false;
+    }
+  }
+
+  @MJI
+  public int compareTo__Ljava_lang_String_2__I (MJIEnv env, int objRef, int anotherStringRef) {
+    String obj = env.getStringObject(objRef);
+    String anotherString = env.getStringObject(anotherStringRef);
+    return obj.compareTo(anotherString);
+  }
+
+  @MJI
+  public int MJIcompare__Ljava_lang_String_2Ljava_lang_String_2__I (MJIEnv env, int clsRef, int s1Ref, int s2Ref) {
+    // Is there a way to reflect?
+    String a = env.getStringObject(s1Ref);
+    String s2 = env.getStringObject(s2Ref);
+    int n1 = a.length();
+    int n2 = s2.length();
+    int min = Math.min(n1, n2);
+    for (int i = 0; i < min; i++) {
+      char x = a.charAt(i);
+      char y = s2.charAt(i);
+      if (x != y) {
+        x = Character.toUpperCase(x);
+        y = Character.toUpperCase(y);
+        if (x != y) {
+          x = Character.toLowerCase(x);
+          y = Character.toLowerCase(y);
+          if (x != y) { return x - y; }
+        }
+      }
+    }
+    return n1 - n2;
+  }
+
+  @MJI
+  public boolean regionMatches__ILjava_lang_String_2II__Z (MJIEnv env, int objRef, int toffset, int otherRef, int ooffset, int len) {
+    String obj = env.getStringObject(objRef);
+    String other = env.getStringObject(otherRef);
+    return obj.regionMatches(toffset, other, ooffset, len);
+
+  }
+
+  @MJI
+  public boolean regionMatches__ZILjava_lang_String_2II__Z (MJIEnv env, int objRef, boolean ignoreCase, int toffset, int otherRef, int ooffset, int len) {
+    String obj = env.getStringObject(objRef);
+    String other = env.getStringObject(otherRef);
+    return obj.regionMatches(ignoreCase, toffset, other, ooffset, len);
+
+  }
+
+  @MJI
+  public boolean startsWith__Ljava_lang_String_2I__Z (MJIEnv env, int objRef, int prefixRef, int toffset) {
+    String thisStr = env.getStringObject(objRef);
+    String prefix = env.getStringObject(prefixRef);
+    return thisStr.startsWith(prefix, toffset);
+  }
+
+  @MJI
+  public boolean startsWith__Ljava_lang_String_2__Z (MJIEnv env, int objRef, int prefixRef) {
+    String thisStr = env.getStringObject(objRef);
+    String prefix = env.getStringObject(prefixRef);
+    return thisStr.startsWith(prefix);
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objref) {
+    ElementInfo ei = env.getElementInfo(objref);
+    int h = ei.getIntField("hash");
+
+    if (h == 0) {
+      int vref = env.getReferenceField(objref, "value");
+
+      // now get the char array data, but be aware they are stored as ints
+      ElementInfo eiVal = env.getElementInfo(vref);
+      char[] values = eiVal.asCharArray();
+
+      for (int i = 0; i < values.length; i++) {
+        h = 31 * h + values[i];
+      }
+
+      ei = ei.getModifiableInstance();
+      ei.setIntField("hash", h);
+    }
+
+    return h;
+  }
+
+  @MJI
+  public int indexOf__I__I (MJIEnv env, int objref, int c) {
+    return indexOf__II__I(env, objref, c, 0);
+  }
+
+  @MJI
+  public int indexOf__II__I (MJIEnv env, int objref, int c, int fromIndex) {
+    int vref = env.getReferenceField(objref, "value");
+    ElementInfo ei = env.getElementInfo(vref);
+    char[] values = ((CharArrayFields) ei.getFields()).asCharArray();
+
+    int len = values.length;
+
+    if (fromIndex >= len) { return -1; }
+    if (fromIndex < 0) {
+      fromIndex = 0;
+    }
+
+    for (int i = fromIndex; i < len; i++) {
+      if (values[i] == c) { return i; }
+    }
+
+    return -1;
+  }
+
+  @MJI
+  public int lastIndexOf__I__I (MJIEnv env, int objref, int c) {
+    return lastIndexOf__II__I(env, objref, c, Integer.MAX_VALUE);
+  }
+
+  @MJI
+  public int lastIndexOf__II__I (MJIEnv env, int objref, int c, int fromIndex) {
+    int vref = env.getReferenceField(objref, "value");
+    ElementInfo ei = env.getElementInfo(vref);
+    char[] values = ((CharArrayFields) ei.getFields()).asCharArray();
+
+    int len = values.length;
+
+    if (fromIndex < 0) { return -1; }
+    if (fromIndex > len - 1) {
+      fromIndex = len - 1;
+    }
+
+    for (int i = fromIndex; i > 0; i--) {
+      if (values[i] == c) { return i; }
+    }
+
+    return -1;
+  }
+
+  @MJI
+  public int indexOf__Ljava_lang_String_2__I (MJIEnv env, int objref, int str) {
+    String thisStr = env.getStringObject(objref);
+    String indexStr = env.getStringObject(str);
+
+    return thisStr.indexOf(indexStr);
+  }
+
+  @MJI
+  public int indexOf__Ljava_lang_String_2I__I (MJIEnv env, int objref, int str, int fromIndex) {
+    String thisStr = env.getStringObject(objref);
+    String indexStr = env.getStringObject(str);
+
+    return thisStr.indexOf(indexStr, fromIndex);
+  }
+
+  @MJI
+  public int lastIndexOf__Ljava_lang_String_2I__I (MJIEnv env, int objref, int str, int fromIndex) {
+    String thisStr = env.getStringObject(objref);
+    String indexStr = env.getStringObject(str);
+
+    return thisStr.lastIndexOf(indexStr, fromIndex);
+  }
+
+  @MJI
+  public int substring__I__Ljava_lang_String_2 (MJIEnv env, int objRef, int beginIndex) {
+    String obj = env.getStringObject(objRef);
+    String result = obj.substring(beginIndex);
+    return env.newString(result);
+
+  }
+
+  @MJI
+  public int substring__II__Ljava_lang_String_2 (MJIEnv env, int objRef, int beginIndex, int endIndex) {
+    String obj = env.getStringObject(objRef);
+    String result = obj.substring(beginIndex, endIndex);
+    return env.newString(result);
+
+  }
+
+  @MJI
+  public int concat__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int strRef) {
+    Heap heap = env.getHeap();
+
+    ElementInfo thisStr = heap.get(objRef);
+    CharArrayFields thisFields = (CharArrayFields) heap.get(thisStr.getReferenceField("value")).getFields();
+    char[] thisChars = thisFields.asCharArray();
+    int thisLength = thisChars.length;
+
+    ElementInfo otherStr = heap.get(strRef);
+    CharArrayFields otherFields = (CharArrayFields) heap.get(otherStr.getReferenceField("value")).getFields();
+    char[] otherChars = otherFields.asCharArray();
+    int otherLength = otherChars.length;
+
+    if (otherLength == 0) { return objRef; }
+
+    char resultChars[] = new char[thisLength + otherLength];
+    System.arraycopy(thisChars, 0, resultChars, 0, thisLength);
+    System.arraycopy(otherChars, 0, resultChars, thisLength, otherLength);
+
+    return env.newString(new String(resultChars));
+  }
+
+  // --- the various replaces
+
+  @MJI
+  public int replace__CC__Ljava_lang_String_2 (MJIEnv env, int objRef, char oldChar, char newChar) {
+
+    if (oldChar == newChar) { // nothing to replace
+      return objRef;
+    }
+
+    int vref = env.getReferenceField(objRef, "value");
+    ElementInfo ei = env.getModifiableElementInfo(vref);
+    char[] values = ((CharArrayFields) ei.getFields()).asCharArray();
+    int len = values.length;
+
+    char[] newValues = null;
+
+    for (int i = 0, j = 0; j < len; i++, j++) {
+      char c = values[i];
+      if (c == oldChar) {
+        if (newValues == null) {
+          newValues = new char[len];
+          if (j > 0) {
+            System.arraycopy(values, 0, newValues, 0, j);
+          }
+        }
+        newValues[j] = newChar;
+      } else {
+        if (newValues != null) {
+          newValues[j] = c;
+        }
+      }
+    }
+
+    if (newValues != null) {
+      String s = new String(newValues);
+      return env.newString(s);
+
+    } else { // oldChar not found, return the original string
+      return objRef;
+    }
+  }
+
+  @MJI
+  public boolean matches__Ljava_lang_String_2__Z (MJIEnv env, int objRef, int regexRef) {
+    String s = env.getStringObject(objRef);
+    String r = env.getStringObject(regexRef);
+
+    return s.matches(r);
+  }
+
+  @MJI
+  public int replaceFirst__Ljava_lang_String_2Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int regexRef, int replacementRef) {
+    String thisStr = env.getStringObject(objRef);
+    String regexStr = env.getStringObject(regexRef);
+    String replacementStr = env.getStringObject(replacementRef);
+
+    String result = thisStr.replaceFirst(regexStr, replacementStr);
+    return (result != thisStr) ? env.newString(result) : objRef;
+  }
+
+  @MJI
+  public int replaceAll__Ljava_lang_String_2Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int regexRef, int replacementRef) {
+    String thisStr = env.getStringObject(objRef);
+    String regexStr = env.getStringObject(regexRef);
+    String replacementStr = env.getStringObject(replacementRef);
+
+    String result = thisStr.replaceAll(regexStr, replacementStr);
+    return (result != thisStr) ? env.newString(result) : objRef;
+  }
+
+  @MJI
+  public int split__Ljava_lang_String_2I___3Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int strRef, int limit) {
+    String s = env.getStringObject(strRef);
+    String obj = env.getStringObject(clsObjRef);
+
+    String[] result = obj.split(s, limit);
+
+    return env.newStringArray(result);
+  }
+
+  @MJI
+  public int split__Ljava_lang_String_2___3Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int strRef) {
+    String s = env.getStringObject(strRef);
+    String obj = env.getStringObject(clsObjRef);
+
+    String[] result = obj.split(s);
+
+    return env.newStringArray(result);
+  }
+
+  @MJI
+  public int toLowerCase__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int locRef) {
+    String s = env.getStringObject(objRef);
+    Locale loc = JPF_java_util_Locale.getLocale(env, locRef);
+
+    String lower = s.toLowerCase(loc);
+
+    return (s == lower) ? objRef : env.newString(lower);
+  }
+
+  @MJI
+  public int toLowerCase____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    String s = env.getStringObject(objRef);
+    String lower = s.toLowerCase();
+
+    return (s == lower) ? objRef : env.newString(lower);
+  }
+
+  @MJI
+  public int toUpperCase__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int locRef) {
+    String s = env.getStringObject(objRef);
+    Locale loc = JPF_java_util_Locale.getLocale(env, locRef);
+
+    String upper = s.toUpperCase(loc);
+
+    return (s == upper) ? objRef : env.newString(upper);
+  }
+
+  @MJI
+  public int toUpperCase____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    String s = env.getStringObject(objRef);
+    String upper = s.toUpperCase();
+
+    return (s == upper) ? objRef : env.newString(upper);
+  }
+
+  @MJI
+  public int trim____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    Heap heap = env.getHeap();
+    ElementInfo thisStr = heap.get(objRef);
+
+    CharArrayFields thisFields = (CharArrayFields) heap.get(thisStr.getReferenceField("value")).getFields();
+    char[] thisChars = thisFields.asCharArray();
+    int thisLength = thisChars.length;
+
+    int start = 0;
+    int end = thisLength;
+
+    while ((start < end) && (thisChars[start] <= ' ')) {
+      start++;
+    }
+
+    while ((start < end) && (thisChars[end - 1] <= ' ')) {
+      end--;
+    }
+
+    if (start == 0 && end == thisLength) {
+      // if there was no white space, return the string itself
+      return objRef;
+    }
+
+    String result = new String(thisChars, start, end - start);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int toCharArray_____3C (MJIEnv env, int objref) {
+    int vref = env.getReferenceField(objref, "value");
+    char[] v = env.getCharArrayObject(vref);
+
+    int cref = env.newCharArray(v);
+
+    return cref;
+  }
+
+  @MJI
+  public int format__Ljava_lang_String_2_3Ljava_lang_Object_2__Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int fmtRef, int argRef) {
+    return env.newString(env.format(fmtRef, argRef));
+  }
+
+  @MJI
+  public int format__Ljava_util_Locale_2Ljava_lang_String_2_3Ljava_lang_Object_2__Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int locRef, int fmtRef, int argRef) {
+    Locale loc = JPF_java_util_Locale.getLocale(env, locRef);
+    return env.newString(env.format(loc, fmtRef, argRef));
+  }
+
+  @MJI
+  public int intern____Ljava_lang_String_2 (MJIEnv env, int robj) {
+    // <2do> Replace this with a JPF space HashSet once we have a String model
+    Heap heap = env.getHeap();
+
+    String s = env.getStringObject(robj);
+    ElementInfo ei = heap.newInternString(s, env.getThreadInfo());
+
+    return ei.getObjectRef();
+  }
+
+  @MJI
+  public int valueOf__I__Ljava_lang_String_2 (MJIEnv env, int clsref, int i) {
+    String result = String.valueOf(i);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int valueOf__J__Ljava_lang_String_2 (MJIEnv env, int clsref, long l) {
+    String result = String.valueOf(l);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int valueOf__F__Ljava_lang_String_2 (MJIEnv env, int clsref, float f) {
+    String result = String.valueOf(f);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int valueOf__D__Ljava_lang_String_2 (MJIEnv env, int clsref, double d) {
+    String result = String.valueOf(d);
+    return env.newString(result);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuffer.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuffer.java
new file mode 100644 (file)
index 0000000..88178ae
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+
+/**
+ * MJI NativePeer class for java.lang.StringBuffer library abstraction
+ */
+public class JPF_java_lang_StringBuffer extends NativePeer {
+  
+  boolean hasSharedField = false; // Java 1.4 has, 1.5 doesn't
+
+  @MJI
+  public void $clinit____V (MJIEnv env, int clsObjRef) {
+    // apparently, Java 1.5 has changed the implementation of class
+    // StringBuffer so that it doesn't use the 'shared' state anymore
+    // (which was a performance hack to avoid copying the char array
+    // data when creating String objects from subsequently unmodified
+    // StringBuffers
+    // adding this little extra logic here also serves the purpose of
+    // avoiding a native ObjectStreamClass method which is called during
+    // the static StringBuffer init
+    ClassInfo ci = env.getClassInfo();
+    if (ci.getInstanceField("shared") != null) {
+      hasSharedField = true;
+    }
+  }
+  
+  int appendString (MJIEnv env, int objref, String s) {
+    int slen = s.length();
+    int aref = env.getReferenceField(objref, "value");
+    int alen = env.getArrayLength(aref);
+    int count = env.getIntField(objref, "count");
+    int i, j;
+    int n = count + slen;
+    
+    if (n < alen) {
+      for (i=count, j=0; i<n; i++, j++) {
+        env.setCharArrayElement(aref, i, s.charAt(j));
+      }
+    } else {
+      int m = 3 * alen / 2;
+      if (m < n) {
+        m = n;
+      }
+      int arefNew = env.newCharArray(m);
+      for (i=0; i<count; i++) {
+        env.setCharArrayElement(arefNew, i, env.getCharArrayElement(aref, i));
+      }
+      for (j=0; i<n; i++, j++) {
+        env.setCharArrayElement(arefNew, i, s.charAt(j));
+      }
+      env.setReferenceField(objref, "value", arefNew);
+    }
+    
+    if (hasSharedField) {
+      env.setBooleanField(objref, "shared", false);
+    }
+    env.setIntField(objref, "count", n);
+    
+    return objref;
+  }
+
+/*
+  public static int append__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, int sbref) {
+    int vref = env.getReferenceField(sbref, "value");
+    int sbCount = env.getIntField(sbref, "count");
+
+    // how braindead, how lazy
+    char[] b = env.getCharArrayObject(vref);
+    String s = new String(b, 0, sbCount);
+    
+    return appendString(env, objref, s);
+  }
+*/
+
+  @MJI
+  public int append__Ljava_lang_String_2__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, int sref) {
+    String s = env.getStringObject(sref);    
+    if (s == null) s = "null";
+    
+    return appendString(env, objref, s);
+  }
+  
+  @MJI
+  public int append__I__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, int i) {
+    String s = Integer.toString(i);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__F__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, float f) {
+    String s = Float.toString(f);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__D__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, double d) {
+    String s = Double.toString(d);
+    
+    return appendString(env, objref, s);
+  }
+  
+  @MJI
+  public int append__J__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, long l) {
+    String s = Long.toString(l);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__Z__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, boolean b) {
+    String s = b ? "true" : "false";
+    
+    return appendString(env, objref, s);
+  }
+/*
+  public static int append__B__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, byte b) {
+    return append__C__Ljava_lang_StringBuffer_2(env, objref, (char)b);
+  }
+*/
+  @MJI
+  public int append__C__Ljava_lang_StringBuffer_2 (MJIEnv env, int objref, char c) {
+    int aref = env.getReferenceField(objref, "value");
+    int alen = env.getArrayLength(aref);
+    int count = env.getIntField(objref, "count");
+    int n = count +1;
+    
+    if (n < alen) {
+      env.setCharArrayElement(aref, count, c);
+    } else {
+      int m = 3 * alen / 2;
+      int arefNew = env.newCharArray(m);
+      for (int i=0; i<count; i++) {
+        env.setCharArrayElement(arefNew, i, env.getCharArrayElement(aref, i));
+      }
+      env.setCharArrayElement(arefNew, count, c);
+      env.setReferenceField(objref, "value", arefNew);
+    }
+    
+    if (hasSharedField) {
+      env.setBooleanField(objref, "shared", false);
+    }
+    env.setIntField(objref, "count", n);
+    
+    return objref;
+    
+  }
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuilder.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringBuilder.java
new file mode 100644 (file)
index 0000000..a2584e5
--- /dev/null
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_lang_StringBuilder extends NativePeer {
+  
+  int appendString (MJIEnv env, int objref, String s) {
+    int slen = s.length();
+    int aref = env.getReferenceField(objref, "value");
+    int alen = env.getArrayLength(aref);
+    int count = env.getIntField(objref, "count");
+    int i, j;
+    int n = count + slen;
+    
+    if (n < alen) {
+      for (i=count, j=0; i<n; i++, j++) {
+        env.setCharArrayElement(aref, i, s.charAt(j));
+      }
+    } else {
+      int m = 3 * alen / 2;
+      if (m < n) {
+        m = n;
+      }
+      int arefNew = env.newCharArray(m);
+      for (i=0; i<count; i++) {
+        env.setCharArrayElement(arefNew, i, env.getCharArrayElement(aref, i));
+      }
+      for (j=0; i<n; i++, j++) {
+        env.setCharArrayElement(arefNew, i, s.charAt(j));
+      }
+      env.setReferenceField(objref, "value", arefNew);
+    }
+    
+    env.setIntField(objref, "count", n);
+    
+    return objref;
+  }
+
+  // we skip the AbstractStringBuilder ctor here, which is a bit dangerous
+  // This is only justified because StringBuilders are used everywhere (implicitly)
+  @MJI
+  public void $init____V (MJIEnv env, int objref){
+    int aref = env.newCharArray(16);
+    env.setReferenceField(objref, "value", aref);
+  }
+  @MJI
+  public void $init__I__V (MJIEnv env, int objref, int len){
+    int aref = env.newCharArray(len);
+    env.setReferenceField(objref, "value", aref);
+  }
+  @MJI
+  public void $init__Ljava_lang_String_2__V (MJIEnv env, int objref, int sRef){
+    if (sRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException");
+      return;
+    }
+    
+    char[] src = env.getStringChars(sRef);
+    int aref = env.newCharArray(src.length + 16);
+    char[] dst = env.getCharArrayObject(aref);
+    System.arraycopy(src,0,dst,0,src.length);
+    
+    env.setReferenceField(objref, "value", aref);
+    env.setIntField(objref, "count", src.length);
+  }  
+  
+  
+  @MJI
+  public int append__Ljava_lang_String_2__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, int sref) {
+    String s = env.getStringObject(sref);
+    
+    if (s == null) s = "null";
+    
+    return appendString(env, objref, s);
+  }
+  
+  @MJI
+  public int append__I__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, int i) {
+    String s = Integer.toString(i);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__F__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, float f) {
+    String s = Float.toString(f);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__D__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, double d) {
+    String s = Double.toString(d);
+    
+    return appendString(env, objref, s);
+  }
+  
+  @MJI
+  public int append__J__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, long l) {
+    String s = Long.toString(l);
+    
+    return appendString(env, objref, s);
+  }
+
+  @MJI
+  public int append__Z__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, boolean b) {
+    String s = b ? "true" : "false";
+    
+    return appendString(env, objref, s);
+  }
+  
+  @MJI
+  public int append__C__Ljava_lang_StringBuilder_2 (MJIEnv env, int objref, char c) {
+    int aref = env.getReferenceField(objref, "value");
+    int alen = env.getArrayLength(aref);
+    int count = env.getIntField(objref, "count");
+    int i;
+    int n = count +1;
+    
+    if (n < alen) {
+      env.setCharArrayElement(aref, count, c);
+    } else {
+      int m = 3 * alen / 2;
+      int arefNew = env.newCharArray(m);
+      for (i=0; i<count; i++) {
+        env.setCharArrayElement(arefNew, i, env.getCharArrayElement(aref, i));
+      }
+      env.setCharArrayElement(arefNew, count, c);
+      env.setReferenceField(objref, "value", arefNew);
+    }
+    
+    env.setIntField(objref, "count", n);
+    
+    return objref;
+    
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    int aref = env.getReferenceField(objref, "value");
+    int count = env.getIntField(objref, "count");
+
+    char[] buf = env.getCharArrayObject(aref);
+    String s = new String(buf, 0, count);
+    return env.newString(s);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringCoding.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_StringCoding.java
new file mode 100644 (file)
index 0000000..9c37303
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * we are not really interested in model checking this, so we intercept
+ * and ignore
+ * <2do> at some point we should probably do proper decoding/encoding,
+ * but the java.lang.StringCoding class is unfortunately not public,
+ * and it would be a pain to work around the access restrictions
+ */
+public class JPF_java_lang_StringCoding extends NativePeer {
+
+  @MJI
+  public int decode___3BII___3C (MJIEnv env, int clsObjRef,
+      int bref, int off, int len) {
+
+    
+    int cref = env.newCharArray(len);
+    for (int i=0,j=off; i<len; i++,j++) {
+      env.setCharArrayElement(cref, i, (char)env.getByteArrayElement(bref,j));
+    }
+    
+    return cref;
+  }
+  
+  @MJI
+  public int encode___3CII___3B (MJIEnv env, int clsObjRef,
+      int cref, int off, int len) {
+
+    int bref = env.newByteArray(len);
+    for (int i=0,j=off; i<len; i++,j++) {
+      env.setByteArrayElement(bref, i, (byte)env.getCharArrayElement(cref,j));
+    }
+
+    return bref; 
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_System.java
new file mode 100644 (file)
index 0000000..e909395
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * MJI NativePeer class for java.lang.System library abstraction
+ */
+public class JPF_java_lang_System extends NativePeer {
+  
+  @MJI
+  public void arraycopy__Ljava_lang_Object_2ILjava_lang_Object_2II__V (MJIEnv env, int clsObjRef,
+                                                                              int srcArrayRef, int srcIdx, 
+                                                                              int dstArrayRef, int dstIdx,
+                                                                              int length) {
+    if ((srcArrayRef == MJIEnv.NULL) || (dstArrayRef == MJIEnv.NULL)) {
+      env.throwException("java.lang.NullPointerException");
+      return;
+    }
+
+    ElementInfo eiSrc = env.getElementInfo(srcArrayRef);
+    ElementInfo eiDst = env.getModifiableElementInfo(dstArrayRef);
+    
+    try {
+      eiDst.copyElements( env.getThreadInfo(), eiSrc ,srcIdx, dstIdx, length);
+    } catch (IndexOutOfBoundsException iobx){
+      env.throwException("java.lang.IndexOutOfBoundsException", iobx.getMessage());
+    } catch (ArrayStoreException asx){
+      env.throwException("java.lang.ArrayStoreException", asx.getMessage());      
+    }
+  }
+
+  @MJI
+  public int getenv__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int clsObjRef,
+                                                                         int keyRef){
+    String k = env.getStringObject(keyRef);
+    String v = System.getenv(k);
+    
+    if (v == null){
+      return MJIEnv.NULL;
+    } else {
+      return env.newString(v);
+    }
+  }
+
+  
+  int createPrintStream (MJIEnv env, int clsObjRef){
+    ThreadInfo ti = env.getThreadInfo();
+    Instruction insn = ti.getPC();
+    StackFrame frame = ti.getTopFrame();
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo("gov.nasa.jpf.ConsoleOutputStream");
+
+    if (ci.initializeClass(ti)) {
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+
+    return env.newObject(ci);
+  }
+  
+  @MJI
+  public int createSystemOut____Ljava_io_PrintStream_2 (MJIEnv env, int clsObjRef){
+    return createPrintStream(env,clsObjRef);
+  }
+  
+  @MJI
+  public int createSystemErr____Ljava_io_PrintStream_2 (MJIEnv env, int clsObjRef){
+    return createPrintStream(env,clsObjRef);
+  }
+  
+  int getProperties (MJIEnv env, Properties p){
+    int n = p.size() * 2;
+    int aref = env.newObjectArray("Ljava/lang/String;", n);
+    int i=0;
+    
+    for (Map.Entry<Object,Object> e : p.entrySet() ){
+      env.setReferenceArrayElement(aref,i++, 
+                                   env.newString(e.getKey().toString()));
+      env.setReferenceArrayElement(aref,i++,
+                                   env.newString(e.getValue().toString()));
+    }
+    
+    return aref;
+  }
+
+  int getSysPropsFromHost (MJIEnv env){
+    return getProperties(env, System.getProperties());
+  }
+  
+  int getSysPropsFromFile (MJIEnv env){
+    Config conf = env.getConfig();
+    
+    String cf = conf.getString("vm.sysprop.file", "system.properties");
+    if (cf != null){
+      try {
+        Properties p = new Properties();
+        FileInputStream fis = new FileInputStream(cf);
+        p.load(fis);
+        
+        return getProperties(env, p);
+        
+      } catch (IOException iox){
+        return MJIEnv.NULL;
+      }
+    }
+    //.. not yet
+    return MJIEnv.NULL;
+  }
+  
+  static String JAVA_CLASS_PATH = "java.class.path";
+  
+  @MJI
+  public String getSUTJavaClassPath(VM vm) {
+    ClassInfo system = ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.System");
+    
+    ThreadInfo ti = vm.getCurrentThread();
+    Heap heap = vm.getHeap();
+    ElementInfo eiClassPath = heap.newString(JAVA_CLASS_PATH, ti);
+    
+    MethodInfo miGetProperty = system.getMethod("getProperty(Ljava/lang/String;)Ljava/lang/String;", true);
+    DirectCallStackFrame frame = miGetProperty.createDirectCallStackFrame(ti, 0);
+    frame.setReferenceArgument( 0, eiClassPath.getObjectRef(), null);
+    frame.setFireWall(); // we don't want exceptions to escape into the SUT
+    
+    
+    try {
+      ti.executeMethodHidden(frame);
+      
+    } catch (UncaughtException e) {
+       ti.clearPendingException();
+       StackFrame caller = ti.popAndGetModifiableTopFrame();
+       caller.advancePC();
+       return null;
+    }
+    
+    int ref = frame.peek();
+    ElementInfo metaResult = heap.get(ref);
+    String result = metaResult.asString();
+    
+    return result;
+  }
+  
+  int getSelectedSysPropsFromHost (MJIEnv env){
+    Config conf = env.getConfig();
+    String keys[] = conf.getStringArray("vm.sysprop.keys");
+
+    if (keys == null){
+      String[] defKeys = {
+        "path.separator",
+        "line.separator", 
+        "file.separator",
+        "user.name",
+        "user.dir",
+        "user.timezone",
+        "user.country",
+        "java.home",
+        "java.version",
+        "java.io.tmpdir",
+        JAVA_CLASS_PATH
+        //... and probably some more
+        // <2do> what about -Dkey=value commandline options
+      };
+      keys = defKeys;
+    }
+    
+    int aref = env.newObjectArray("Ljava/lang/String;", keys.length * 2);
+    int i=0;
+    
+    for (String s : keys) {
+      String v;
+      
+      int idx = s.indexOf('/');
+      if (idx >0){ // this one is an explicit key/value pair from vm.system.properties
+        v = s.substring(idx+1);
+        s = s.substring(0,idx);
+        
+      } else {
+        // the special beasts first (if they weren't overridden with vm.system.properties)
+        if (s == JAVA_CLASS_PATH) {
+          // maybe we should just use vm.classpath
+          // NOTE: the curent classloader at the point it has to be a system classloader.
+          ClassPath cp = ClassLoaderInfo.getCurrentClassLoader().getClassPath();
+          // <2do> should be consistent with path.separator (this is host VM notation)
+          v = cp.toString();
+          
+        } else { // we bluntly grab it from the host VM properties
+          v = System.getProperty(s);
+        }
+      }
+            
+      if (v != null){
+        env.setReferenceArrayElement(aref,i++, env.newString(s));
+        env.setReferenceArrayElement(aref,i++, env.newString(v));
+      }
+    }
+        
+    return aref;
+  }
+
+  /**
+   * policy of how to initialize system properties of the system under test
+   */
+  static enum SystemPropertyPolicy {
+    SELECTED,  // copy host values for keys specified in  
+    FILE, 
+    HOST
+  };
+
+  @MJI
+  public int getKeyValuePairs_____3Ljava_lang_String_2 (MJIEnv env, int clsObjRef){
+    Config conf = env.getConfig();
+    SystemPropertyPolicy sysPropSrc = conf.getEnum( "vm.sysprop.source", SystemPropertyPolicy.values(), SystemPropertyPolicy.SELECTED);
+
+    if (sysPropSrc == SystemPropertyPolicy.FILE){
+      return getSysPropsFromFile(env);
+    } else if (sysPropSrc == SystemPropertyPolicy.HOST){
+      return getSysPropsFromHost(env);
+    } else if (sysPropSrc == SystemPropertyPolicy.SELECTED){
+      return getSelectedSysPropsFromHost(env);
+    }
+    
+    return 0;
+  }
+  
+  // <2do> - this break every app which uses time delta thresholds
+  // (sort of "if ((t2 - t1) > d)"). Ok, we can't deal with
+  // real time, but we could at least give some SystemState dependent
+  // increment
+  @MJI
+  public long currentTimeMillis____J (MJIEnv env, int clsObjRef) {
+    return env.currentTimeMillis();
+  }
+
+  // <2do> - likewise. Java 1.5's way to measure relative time
+  @MJI
+  public long nanoTime____J (MJIEnv env, int clsObjRef) {
+    return env.nanoTime();
+  }  
+  
+  // this works on the assumption that we sure break the transition, and
+  // then the search determines that it is an end state (all terminated)
+  @MJI
+  public void exit__I__V (MJIEnv env, int clsObjRef, int ret) {
+    ThreadInfo ti = env.getThreadInfo();
+    env.getVM().terminateProcess(ti);
+  }
+
+  @MJI
+  public void gc____V (MJIEnv env, int clsObjRef) {
+    env.getSystemState().activateGC();
+  }
+
+  @MJI
+  public int identityHashCode__Ljava_lang_Object_2__I (MJIEnv env, int clsObjRef, int objref) {
+    return (objref ^ 0xABCD);
+  }
+  
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Thread.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Thread.java
new file mode 100644 (file)
index 0000000..11362aa
--- /dev/null
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+
+
+/**
+ * MJI NativePeer class for java.lang.Thread library abstraction
+ * 
+ * NOTE - this implementation depends on all live thread objects being
+ * in ThreadList
+ */
+public class JPF_java_lang_Thread extends NativePeer {
+
+  static JPFLogger log = JPF.getLogger("gov.nasa.jpf.vm.ThreadInfo");
+  
+  
+  /**
+   * This method is the common initializer for all Thread ctors, and the only
+   * single location where we can init our ThreadInfo, but it is PRIVATE
+   */
+  @MJI
+  public void init0__Ljava_lang_ThreadGroup_2Ljava_lang_Runnable_2Ljava_lang_String_2J__V (MJIEnv env,
+                         int objRef, int groupRef, int runnableRef, int nameRef, long stackSize) {
+    VM vm = env.getVM();
+    
+    // we only need to create the ThreadInfo - its initialization will take care
+    // of proper linkage to the java.lang.Thread object (objRef)
+    vm.createThreadInfo( objRef, groupRef, runnableRef, nameRef);
+  }
+
+  @MJI
+  public boolean isAlive____Z (MJIEnv env, int objref) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    if (ti != null){
+      return ti.isAlive();
+    } else {
+      return false; // not in ThreadList anymore
+    }
+  }
+
+  @MJI
+  public void setDaemon0__Z__V (MJIEnv env, int objref, boolean isDaemon) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    ti.setDaemon(isDaemon);
+  }
+
+  @MJI
+  public void dumpStack____V (MJIEnv env, int clsObjRef){
+    ThreadInfo ti = env.getThreadInfo();
+    ti.printStackTrace(); // this is not correct, we should go through VM.print
+  }
+
+  @MJI
+  public void setName0__Ljava_lang_String_2__V (MJIEnv env, int objref, int nameRef) {
+    // it bails if you try to set a null name
+    if (nameRef == MJIEnv.NULL) {
+      env.throwException("java.lang.IllegalArgumentException");
+
+      return;
+    }
+
+    // we have to intercept this to cache the name as a Java object
+    // (to be stored in ThreadData)
+    // luckily enough, it's copied into the java.lang.Thread object
+    // as a char[], i.e. does not have to preserve identity
+    // Note the nastiness in here - the java.lang.Thread object is only used
+    // to get the initial values into ThreadData, and gets inconsistent
+    // if this method is called (just works because the 'name' field is only
+    // directly accessed from within the Thread ctors)
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    ti.setName(env.getStringObject(nameRef));
+  }
+
+  @MJI
+  public void setPriority0__I__V (MJIEnv env, int objref, int prio) {
+    // again, we have to cache this in ThreadData for performance reasons
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    
+    if (prio != ti.getPriority()){
+      ti.setPriority(prio);
+    
+      // this could cause a context switch in a priority based scheduler
+      if (ti.getScheduler().setsPriorityCG(ti)){
+        env.repeatInvocation();
+        return;
+      }
+    }
+  }
+
+  @MJI
+  public int countStackFrames____I (MJIEnv env, int objref) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    return ti.countStackFrames();
+  }
+
+  @MJI
+  public int currentThread____Ljava_lang_Thread_2 (MJIEnv env, int clsObjRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    return ti.getThreadObjectRef();
+  }
+
+  @MJI
+  public boolean holdsLock__Ljava_lang_Object_2__Z (MJIEnv env, int clsObjRef, int objref) {
+    ThreadInfo  ti = env.getThreadInfo();
+    ElementInfo ei = env.getElementInfo(objref);
+
+    return ei.isLockedBy(ti);
+  }
+
+  @MJI
+  public void interrupt____V (MJIEnv env, int objref) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiInterrupted = env.getThreadInfoForObjRef(objref);
+
+    if (!tiCurrent.isFirstStepInsn()) {
+      tiInterrupted.interrupt();
+    }
+    
+    if (tiCurrent.getScheduler().setsInterruptCG(tiCurrent, tiInterrupted)) {
+      env.repeatInvocation();
+      return;
+    }
+  }
+
+  // these could be in the model, but we keep it symmetric, which also saves
+  // us the effort of avoiding unwanted shared object field access CGs
+  @MJI
+  public boolean isInterrupted____Z (MJIEnv env, int objref) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    return ti.isInterrupted(false);
+  }
+
+  @MJI
+  public boolean interrupted____Z (MJIEnv env, int clsObjRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    return ti.isInterrupted(true);
+  }
+
+  @MJI
+  public void start____V (MJIEnv env, int objref) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiStarted = env.getThreadInfoForObjRef(objref);
+    VM vm = tiCurrent.getVM();
+
+    //--- actions that are only performed upon first execution
+    if (!tiCurrent.isFirstStepInsn()){
+      if (tiStarted.isStopped()) {
+        // don't do anything but set it terminated - it hasn't acquired any resources yet.
+        // note that apparently host VMs don't schedule this thread, so there is no handler invocation
+        tiStarted.setTerminated();
+        return;
+      }
+
+      if (!tiStarted.isNew()) {
+        // alredy running, throw a IllegalThreadStateException. If it already terminated, it just gets
+        // silently ignored in Java 1.4, but the 1.5 spec explicitly marks this
+        // as illegal, so we adopt this by throwing an IllegalThreadState, too
+        env.throwException("java.lang.IllegalThreadStateException");
+        return;
+      }
+
+      int runnableRef = tiStarted.getRunnableRef();
+      if (runnableRef == MJIEnv.NULL) {
+        // note that we don't set the 'tiSuspended' field, since java.lang.Thread doesn't
+        runnableRef = objref;
+      }
+
+      ElementInfo eiTarget = env.getElementInfo(runnableRef);
+      ClassInfo   ci = eiTarget.getClassInfo();
+      MethodInfo  miRun = ci.getMethod("run()V", true);
+
+      // we do direct call run() invocation so that we have a well defined
+      // exit point (DIRECTCALLRETURN) in case the thread is stopped or there is
+      // a fail-safe UncaughtExceptionHandler set
+      DirectCallStackFrame runFrame = miRun.createRunStartStackFrame(tiStarted);
+      runFrame.setReferenceArgument(0, runnableRef, null);
+            
+      tiStarted.pushFrame(runFrame);
+      tiStarted.setState(ThreadInfo.State.RUNNING);
+      
+      vm.notifyThreadStarted(tiStarted);
+    }
+    
+    //--- scheduling point
+    if (tiCurrent.getScheduler().setsStartCG(tiCurrent, tiStarted)){
+      env.repeatInvocation();
+    }
+    // everything that would follow would be re-executed
+  }
+
+  @MJI
+  public void yield____V (MJIEnv env, int clsObjRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    if (ti.getScheduler().setsYieldCG(ti)){
+      env.repeatInvocation();
+    }
+  }
+
+  @MJI
+  public void sleep__JI__V (MJIEnv env, int clsObjRef, long millis, int nanos) {
+    ThreadInfo ti = env.getThreadInfo();
+
+    // check scheduling point
+    if (ti.getScheduler().setsSleepCG(ti, millis, nanos)){
+      ti.setSleeping();
+      env.repeatInvocation();
+      return;
+    }
+    
+    if (ti.isSleeping()){
+      ti.setRunning();
+    }
+  }
+
+  @MJI
+  public void suspend____V (MJIEnv env, int threadObjRef) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiSuspended = env.getThreadInfoForObjRef(threadObjRef);
+
+    if (tiSuspended.isTerminated()) {
+      return;  // nothing to do, it's already gone
+    }
+    
+    if (!tiCurrent.isFirstStepInsn()){ // do this just once
+      tiSuspended.suspend();
+    }
+    
+    // scheduling point
+    if (tiCurrent.getScheduler().setsSuspendCG(tiCurrent, tiSuspended)){
+      env.repeatInvocation();      
+    }
+  }
+
+  @MJI
+  public void resume____V (MJIEnv env, int threadObjRef) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiResumed = env.getThreadInfoForObjRef(threadObjRef);
+
+    if (tiCurrent == tiResumed){
+      return;  // no self resume prior to suspension
+    }
+
+    if (tiResumed.isTerminated()) {
+      return;  // nothing to resume
+    }
+
+    if (!tiCurrent.isFirstStepInsn()) { // do this just once
+      tiResumed.resume();
+    }
+    
+    // check scheduling point
+    if (tiCurrent.getScheduler().setsResumeCG(tiCurrent, tiResumed)){
+      env.repeatInvocation();
+      return;
+    }
+  }
+
+  /*
+   * the join() workhorse. We use lockfree waits instead of a simple wait from a synchronized block
+   * to save states
+   */
+  protected void join0 (MJIEnv env, int joineeRef, long timeout){
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiJoinee = env.getThreadInfoForObjRef(joineeRef);
+    ElementInfo eiJoinee = env.getModifiableElementInfo(joineeRef); // the thread object to wait on
+
+    if (timeout < 0) {
+      env.throwException("java.lang.IllegalArgumentException", "timeout value is negative");
+      return;
+    }
+      
+    if (tiCurrent.isInterrupted(true)){ // interrupt status is set, throw and bail      
+      // since we use lock-free waits, we need to remove ourselves from the lock contender list
+      eiJoinee.setMonitorWithoutLocked(tiCurrent);
+      
+      // note that we have to throw even if the thread to join to is not alive anymore
+      env.throwInterrupt();
+      return;
+    }
+  
+    if (!tiCurrent.isFirstStepInsn()){ // to be executed only once    
+      if (tiJoinee.isAlive()) {
+        // block in first top half so that following transitions see this thread as not runnable
+        eiJoinee.wait( tiCurrent, timeout, false);
+      } else {
+        return; // nothing to do
+      }
+    }    
+
+    if (tiCurrent.getScheduler().setsJoinCG(tiCurrent, tiJoinee, timeout)) {
+      env.repeatInvocation();
+      return;
+    }
+    
+    // unblock in bottom half
+    switch (tiCurrent.getState()) {
+      case WAITING:
+      case TIMEOUT_WAITING:
+        throw new JPFException("blocking join without transition break");        
+      
+      case UNBLOCKED:
+        // Thread was owning the lock when it joined - we have to wait until
+        // we can reacquire it
+        eiJoinee.lockNotified(tiCurrent);
+        break;
+
+      case TIMEDOUT:
+        eiJoinee.resumeNonlockedWaiter(tiCurrent);
+        break;
+
+      case RUNNING:
+        if (tiJoinee.isAlive()) { // we still need to wait
+          eiJoinee.wait(tiCurrent, timeout, false); // no need for a new CG
+          env.repeatInvocation();
+        }
+        break;
+    }
+  }
+
+  @MJI
+  public void join____V (MJIEnv env, int objref){
+    join0(env,objref,0);
+  }
+
+  @MJI
+  public void join__J__V (MJIEnv env, int objref, long millis) {
+    join0(env,objref,millis);
+
+  }
+
+  @MJI
+  public void join__JI__V (MJIEnv env, int objref, long millis, int nanos) {
+    join0(env,objref,millis); // <2do> we ignore nanos for now
+  }
+
+  @MJI
+  public int getState0____I (MJIEnv env, int objref) {
+    // return the state index with respect to one of the public Thread.States
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+
+    switch (ti.getState()) {
+      case NEW:
+        return 1;
+      case RUNNING:
+        return 2;
+      case BLOCKED:
+        return 0;
+      case UNBLOCKED:
+        return 2;
+      case WAITING:
+        return 5;
+      case TIMEOUT_WAITING:
+        return 4;
+      case SLEEPING:
+        return 4;
+      case NOTIFIED:
+        return 0;
+      case INTERRUPTED:
+        return 0;
+      case TIMEDOUT:
+        return 2;
+      case TERMINATED:
+        return 3;
+      default:
+        throw new JPFException("illegal thread state: " + ti.getState());
+    }
+  }
+
+  @MJI
+  public long getId____J (MJIEnv env, int objref) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(objref);
+    return ti.getId();
+  }
+  
+  @MJI
+  public void stop____V (MJIEnv env, int threadRef) {
+    stop__Ljava_lang_Throwable_2__V(env, threadRef, MJIEnv.NULL);
+  }
+
+  @MJI
+  public void stop__Ljava_lang_Throwable_2__V (MJIEnv env, int threadRef, int throwableRef) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiStopped = env.getThreadInfoForObjRef(threadRef);
+
+    if (tiStopped.isTerminated() || tiStopped.isStopped()) {
+      return; // silently ignored
+    }
+
+    if (tiCurrent.getScheduler().setsStopCG(tiCurrent, tiStopped)){
+      env.repeatInvocation();
+      return;
+    }
+
+    // stop thread in bottom half
+    tiStopped.setStopped(throwableRef);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_ThreadLocal.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_ThreadLocal.java
new file mode 100644 (file)
index 0000000..602061b
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+/**
+ * peer for java.lang.ThreadLocal
+ */
+public class JPF_java_lang_ThreadLocal extends NativePeer {
+  
+  @MJI
+  public int getEntry____Ljava_lang_ThreadLocal$Entry_2 (MJIEnv env, int objRef){
+    ThreadInfo ti = env.getThreadInfo();
+    int tObjRef = ti.getThreadObjectRef();
+    
+    int aRef = env.getReferenceField(tObjRef, "threadLocals");
+    if (aRef == MJIEnv.NULL){
+      return MJIEnv.NULL;
+    }
+    int[] erefs = env.getReferenceArrayObject(aRef);
+    int len = env.getArrayLength(aRef);
+    
+    
+    for (int i=0; i<len; i++){
+      int e = erefs[i];
+      int k = env.getReferenceField(e, "ref");
+      
+      if (k == objRef){
+        return e;
+      }
+    }
+    
+    return MJIEnv.NULL;
+  }
+  
+  @MJI
+  public void addEntry__Ljava_lang_ThreadLocal$Entry_2 (MJIEnv env, int objRef, int eRef){
+    ThreadInfo ti = env.getThreadInfo();
+    int tObjRef = ti.getThreadObjectRef();
+    int aRef = env.getReferenceField(tObjRef, "threadLocals");
+    
+    if (aRef == MJIEnv.NULL){ // first entry
+      int newaRef = env.newObjectArray("Ljava/lang/ThreadLocal.Entry;", 1);
+      env.setReferenceArrayElement(newaRef, 0, eRef);
+      env.setReferenceField(tObjRef, "threadLocals", newaRef);
+      
+    } else {
+      int len = env.getArrayLength(aRef);
+      int[] erefs = env.getReferenceArrayObject(aRef);
+
+      // new key/value
+      int newaRef = env.newObjectArray("Ljava/lang/ThreadLocal.Entry;", len+1);
+      env.arrayCopy(aRef, 0, newaRef, 0, len);
+      env.setReferenceArrayElement(newaRef, len, eRef);
+      env.setReferenceField(tObjRef, "threadLocals", newaRef);
+    }
+    
+  }
+  
+  @MJI
+  public void removeEntry__Ljava_lang_ThreadLocal$Entry_2 (MJIEnv env, int objRef, int eRef){
+    ThreadInfo ti = env.getThreadInfo();
+    int tObjRef = ti.getThreadObjectRef();
+    int aRef = env.getReferenceField(tObjRef, "threadLocals");
+    
+    if (aRef != MJIEnv.NULL){
+      int len = env.getArrayLength(aRef);
+      int[] erefs = env.getReferenceArrayObject(aRef);
+      int newaRef = env.newObjectArray("Ljava/lang/ThreadLocal.Entry;", len-1);
+
+      for (int i=0, j=0; i<len; i++){
+        int e = erefs[i];
+        if (e != eRef){
+          env.setReferenceArrayElement(newaRef, j++, e);
+        }
+      }
+      
+      env.setReferenceField(tObjRef, "threadLocals", newaRef);      
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Throwable.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_Throwable.java
new file mode 100644 (file)
index 0000000..d16ade8
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+
+/**
+ * MJI NativePeer class for java.lang.Throwable library abstraction
+ */
+public class JPF_java_lang_Throwable extends NativePeer {    
+  /**
+   * return array of StackTraceElement elements from the snapshot stored in the Throwable
+   */
+  @MJI
+  public int createStackTrace_____3Ljava_lang_StackTraceElement_2 (MJIEnv env, int objref) {
+    int aref = env.getReferenceField(objref, "snapshot");
+    int[] snap = env.getIntArrayObject(aref);
+    
+    return env.getThreadInfo().createStackTraceElements(snap);
+  }
+  
+  @MJI
+  public int fillInStackTrace____Ljava_lang_Throwable_2 (MJIEnv env, int objref) {
+    ThreadInfo ti = env.getThreadInfo();
+    int[] snap = ti.getSnapshot(objref);
+    
+    int aref = env.newIntArray(snap);
+    env.setReferenceField(objref, "snapshot", aref);
+    
+    return objref;
+  }
+    
+  // native because we don't want to waste states
+  @MJI
+  public void printStackTrace____V (MJIEnv env, int objRef) {
+    env.getThreadInfo().printStackTrace(objRef);
+  }
+  
+  // a helper method to get a string representation of the stacktrace
+  @MJI
+  public int getStackTraceAsString____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    StringWriter sw = new StringWriter();
+    PrintWriter pw = new PrintWriter(sw);
+    
+    ti.printStackTrace(pw, objRef);
+    String stackTrace = sw.toString();
+    pw.close();
+    
+    return env.newString(stackTrace);
+  }
+  
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    ClassInfo ci = env.getClassInfo(objRef);
+    int msgRef = env.getReferenceField(objRef, "detailMessage");
+    
+    String s = ci.getName();
+    if (msgRef != MJIEnv.NULL){
+      s += ": " + env.getStringObject(msgRef);
+    }
+    
+    return env.newString(s);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Array.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Array.java
new file mode 100644 (file)
index 0000000..8dfa035
--- /dev/null
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.Types;
+
+/**
+ * MJI NativePeer class for java.lang.reflect.Array library abstraction
+ */
+public class JPF_java_lang_reflect_Array extends NativePeer {
+  @MJI
+  public int getLength__Ljava_lang_Object_2__I (MJIEnv env, int clsObjRef, 
+                                                    int objRef) {
+    if (objRef == MJIEnv.NULL) {
+      env.throwException("java.lang.NullPointerException", "array argument is null");
+      return 0;
+    }
+    if (!env.isArray(objRef)) {
+      env.throwException("java.lang.IllegalArgumentException", "argument is not an array");
+      return 0;
+    }
+
+    return env.getArrayLength(objRef);
+  }
+  
+  @MJI
+  public int newArray__Ljava_lang_Class_2I__Ljava_lang_Object_2 (MJIEnv env, int clsRef,
+                                                                        int componentTypeRef, int length) {
+    ClassInfo ci = env.getReferredClassInfo(componentTypeRef);
+    String clsName = ci.getName();
+    
+    return createNewArray( env, clsName, length);
+  }
+  
+  static int createNewArray (MJIEnv env, String clsName, int length) {
+    int aRef = MJIEnv.NULL;
+    
+    if ("boolean".equals(clsName)) { aRef = env.newBooleanArray(length); }
+    else if ("byte".equals(clsName)) { aRef = env.newByteArray(length); }
+    else if ("char".equals(clsName)) { aRef = env.newCharArray(length); }
+    else if ("short".equals(clsName)) { aRef = env.newShortArray(length); }
+    else if ("int".equals(clsName)) { aRef = env.newIntArray(length); }
+    else if ("long".equals(clsName)) { aRef = env.newLongArray(length); }
+    else if ("float".equals(clsName)) { aRef = env.newFloatArray(length); }
+    else if ("double".equals(clsName)) { aRef = env.newDoubleArray(length); }
+    else { aRef = env.newObjectArray(clsName, length); }
+    return aRef;    
+  }
+  
+  @MJI
+  public int multiNewArray__Ljava_lang_Class_2_3I__Ljava_lang_Object_2 (MJIEnv env, int clsRef,
+                                                                               int componentTypeRef,
+                                                                               int dimArrayRef) {
+    ClassInfo ci = env.getReferredClassInfo(componentTypeRef);
+    String clsName = ci.getName();
+    int n = env.getArrayLength(dimArrayRef);
+    int i;
+
+    clsName = Types.getTypeSignature(clsName, true);
+    
+    String arrayType = "[";
+    for (i=2; i<n; i++) arrayType += '[';
+    arrayType += clsName;
+    
+    int[] dim = new int[n];
+    for (i=0; i<n; i++) {
+      dim[i] = env.getIntArrayElement(dimArrayRef, i);
+    }
+    
+    int aRef = createNewMultiArray(env, arrayType, dim, 0); 
+    return aRef;
+  }
+  
+  static int createNewMultiArray (MJIEnv env, String arrayType, int[] dim, int level) {
+    int aRef = MJIEnv.NULL;
+    int len = dim[level];
+    
+    if (level < dim.length-1) {
+      aRef = env.newObjectArray(arrayType, len);
+    
+      for (int i=0; i<len; i++) {
+        int eRef = createNewMultiArray(env, arrayType.substring(1), dim, level+1);
+        env.setReferenceArrayElement(aRef, i, eRef);
+      }
+    } else {
+      aRef = createNewArray( env, arrayType, len);
+    }
+    
+    return aRef;
+  }
+
+  @MJI
+  public int get__Ljava_lang_Object_2I__Ljava_lang_Object_2 (MJIEnv env, int clsRef,
+                                                                    int aref, int index){
+    String at = env.getArrayType(aref);
+    if (at.equals("int")){
+      int vref = env.newObject("java.lang.Integer");
+      env.setIntField(vref, "value", env.getIntArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("long")){
+      int vref = env.newObject("java.lang.Long");
+      env.setLongField(vref, "value", env.getLongArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("double")){
+      int vref = env.newObject("java.lang.Double");
+      env.setDoubleField(vref, "value", env.getDoubleArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("boolean")){
+      int vref = env.newObject("java.lang.Boolean");
+      env.setBooleanField(vref, "value", env.getBooleanArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("char")){
+      int vref = env.newObject("java.lang.Character");
+      env.setCharField(vref, "value", env.getCharArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("byte")){
+      int vref = env.newObject("java.lang.Byte");
+      env.setByteField(vref, "value", env.getByteArrayElement(aref,index));
+      return vref;
+      
+    } else if (at.equals("short")){
+      int vref = env.newObject("java.lang.Short");
+      env.setShortField(vref, "value", env.getShortArrayElement(aref,index));
+      return vref;
+
+    } else if (at.equals("float")){
+      int vref = env.newObject("java.lang.Float");
+      env.setFloatField(vref, "value", env.getFloatArrayElement(aref,index));
+      return vref;
+
+    } else {
+      return env.getReferenceArrayElement(aref, index);
+    }
+  }
+
+  private static boolean check (MJIEnv env, int aref, int index) {
+    if (aref == MJIEnv.NULL) {
+      env.throwException("java.lang.NullPointerException", "array argument is null");
+      return false;
+    }
+    if (!env.isArray(aref)) {
+      env.throwException("java.lang.IllegalArgumentException", "argument is not an array");
+      return false;
+    }
+    if (index < 0 || index >= env.getArrayLength(aref)) {
+      env.throwException("java.lang.IndexOutOfBoundsException", "index " + index + " is out of bounds");
+      return false;
+    }
+    return true;
+  }
+
+  @MJI
+  public boolean getBoolean__Ljava_lang_Object_2I__Z (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getBooleanArrayElement(aref, index);
+    }
+    return false;
+  }
+
+  @MJI
+  public static byte getByte__Ljava_lang_Object_2I__B (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getByteArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public char getChar__Ljava_lang_Object_2I__C (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getCharArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public short getShort__Ljava_lang_Object_2I__S (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getShortArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public int getInt__Ljava_lang_Object_2I__I (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getIntArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public long getLong__Ljava_lang_Object_2I__J (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getLongArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public float getFloat__Ljava_lang_Object_2I__F (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getFloatArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public double getDouble__Ljava_lang_Object_2I__D (MJIEnv env, int clsRef, int aref, int index) {
+    if (check(env, aref, index)) {
+      return env.getDoubleArrayElement(aref, index);
+    }
+    return 0;
+  }
+
+  @MJI
+  public void setBoolean__Ljava_lang_Object_2IZ__V (MJIEnv env, int clsRef, int aref, int index, boolean val) {
+    if (check(env, aref, index)) {
+      env.setBooleanArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setByte__Ljava_lang_Object_2IB__V (MJIEnv env, int clsRef, int aref, int index, byte val) {
+    if (check(env, aref, index)) {
+      env.setByteArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setChar__Ljava_lang_Object_2IC__V (MJIEnv env, int clsRef, int aref, int index, char val) {
+    if (check(env, aref, index)) {
+      env.setCharArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setShort__Ljava_lang_Object_2IS__V (MJIEnv env, int clsRef, int aref, int index, short val) {
+    if (check(env, aref, index)) {
+      env.setShortArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setInt__Ljava_lang_Object_2II__V (MJIEnv env, int clsRef, int aref, int index, int val) {
+    if (check(env, aref, index)) {
+      env.setIntArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setLong__Ljava_lang_Object_2IJ__V (MJIEnv env, int clsRef, int aref, int index, long val) {
+    if (check(env, aref, index)) {
+      env.setLongArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setFloat__Ljava_lang_Object_2IF__V (MJIEnv env, int clsRef, int aref, int index, float val) {
+    if (check(env, aref, index)) {
+      env.setFloatArrayElement(aref, index, val);
+    }
+  }
+
+  @MJI
+  public void setDouble__Ljava_lang_Object_2ID__V (MJIEnv env, int clsRef, int aref, int index, double val) {
+    if (check(env, aref, index)) {
+      env.setDoubleArrayElement(aref, index, val);
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Constructor.java
new file mode 100644 (file)
index 0000000..a37e87c
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import java.lang.reflect.Modifier;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.MethodInfoRegistry;
+import gov.nasa.jpf.util.RunListener;
+import gov.nasa.jpf.util.RunRegistry;
+
+/**
+ * native peer for rudimentary constructor reflection.
+ * 
+ * Unfortunately, this is quite redundant to the Method peer, but Constructor
+ * is not a Method subclass, and hence we can't rely on it's initialization
+ */
+public class JPF_java_lang_reflect_Constructor extends NativePeer {
+  
+  static MethodInfoRegistry registry;
+  
+  public static boolean init (Config conf) {
+    // this is an example of how to handle cross-initialization between
+    // native peers - this might also get explicitly called by the java.lang.Class
+    // peer, since it creates Constructor objects. Here we have to make sure
+    // we only reset between JPF runs
+    
+    if (registry == null){
+      registry = new MethodInfoRegistry();
+      
+      RunRegistry.getDefaultRegistry().addListener( new RunListener() {
+        @Override
+               public void reset (RunRegistry reg){
+          registry = null;
+        }
+      });
+    }
+    return true;
+  }
+
+  static int createConstructorObject (MJIEnv env, ClassInfo ciCtor, MethodInfo mi){
+    // note - it is the callers responsibility to ensure Constructor is properly initialized    
+    
+    int regIdx = registry.registerMethodInfo(mi);
+    int eidx = env.newObject(ciCtor);
+    ElementInfo ei = env.getModifiableElementInfo(eidx);
+    
+    ei.setIntField("regIdx", regIdx);
+    return eidx;
+  }
+
+  static MethodInfo getMethodInfo (MJIEnv env, int objRef){
+    return registry.getMethodInfo(env,objRef, "regIdx");
+  }
+  
+  @MJI
+  public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    MethodInfo mi = getMethodInfo(env, objRef);
+    
+    int nameRef = env.getReferenceField( objRef, "name");
+    if (nameRef == MJIEnv.NULL) {
+      nameRef = env.newString(mi.getName());
+      env.setReferenceField(objRef, "name", nameRef);
+    }
+   
+    return nameRef;
+  }
+  
+  // <2do> .. and some more delegations to JPF_java_lang_Method
+
+  @MJI
+  public int newInstance___3Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int mthRef, int argsRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+    MethodInfo miCallee = getMethodInfo(env,mthRef);
+
+    if (frame == null) { // first time
+      ClassInfo ci = miCallee.getClassInfo();
+
+       if (ci.isAbstract()){
+        env.throwException("java.lang.InstantiationException");
+        return MJIEnv.NULL;
+      }
+
+      int objRef = env.newObjectOfUncheckedClass( ci);
+      frame = miCallee.createDirectCallStackFrame( ti, 1);
+      frame.setReflection();
+      
+      frame.setLocalReferenceVariable(0, objRef);  // (1) store the objRef for retrieval during re-exec
+      
+      int argOffset = frame.setReferenceArgument(0, objRef, null);
+      if (!JPF_java_lang_reflect_Method.pushUnboxedArguments( env, miCallee, frame, argOffset, argsRef)) {
+        // we've got a IllegalArgumentException
+        return MJIEnv.NULL;
+      }
+      ti.pushFrame(frame);
+       
+      ci.initializeClass(ti);
+      
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+      
+    } else { // reflection call returned
+      int objRef = frame.getLocalVariable(0); // that's the object ref we stored in (1)
+      return objRef;
+    }
+  }
+    
+  @MJI
+  public int getParameterTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    // kind of dangerous, but we don't refer to any fields and the underlying JPF construct
+    // (MethodInfo) is the same, so we just delegate to avoid copying non-trivial code
+    return JPF_java_lang_reflect_Method.getParameterTypes (env, getMethodInfo(env,objRef));
+  }
+
+  @MJI
+  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef){
+    // <2do> check if ctor annotations are inherited, which is a bit off
+    return JPF_java_lang_reflect_Method.getAnnotations( env, getMethodInfo(env,objRef));
+  }
+  
+  @MJI
+  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef){
+    return JPF_java_lang_reflect_Method.getDeclaredAnnotations( env, getMethodInfo(env,objRef));
+  }
+  
+  @MJI
+  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef, int annotationClsRef) {
+    return JPF_java_lang_reflect_Method.getAnnotation( env, getMethodInfo(env,objRef), annotationClsRef);
+  }
+  
+  @MJI
+  public int getParameterAnnotations_____3_3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef){
+    return JPF_java_lang_reflect_Method.getParameterAnnotations( env, getMethodInfo(env,objRef));
+  }
+
+  @MJI
+  public int getModifiers____I (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    return mi.getModifiers();
+  }
+
+  @MJI
+  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);    
+    ClassInfo ci = mi.getClassInfo();
+    // can't get a Constructor object w/o having initialized it's declaring class first
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    StringBuilder sb = new StringBuilder();
+    
+    MethodInfo mi = getMethodInfo(env, objRef);
+
+    sb.append(Modifier.toString(mi.getModifiers()));
+    sb.append(' ');
+
+    sb.append(mi.getClassInfo().getName());
+    sb.append('(');
+    
+    String[] at = mi.getArgumentTypeNames();
+    for (int i=0; i<at.length; i++){
+      if (i>0) sb.append(',');
+      sb.append(at[i]);
+    }
+    
+    sb.append(')');
+    
+    int sref = env.newString(sb.toString());
+    return sref;
+  }
+
+  @MJI
+  public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int mthRef){
+    ElementInfo ei = env.getElementInfo(mthRef);
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(JPF_java_lang_Class.CONSTRUCTOR_CLASSNAME);
+
+    if (ei.getClassInfo() == ci){
+      MethodInfo mi1 = getMethodInfo(env, objRef);
+      MethodInfo mi2 = getMethodInfo(env, mthRef);
+      if (mi1.getClassInfo() == mi2.getClassInfo()){
+        if (mi1.getName().equals(mi2.getName())){
+          if (mi1.getReturnType().equals(mi2.getReturnType())){
+            byte[] params1 = mi1.getArgumentTypes();
+            byte[] params2 = mi2.getArgumentTypes();
+            if (params1.length == params2.length){
+              for (int i = 0; i < params1.length; i++){
+                if (params1[i] != params2[i])
+                  return false;
+              }
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objRef){
+    MethodInfo ctor = getMethodInfo(env, objRef);
+    return ctor.getClassName().hashCode();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Field.java
new file mode 100644 (file)
index 0000000..758a0c7
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+
+import java.lang.reflect.Modifier;
+
+/**
+ * native peer for java.lang.reflect.Field
+ */
+public class JPF_java_lang_reflect_Field extends NativePeer {
+
+  // <2do> using Fields is fine, but creating them is not efficient until we get rid of the registry
+  
+  static final int NREG = 64;
+  static FieldInfo[] registered;
+  static int nRegistered;
+  
+  public static boolean init (Config conf){
+    registered = new FieldInfo[NREG];
+    nRegistered = 0;
+    return true;
+  }
+  
+  static int registerFieldInfo (FieldInfo fi) {
+    int idx;
+    
+    for (idx=0; idx < nRegistered; idx++) {
+      if (registered[idx] == fi) {
+        return idx;
+      }
+    }
+    
+    if (idx == registered.length) {
+      FieldInfo[] newReg = new FieldInfo[registered.length+NREG];
+      System.arraycopy(registered, 0, newReg, 0, registered.length);
+      registered = newReg;
+    }
+    
+    registered[idx] = fi;
+    nRegistered++;
+    return idx;
+  }
+  
+  static FieldInfo getRegisteredFieldInfo (int idx) {
+    return registered[idx];
+  }
+  
+  /**
+   * <2do> that doesn't take care of class init yet
+   */
+  @MJI
+  public int getType____Ljava_lang_Class_2 (MJIEnv env, int objRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+
+    ClassInfo ci = fi.getTypeClassInfo();
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+    }
+
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int getModifiers____I (MJIEnv env, int objRef){
+    FieldInfo fi = getFieldInfo(env, objRef);
+    return fi.getModifiers();
+  }
+
+  protected StackFrame getCallerFrame (MJIEnv env){
+    ThreadInfo ti = env.getThreadInfo();
+    StackFrame frame = ti.getTopFrame(); // this is the Field.get/setX(), so we still have to get down
+    return frame.getPrevious();
+  }
+  
+  protected boolean isAccessible (MJIEnv env, FieldInfo fi, int fieldRef, int ownerRef){
+    
+    // note that setAccessible() even overrides final
+    ElementInfo fei = env.getElementInfo(fieldRef);
+    if (fei.getBooleanField("isAccessible")){
+      return true;
+    }
+    
+    if (fi.isFinal()){
+      return false;
+    }
+    
+    if (fi.isPublic()){
+      return true;
+    }
+    
+    // otherwise we have to check object identities and access modifier of the executing method
+    ClassInfo ciDecl = fi.getClassInfo();
+    String declPackage = ciDecl.getPackageName();
+    
+    StackFrame frame = getCallerFrame(env);    
+    MethodInfo mi = frame.getMethodInfo();
+    ClassInfo ciMethod = mi.getClassInfo();
+    String mthPackage = ciMethod.getPackageName();
+
+    if (!fi.isPrivate() && declPackage.equals(mthPackage)) {
+      return true;
+    }
+    
+    if (fi.isStatic()){
+      if (ciDecl == ciMethod){
+        return true;
+      }
+      
+    } else {
+      int thisRef = frame.getCalleeThis(mi);
+      if (thisRef == ownerRef) { // same object
+        return true;
+      }
+    }
+    
+    // <2do> lots of more checks here
+    
+    return false;
+  }
+  
+  protected ElementInfo getCheckedElementInfo (MJIEnv env, FieldInfo fi, int objRef, int ownerRef, boolean isWrite){
+    ElementInfo ei;
+
+    if (!isAvailable(env, fi, ownerRef)){
+      return null;
+    }
+
+    if (fi.isStatic()){
+      ClassInfo fci = fi.getClassInfo();
+      ei = isWrite ? fci.getModifiableStaticElementInfo() : fci.getStaticElementInfo();
+    } else { // instance field
+      ei = isWrite ? env.getModifiableElementInfo(ownerRef) : env.getElementInfo(ownerRef);
+    }
+
+    if (ei == null) {
+      env.throwException("java.lang.NullPointerException");
+      return null;
+    }
+
+    if ( !isAccessible(env, fi, objRef, ownerRef)){
+      env.throwException("java.lang.IllegalAccessException", "field not accessible: " + fi);
+      return null;
+    }
+    
+    return ei;
+  }
+  
+  protected boolean checkFieldType (MJIEnv env, FieldInfo fi, Class<?> fiType){
+    if (!fiType.isInstance(fi)) {
+      env.throwException("java.lang.IllegalArgumentException", "incompatible field type: " + fi);
+      return false;
+    }
+    
+    return true;
+  }
+  
+  protected ElementInfo checkSharedFieldAccess (ThreadInfo ti, ElementInfo ei, FieldInfo fi){    
+    Instruction insn = ti.getPC();
+    Scheduler scheduler = ti.getScheduler();
+    
+    if (fi.isStatic()){      
+      if (scheduler.canHaveSharedClassCG(ti, insn, ei, fi)){
+        ei = scheduler.updateClassSharedness(ti, ei, fi);
+        scheduler.setsSharedClassCG(ti, insn, ei, fi);
+      }
+      
+    } else {
+      if (scheduler.canHaveSharedObjectCG(ti, insn, ei, fi)){
+        ei = scheduler.updateObjectSharedness(ti, ei, fi);
+        scheduler.setsSharedObjectCG(ti, insn, ei, fi);
+      }
+    }
+    
+    return ei;
+  }
+  
+  @MJI
+  public boolean getBoolean__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, BooleanFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return false;
+        }
+        
+        return ei.getBooleanField(fi);
+      }
+    }
+    return false;
+  }
+
+  @MJI
+  public byte getByte__Ljava_lang_Object_2__B (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null) {
+      if (checkFieldType(env, fi, BooleanFieldInfo.class)) {
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getByteField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public char getChar__Ljava_lang_Object_2__C (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, CharFieldInfo.class)) {
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getCharField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public short getShort__Ljava_lang_Object_2__S (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, ShortFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getShortField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public int getInt__Ljava_lang_Object_2__I (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, IntegerFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getIntField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public long getLong__Ljava_lang_Object_2__J (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, LongFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getLongField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public float getFloat__Ljava_lang_Object_2__F (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, FloatFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getFloatField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public double getDouble__Ljava_lang_Object_2__D (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, false);
+    if (ei != null){
+      if (checkFieldType(env, fi, DoubleFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return 0;
+        }
+        return ei.getDoubleField(fi);
+      }
+    }
+    return 0;
+  }
+
+  @MJI
+  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef, int annotationClsRef) {
+    FieldInfo fi = getFieldInfo(env,objRef);
+    ClassInfo aci = env.getReferredClassInfo(annotationClsRef);
+    
+    AnnotationInfo ai = fi.getAnnotation(aci.getName());
+    if (ai != null){
+      ClassInfo aciProxy = aci.getAnnotationProxy();
+      try {
+        return env.newAnnotationProxy(aciProxy, ai);
+      } catch (ClinitRequired x){
+        env.handleClinitRequest(x.getRequiredClassInfo());
+        return MJIEnv.NULL;
+      }
+    }
+    
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef){
+    FieldInfo fi = getFieldInfo(env,objRef);
+    AnnotationInfo[] ai = fi.getAnnotations();
+    
+    try {
+      return env.newAnnotationProxies(ai);
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }
+  }
+
+  @MJI
+  public void setBoolean__Ljava_lang_Object_2Z__V (MJIEnv env, int objRef, int ownerRef,
+                                                          boolean val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, BooleanFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setBooleanField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setByte__Ljava_lang_Object_2B__V (MJIEnv env, int objRef, int ownerRef, byte val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, ByteFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setByteField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setChar__Ljava_lang_Object_2C__V (MJIEnv env, int objRef, int ownerRef, char val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, CharFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setCharField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setShort__Ljava_lang_Object_2S__V (MJIEnv env, int objRef, int ownerRef,  short val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, ShortFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setShortField(fi, val);
+      }
+    }
+  }  
+
+  @MJI
+  public void setInt__Ljava_lang_Object_2I__V (MJIEnv env, int objRef, int ownerRef, int val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, IntegerFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setIntField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setLong__Ljava_lang_Object_2J__V (MJIEnv env, int objRef, int ownerRef, long val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, LongFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setLongField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setFloat__Ljava_lang_Object_2F__V (MJIEnv env, int objRef, int ownerRef, float val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, FloatFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setFloatField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public void setDouble__Ljava_lang_Object_2D__V (MJIEnv env, int objRef, int ownerRef, double val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      if (checkFieldType(env, fi, DoubleFieldInfo.class)){
+        ei = checkSharedFieldAccess(ti, ei, fi);
+        if (ti.getVM().hasNextChoiceGenerator()) {
+          env.repeatInvocation();
+          return;
+        }
+        ei.setDoubleField(fi, val);
+      }
+    }
+  }
+
+  @MJI
+  public int get__Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int objRef, int ownerRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    
+    ElementInfo ei = getCheckedElementInfo( env, fi, objRef, ownerRef, false); // no type check here
+    if (ei == null){
+      // just return, NPE already thrown by getCheckedElementInfo()
+      return 0;
+    }
+     
+    ei = checkSharedFieldAccess(ti, ei, fi);
+    if (ti.getVM().hasNextChoiceGenerator()) {
+      env.repeatInvocation();
+      return 0;
+    }
+    
+    if (!(fi instanceof ReferenceFieldInfo)) { // primitive type, we need to box it
+      if (fi instanceof DoubleFieldInfo){
+        double d = ei.getDoubleField(fi);
+        return env.newDouble(d);
+      } else if (fi instanceof FloatFieldInfo){
+        float f = ei.getFloatField(fi);
+        return env.newFloat(f);
+      } else if (fi instanceof LongFieldInfo){
+        long l = ei.getLongField(fi);
+        return env.newLong(l);
+      } else if (fi instanceof IntegerFieldInfo){
+        // this might actually represent a plethora of types
+        int i = ei.getIntField(fi);
+        return env.newInteger(i);
+      } else if (fi instanceof BooleanFieldInfo){
+        boolean b = ei.getBooleanField(fi);
+        return env.newBoolean(b);
+      } else if (fi instanceof ByteFieldInfo){
+        byte z = ei.getByteField(fi);
+        return env.newByte(z);
+      } else if (fi instanceof CharFieldInfo){
+        char c = ei.getCharField(fi);
+        return env.newCharacter(c);
+      } else if (fi instanceof ShortFieldInfo){
+        short s = ei.getShortField(fi);
+        return env.newShort(s);
+      }
+      
+    } else { // it's a reference
+      int ref = ei.getReferenceField(fi); // we internally store it as int
+      return ref;
+    }
+    
+    env.throwException("java.lang.IllegalArgumentException", "unknown field type");
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objref){
+    FieldInfo fi = getFieldInfo(env, objref);
+    ClassInfo ci = fi.getClassInfo();
+    return ci.getClassObjectRef();
+  }
+
+  @MJI
+  public boolean isSynthetic____Z (MJIEnv env, int objref){
+    FieldInfo fi = getFieldInfo(env, objref);
+    String fn = fi.getName();
+    return (fn.startsWith("this$") || fn.startsWith("val$"));
+  }
+
+  @MJI
+  public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    FieldInfo fi = getFieldInfo(env, objRef);
+    
+    int nameRef = env.getReferenceField( objRef, "name");
+    if (nameRef == MJIEnv.NULL) {
+      nameRef = env.newString(fi.getName());
+      env.setReferenceField(objRef, "name", nameRef);
+    }
+   
+    return nameRef;
+  }
+
+  static FieldInfo getFieldInfo (MJIEnv env, int objRef) {
+    int fidx = env.getIntField( objRef, "regIdx");
+    assert ((fidx >= 0) || (fidx < nRegistered)) : "illegal FieldInfo request: " + fidx + ", " + nRegistered;
+    
+    return registered[fidx];
+  }
+  
+  static boolean isAvailable (MJIEnv env, FieldInfo fi, int ownerRef){
+    if (fi.isStatic()){
+      ClassInfo fci = fi.getClassInfo();
+      if (fci.initializeClass(env.getThreadInfo())){
+        env.repeatInvocation();
+        return false;
+      }
+      
+    } else {
+      if (ownerRef == MJIEnv.NULL){
+        env.throwException("java.lang.NullPointerException");
+        return false;        
+      }
+      // class had obviously been initialized, otherwise we won't have an instance of it
+    }
+
+    return true;
+  }
+  
+  
+  /**
+   * Peer method for the <code>java.lang.reflect.Field.set</code> method.
+   * 
+   * <2do> refactor to make boxed type handling more efficient
+   */
+  @MJI
+  public void set__Ljava_lang_Object_2Ljava_lang_Object_2__V (MJIEnv env, int objRef, int ownerRef, int val) {
+    ThreadInfo ti = env.getThreadInfo();
+    FieldInfo fi = getFieldInfo(env, objRef);
+
+    if (!isAvailable(env, fi, ownerRef)){
+      return;
+    }
+        
+    ClassInfo ci = fi.getClassInfo();
+    ClassInfo cio = env.getClassInfo(ownerRef);
+
+    if (!fi.isStatic() && !cio.isInstanceOf(ci)) {
+      env.throwException("java.lang.IllegalArgumentException", 
+                         fi.getType() + "field " + fi.getName() + " does not belong to this object");
+      return;
+    }
+    
+    Object[] attrs = env.getArgAttributes();
+    Object attr = (attrs==null)? null: attrs[2];
+    
+    String type = getValueType(env, val);
+    
+    if (!isAssignmentCompatible(env, fi, val)){
+      env.throwException("java.lang.IllegalArgumentException", 
+                         "field of type " + fi.getType() + " not assignment compatible with " + type + " object");      
+    }
+    
+    ElementInfo ei = getCheckedElementInfo(env, fi, objRef, ownerRef, true);
+    if (ei != null){
+      // <2do> what about exposure?
+      ei = checkSharedFieldAccess(ti, ei, fi);
+      if (ti.getVM().hasNextChoiceGenerator()) {
+        env.repeatInvocation();
+        return;
+      }
+
+      if (!setValue(env, fi, ownerRef, val, attr)) {
+        env.throwException("java.lang.IllegalArgumentException",
+                "Can not set " + fi.getType() + " field " + fi.getFullName() + " to " + ((MJIEnv.NULL != val) ? env.getClassInfo(val).getName() + " object " : "null"));
+      }
+    }
+  }
+
+  protected String getValueType (MJIEnv env, int ref){
+    if (ref != MJIEnv.NULL){
+      ElementInfo eiVal = env.getElementInfo(ref);
+      return eiVal.getType();
+    } else {
+      return null;
+    }
+  }
+  
+  protected boolean isAssignmentCompatible (MJIEnv env, FieldInfo fi, int refVal){
+    if (refVal == MJIEnv.NULL){
+      return true;
+      
+    } else {
+      ElementInfo eiVal = env.getElementInfo(refVal);
+      ClassInfo ciVal = eiVal.getClassInfo();
+      String valClsName = ciVal.getName();
+      
+      if (fi.isBooleanField() && valClsName.equals("java.lang.Boolean")) return true;
+      else if (fi.isByteField() && valClsName.equals("java.lang.Byte")) return true;
+      else if (fi.isCharField() && valClsName.equals("java.lang.Char")) return true;
+      else if (fi.isShortField() && valClsName.equals("java.lang.Short")) return true;
+      else if (fi.isIntField() && valClsName.equals("java.lang.Integer")) return true;
+      else if (fi.isLongField() && valClsName.equals("java.lang.Long")) return true;
+      else if (fi.isFloatField() && valClsName.equals("java.lang.Float")) return true;
+      else if (fi.isDoubleField() && valClsName.equals("java.lang.Double")) return true;
+      else {
+        return ciVal.isInstanceOf(fi.getTypeClassInfo());
+      }
+    }
+  }
+  
+  protected static boolean setValue(MJIEnv env, FieldInfo fi, int obj, int value, Object attr) {
+    ClassInfo fieldClassInfo = fi.getClassInfo();
+    String className = fieldClassInfo.getName();
+    String fieldType = fi.getType();
+    ClassInfo tci = fi.getTypeClassInfo();
+    
+    ElementInfo ei = null;
+    if (fi.isStatic()) {
+      ei = fi.getClassInfo().getModifiableStaticElementInfo();
+    } else {
+      ei = env.getModifiableElementInfo(obj);
+    }
+
+    if (tci.isPrimitive()) {
+      if (value == MJIEnv.NULL) {
+        return false;
+      }
+
+      // checks whether unboxing can be done by accessing the field "value"
+      final String fieldName = "value";
+      FieldInfo finfo = env.getElementInfo(value).getFieldInfo(fieldName);
+      if (finfo == null) {
+        return false;
+      }
+      
+      ei.setFieldAttr(fi, attr);
+
+      if ("boolean".equals(fieldType)){
+        boolean val = env.getBooleanField(value, fieldName);
+        ei.setBooleanField(fi, val);
+        return true;
+      } else if ("byte".equals(fieldType)){
+        byte val = env.getByteField(value, fieldName);
+        ei.setByteField(fi, val);
+        return true;
+      } else if ("char".equals(fieldType)){
+        char val = env.getCharField(value, fieldName);
+        ei.setCharField(fi, val);
+        return true;
+      } else if ("short".equals(fieldType)){
+        short val = env.getShortField(value, fieldName);
+        ei.setShortField(fi, val);
+        return true;
+      } else if ("int".equals(fieldType)){
+        int val = env.getIntField(value, fieldName);
+        ei.setIntField(fi, val);
+        return true;
+      } else if ("long".equals(fieldType)){
+        long val = env.getLongField(value, fieldName);
+        ei.setLongField(fi, val);
+        return true;
+      } else if ("float".equals(fieldType)){
+        float val = env.getFloatField(value, fieldName);
+        ei.setFloatField(fi, val);
+        return true;
+      } else if ("double".equals(fieldType)){
+        double val = env.getDoubleField(value, fieldName);
+        ei.setDoubleField(fi, val);
+        return true;
+      } else {
+        return false;
+      }
+
+    } else { // it's a reference
+      if (value != MJIEnv.NULL) {
+        ClassInfo ciValue = env.getClassInfo(value);
+        if (!ciValue.isInstanceOf(tci)) {
+          return false;
+        }
+      }
+
+      ei.setFieldAttr(fi, attr);
+
+      if (fi.isStatic()) {
+        env.setStaticReferenceField(className, fi.getName(), value);
+      } else {
+        env.setReferenceField(obj, fi.getName(), value);
+      }
+      return true;
+    }
+  }
+
+  @MJI
+  public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int ownerRef){
+    int fidx = env.getIntField(ownerRef, "regIdx");
+    if (fidx >= 0 && fidx < nRegistered){
+      FieldInfo fi1 = getFieldInfo(env, objRef);
+      FieldInfo fi2 = getFieldInfo(env, ownerRef);
+      return ((fi1.getClassInfo() == fi2.getClassInfo()) && fi1.getName().equals(fi2.getName()) && fi1.getType().equals(fi2.getType()));
+    }
+    return false;
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    StringBuilder sb = new StringBuilder();
+    FieldInfo fi = getFieldInfo(env, objRef);
+    sb.append(Modifier.toString(fi.getModifiers()));
+    sb.append(' ');
+    sb.append(fi.getType());
+    sb.append(' ');
+    sb.append(fi.getFullName());
+    int sref = env.newString(sb.toString());
+    return sref;
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objRef){
+    FieldInfo fi = getFieldInfo(env, objRef);
+    return fi.getClassInfo().getName().hashCode() ^ fi.getName().hashCode();
+  }
+
+  @MJI
+  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int objRef){
+    return getAnnotations_____3Ljava_lang_annotation_Annotation_2(env, objRef);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Method.java
new file mode 100644 (file)
index 0000000..66b564e
--- /dev/null
@@ -0,0 +1,654 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.MethodInfoRegistry;
+import gov.nasa.jpf.util.RunListener;
+import gov.nasa.jpf.util.RunRegistry;
+
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+public class JPF_java_lang_reflect_Method extends NativePeer {
+
+  static MethodInfoRegistry registry;
+
+  // class init - this is called automatically from the NativePeer ctor
+  public static boolean init (Config conf) {
+    // this is an example of how to handle cross-initialization between
+    // native peers - this might also get explicitly called by the java.lang.Class
+    // peer, since it creates Method objects. Here we have to make sure
+    // we only reset between JPF runs
+
+    if (registry == null){
+      registry = new MethodInfoRegistry();
+      
+      RunRegistry.getDefaultRegistry().addListener( new RunListener() {
+        @Override
+               public void reset (RunRegistry reg){
+          registry = null;
+        }
+      });
+    }
+    return true;
+  }
+
+  static int createMethodObject (MJIEnv env, ClassInfo ciMth, MethodInfo mi){
+    // note - it is the callers responsibility to ensure Method is properly initialized    
+    int regIdx = registry.registerMethodInfo(mi);
+    int eidx = env.newObject( ciMth);
+    ElementInfo ei = env.getModifiableElementInfo(eidx);
+    
+    ei.setIntField("regIdx", regIdx);
+    ei.setBooleanField("isAccessible", mi.isPublic());
+    
+    return eidx;
+  }
+  
+  // this is NOT an MJI method, but it is used outside this package, so
+  // we have to add 'final'
+  public static final MethodInfo getMethodInfo (MJIEnv env, int objRef){
+    return registry.getMethodInfo(env,objRef, "regIdx");
+  }
+  
+  @MJI
+  public int getName____Ljava_lang_String_2 (MJIEnv env, int objRef) {
+    MethodInfo mi = getMethodInfo(env, objRef);
+    
+    int nameRef = env.getReferenceField( objRef, "name");
+    if (nameRef == MJIEnv.NULL) {
+      nameRef = env.newString(mi.getName());
+      env.setReferenceField(objRef, "name", nameRef);
+    }
+   
+    return nameRef;
+  }
+
+  @MJI
+  public int getModifiers____I (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    return mi.getModifiers();
+  }
+  
+  static int getParameterTypes( MJIEnv env, MethodInfo mi) {
+    ThreadInfo ti = env.getThreadInfo();
+    String[] argTypeNames = mi.getArgumentTypeNames();
+    int[] ar = new int[argTypeNames.length];
+
+    for (int i = 0; i < argTypeNames.length; i++) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(argTypeNames[i]);
+      if (!ci.isRegistered()) {
+        ci.registerClass(ti);
+      }
+
+      ar[i] = ci.getClassObjectRef();
+    }
+
+    int aRef = env.newObjectArray("Ljava/lang/Class;", argTypeNames.length);
+    for (int i = 0; i < argTypeNames.length; i++) {
+      env.setReferenceArrayElement(aRef, i, ar[i]);
+    }
+
+    return aRef;
+  }
+  
+  @MJI
+  public int getParameterTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    return getParameterTypes(env, getMethodInfo(env, objRef));
+  }
+  
+  int getExceptionTypes(MJIEnv env, MethodInfo mi) {
+    ThreadInfo ti = env.getThreadInfo();
+    String[] exceptionNames = mi.getThrownExceptionClassNames();
+     
+    if (exceptionNames == null) {
+      exceptionNames = new String[0];
+    }
+     
+    int[] ar = new int[exceptionNames.length];
+     
+    for (int i = 0; i < exceptionNames.length; i++) {
+      ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(exceptionNames[i]);
+      if (!ci.isRegistered()) {
+        ci.registerClass(ti);
+      }
+       
+      ar[i] = ci.getClassObjectRef();
+    }
+     
+    int aRef = env.newObjectArray("Ljava/lang/Class;", exceptionNames.length);
+    for (int i = 0; i < exceptionNames.length; i++) {
+      env.setReferenceArrayElement(aRef, i, ar[i]);
+    }
+     
+    return aRef;
+  }
+  
+  @MJI
+  public int getExceptionTypes_____3Ljava_lang_Class_2 (MJIEnv env, int objRef) {
+    return getExceptionTypes(env, getMethodInfo(env, objRef));
+  }
+  
+  @MJI
+  public int getReturnType____Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    ThreadInfo ti = env.getThreadInfo();
+
+    ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(mi.getReturnTypeName());
+    if (!ci.isRegistered()) {
+      ci.registerClass(ti);
+    }
+
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int getDeclaringClass____Ljava_lang_Class_2 (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);    
+    ClassInfo ci = mi.getClassInfo();
+    // it's got to be registered, otherwise we wouldn't be able to acquire the Method object
+    return ci.getClassObjectRef();
+  }
+    
+  static int createBoxedReturnValueObject (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame) {
+    byte rt = mi.getReturnTypeCode();
+    int ret = MJIEnv.NULL;
+    ElementInfo rei;
+    Object attr = null;
+
+    if (rt == Types.T_DOUBLE) {
+      attr = frame.getLongResultAttr();
+      double v = frame.getDoubleResult();
+      ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Double"));
+      rei = env.getModifiableElementInfo(ret);
+      rei.setDoubleField("value", v);
+    } else if (rt == Types.T_FLOAT) {
+      attr = frame.getResultAttr();
+      float v = frame.getFloatResult();
+      ret = env.newObject(ClassLoaderInfo.getSystemResolvedClassInfo("java.lang.Float"));
+      rei = env.getModifiableElementInfo(ret);
+      rei.setFloatField("value", v);
+    } else if (rt == Types.T_LONG) {
+      attr = frame.getLongResultAttr();
+      long v = frame.getLongResult();
+      ret = env.valueOfLong(v);
+    } else if (rt == Types.T_BYTE) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfByte((byte)v);
+    } else if (rt == Types.T_CHAR) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfCharacter((char)v);
+    } else if (rt == Types.T_SHORT) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfShort((short)v);
+    } else if (rt == Types.T_INT) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult(); 
+      ret = env.valueOfInteger(v);
+    } else if (rt == Types.T_BOOLEAN) {
+      attr = frame.getResultAttr();
+      int v = frame.getResult();
+      ret = env.valueOfBoolean((v == 1)? true: false);
+    } else if (mi.isReferenceReturnType()){ 
+      attr = frame.getResultAttr();
+      ret = frame.getReferenceResult();
+    }
+
+    env.setReturnAttribute(attr);
+    return ret;
+  }
+
+  static boolean pushUnboxedArguments (MJIEnv env, MethodInfo mi, DirectCallStackFrame frame, int argIdx, int argsRef) {
+    ElementInfo source;
+    ClassInfo sourceClass;
+    String destTypeNames[];
+    int nArgs, passedCount, sourceRef;
+    byte sourceType, destTypes[];
+
+    destTypes     = mi.getArgumentTypes();
+    destTypeNames = mi.getArgumentTypeNames();
+    nArgs         = destTypeNames.length;
+    
+    // according to the API docs, passing null instead of an empty array is allowed for no args
+    passedCount   = (argsRef != MJIEnv.NULL) ? env.getArrayLength(argsRef) : 0;
+    
+    if (nArgs != passedCount) {
+      env.throwException(IllegalArgumentException.class.getName(), "Wrong number of arguments passed.  Actual = " + passedCount + ".  Expected = " + nArgs);
+      return false;
+    }
+    
+    for (int i = 0; i < nArgs; i++) {
+      sourceRef = env.getReferenceArrayElement(argsRef, i);
+
+      // we have to handle null references explicitly
+      if (sourceRef == MJIEnv.NULL) {
+        if ((destTypes[i] != Types.T_REFERENCE) && (destTypes[i] != Types.T_ARRAY)) {
+          env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Actual = (null).  Expected = " + destTypeNames[i]);
+          return false;
+        } 
+         
+        frame.pushRef(MJIEnv.NULL);
+        continue;
+      }
+
+      source      = env.getElementInfo(sourceRef);
+      sourceClass = source.getClassInfo();   
+      sourceType = getSourceType( sourceClass, destTypes[i], destTypeNames[i]);
+
+      Object attr = env.getElementInfo(argsRef).getFields().getFieldAttr(i);
+      if ((argIdx = pushArg( argIdx, frame, source, sourceType, destTypes[i], attr)) < 0 ){
+        env.throwException(IllegalArgumentException.class.getName(), "Wrong argument type at index " + i + ".  Source Class = " + sourceClass.getName() + ".  Dest Class = " + destTypeNames[i]);
+        return false;        
+      }
+    }
+    
+    return true;
+  }
+
+  // this returns the primitive type in case we have to unbox, and otherwise checks reference type compatibility
+  private static byte getSourceType (ClassInfo ciArgVal, byte destType, String destTypeName){
+    switch (destType){
+    // the primitives
+    case Types.T_BOOLEAN:
+    case Types.T_BYTE:
+    case Types.T_CHAR:
+    case Types.T_SHORT:
+    case Types.T_INT:
+    case Types.T_LONG:
+    case Types.T_FLOAT:
+    case Types.T_DOUBLE:
+      return Types.getUnboxedType(ciArgVal.getName());
+      
+    case Types.T_ARRAY:
+    case Types.T_REFERENCE: // check if the source type is assignment compatible with the destType
+      if (ciArgVal.isInstanceOf(destTypeName)){
+        return destType;
+      }
+    }
+    
+    return Types.T_NONE;
+  }
+  
+  // do the proper type conversion - Java is pretty forgiving here and does
+  // not throw exceptions upon value truncation
+  private static int pushArg( int argIdx, DirectCallStackFrame frame, ElementInfo eiArg, byte srcType, byte destType, Object attr){    
+    switch (srcType) {
+    case Types.T_DOUBLE:
+    {
+      double v = eiArg.getDoubleField("value");
+      if (destType == Types.T_DOUBLE){
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_FLOAT: // covers float, double
+    {
+      float v = eiArg.getFloatField("value");
+      switch (destType){
+      case Types.T_FLOAT:
+        return frame.setFloatArgument( argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_LONG:
+    {
+      long v = eiArg.getLongField("value");
+      switch (destType){
+      case Types.T_LONG:
+        return frame.setLongArgument(argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_INT:
+    { 
+      int v = eiArg.getIntField("value");
+      switch (destType){
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_SHORT:
+    { 
+      int v = eiArg.getShortField("value");
+      switch (destType){
+      case Types.T_SHORT:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_BYTE:
+    { 
+      byte v = eiArg.getByteField("value");
+      switch (destType){
+      case Types.T_BYTE:
+      case Types.T_SHORT:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_CHAR:
+    {
+      char v = eiArg.getCharField("value");
+      switch (destType){
+      case Types.T_CHAR:
+      case Types.T_INT:
+        return frame.setArgument( argIdx, v, attr);
+      case Types.T_LONG:
+        return frame.setLongArgument( argIdx, v, attr);
+      case Types.T_FLOAT:
+        return frame.setFloatArgument(argIdx, v, attr);
+      case Types.T_DOUBLE:
+        return frame.setDoubleArgument( argIdx, v, attr);
+      }
+      return -1;
+    }
+    case Types.T_BOOLEAN:
+    {
+      boolean v = eiArg.getBooleanField("value");
+      if (destType == Types.T_BOOLEAN){
+        return frame.setArgument( argIdx, v ? 1 : 0, attr);
+      }
+      return -1;
+    }
+    case Types.T_ARRAY:
+    {
+      int ref =  eiArg.getObjectRef();
+      if (destType == Types.T_ARRAY){
+        return frame.setReferenceArgument( argIdx, ref, attr);
+      }
+      return -1;
+    }
+    case Types.T_REFERENCE:
+    {
+      int ref =  eiArg.getObjectRef();
+      if (destType == Types.T_REFERENCE){
+        return frame.setReferenceArgument( argIdx, ref, attr);
+      }
+      return -1;
+    }
+    default:
+      // T_VOID, T_NONE
+      return -1;
+    }
+  }
+
+  @MJI
+  public int invoke__Ljava_lang_Object_2_3Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int mthRef, int objRef, int argsRef) {
+    ThreadInfo ti = env.getThreadInfo();
+    MethodInfo miCallee = getMethodInfo(env, mthRef);
+    ClassInfo calleeClass = miCallee.getClassInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+    
+    if (frame == null){ // first time
+
+      //--- check the instance we are calling on
+      if (!miCallee.isStatic()) {
+        if (objRef == MJIEnv.NULL){
+          env.throwException("java.lang.NullPointerException");
+          return MJIEnv.NULL;
+          
+        } else {
+          ElementInfo eiObj = ti.getElementInfo(objRef);
+          ClassInfo objClass = eiObj.getClassInfo();
+        
+          if (!objClass.isInstanceOf(calleeClass)) {
+            env.throwException(IllegalArgumentException.class.getName(), "Object is not an instance of declaring class.  Actual = " + objClass + ".  Expected = " + calleeClass);
+            return MJIEnv.NULL;
+          }
+        }
+      }
+
+      //--- check accessibility
+      ElementInfo eiMth = ti.getElementInfo(mthRef);
+      if (! (Boolean) eiMth.getFieldValueObject("isAccessible")) {
+        StackFrame caller = ti.getTopFrame().getPrevious();
+        ClassInfo callerClass = caller.getClassInfo();
+
+        if (callerClass != calleeClass) {
+          env.throwException(IllegalAccessException.class.getName(), "Class " + callerClass.getName() +
+                  " can not access a member of class " + calleeClass.getName()
+                  + " with modifiers \"" + Modifier.toString(miCallee.getModifiers()));
+          return MJIEnv.NULL;
+        }
+      }
+      
+      //--- push the direct call
+      frame = miCallee.createDirectCallStackFrame(ti, 0);
+      frame.setReflection();
+      
+      int argOffset = 0;
+      if (!miCallee.isStatic()) {
+        frame.setReferenceArgument( argOffset++, objRef, null);
+      }
+      if (!pushUnboxedArguments( env, miCallee, frame, argOffset, argsRef)) {
+        // we've got a IllegalArgumentException
+        return MJIEnv.NULL;  
+      }
+      ti.pushFrame(frame);
+      
+      
+      //--- check for and push required clinits
+      if (miCallee.isStatic()){
+        calleeClass.initializeClass(ti);
+      }
+      
+      return MJIEnv.NULL; // reexecute
+      
+    } else { // we have returned from the direct call
+      return createBoxedReturnValueObject( env, miCallee, frame);
+    }
+  }
+  
+
+  // this one has to collect annotations upwards in the inheritance chain
+  static int getAnnotations (MJIEnv env, MethodInfo mi){
+    String mname = mi.getName();
+    String msig = mi.genericSignature;
+    ArrayList<AnnotationInfo> aiList = new ArrayList<AnnotationInfo>();
+    
+    // our own annotations
+    ClassInfo ci = mi.getClassInfo();
+    for (AnnotationInfo ai : mi.getAnnotations()) {
+      aiList.add(ai);
+    }
+    
+    // our superclass annotations
+    for (ci = ci.getSuperClass(); ci != null; ci = ci.getSuperClass()){
+      mi = ci.getMethod(mname, msig, false);
+      if (mi != null){
+        for (AnnotationInfo ai: mi.getAnnotations()){
+          aiList.add(ai);
+        }        
+      }
+    }
+
+    try {
+      return env.newAnnotationProxies(aiList.toArray(new AnnotationInfo[aiList.size()]));
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getAnnotations( env, getMethodInfo(env,mthRef));
+  }
+  
+  // the following ones consist of a package default implementation that is shared with
+  // the constructor peer, and a public model method
+  static int getAnnotation (MJIEnv env, MethodInfo mi, int annotationClsRef){
+    ClassInfo aci = env.getReferredClassInfo(annotationClsRef);
+    
+    AnnotationInfo ai = mi.getAnnotation(aci.getName());
+    if (ai != null){
+      ClassInfo aciProxy = aci.getAnnotationProxy();
+      try {
+        return env.newAnnotationProxy(aciProxy, ai);
+      } catch (ClinitRequired x){
+        env.handleClinitRequest(x.getRequiredClassInfo());
+        return MJIEnv.NULL;
+      }
+    }
+    
+    return MJIEnv.NULL;
+  }  
+
+  @MJI
+  public int getAnnotation__Ljava_lang_Class_2__Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef, int annotationClsRef) {
+    return getAnnotation(env, getMethodInfo(env,mthRef), annotationClsRef);
+  }
+  
+  static int getDeclaredAnnotations (MJIEnv env, MethodInfo mi){
+    AnnotationInfo[] ai = mi.getAnnotations();
+
+    try {
+      return env.newAnnotationProxies(ai);
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getDeclaredAnnotations_____3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getDeclaredAnnotations( env, getMethodInfo(env,mthRef));
+  }
+  
+  static int getParameterAnnotations (MJIEnv env, MethodInfo mi){
+    AnnotationInfo[][] pa = mi.getParameterAnnotations();
+    // this should always return an array object, even if the method has no arguments
+    
+    try {
+      int paRef = env.newObjectArray("[Ljava/lang/annotation/Annotation;", pa.length);
+      
+      for (int i=0; i<pa.length; i++){
+        int eRef = env.newAnnotationProxies(pa[i]);
+        env.setReferenceArrayElement(paRef, i, eRef);
+      }
+
+      return paRef;
+      
+    } catch (ClinitRequired x){ // be prepared that we might have to initialize respective annotation classes
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return MJIEnv.NULL;
+    }    
+  }
+
+  @MJI
+  public int getParameterAnnotations_____3_3Ljava_lang_annotation_Annotation_2 (MJIEnv env, int mthRef){
+    return getParameterAnnotations( env, getMethodInfo(env,mthRef));
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    StringBuilder sb = new StringBuilder();
+    
+    MethodInfo mi = getMethodInfo(env, objRef);
+
+    sb.append(Modifier.toString(mi.getModifiers()));
+    sb.append(' ');
+
+    sb.append(mi.getReturnTypeName());
+    sb.append(' ');
+
+    sb.append(mi.getClassName());
+    sb.append('.');
+
+    sb.append(mi.getName());
+
+    sb.append('(');
+    
+    String[] at = mi.getArgumentTypeNames();
+    for (int i=0; i<at.length; i++){
+      if (i>0) sb.append(',');
+      sb.append(at[i]);
+    }
+    
+    sb.append(')');
+    
+    int sref = env.newString(sb.toString());
+    return sref;
+  }
+
+  @MJI
+  public boolean equals__Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int mthRef){
+    ElementInfo ei = env.getElementInfo(mthRef);
+    ClassInfo ci = ClassLoaderInfo.getSystemResolvedClassInfo(JPF_java_lang_Class.METHOD_CLASSNAME);
+
+    if (ei.getClassInfo() == ci){
+      MethodInfo mi1 = getMethodInfo(env, objRef);
+      MethodInfo mi2 = getMethodInfo(env, mthRef);
+      if (mi1.getClassInfo() == mi2.getClassInfo()){
+        if (mi1.getName().equals(mi2.getName())){
+          if (mi1.getReturnType().equals(mi2.getReturnType())){
+            byte[] params1 = mi1.getArgumentTypes();
+            byte[] params2 = mi2.getArgumentTypes();
+            if (params1.length == params2.length){
+              for (int i = 0; i < params1.length; i++){
+                if (params1[i] != params2[i]){
+                  return false;
+                }
+              }
+              return true;
+            }
+          }
+        }
+      }
+    }
+    return false;
+  }
+
+  @MJI
+  public int hashCode____I (MJIEnv env, int objRef){
+    MethodInfo mi = getMethodInfo(env, objRef);
+    return mi.getClassName().hashCode() ^ mi.getName().hashCode();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Proxy.java b/src/peers/gov/nasa/jpf/vm/JPF_java_lang_reflect_Proxy.java
new file mode 100644 (file)
index 0000000..5ed941a
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+
+public class JPF_java_lang_reflect_Proxy extends NativePeer {
+  @MJI
+  public int defineClass0 (MJIEnv env, int clsObjRef, int classLoaderRef, int nameRef, int bufferRef, int offset, int length) {  
+    String clsName = env.getStringObject(nameRef);
+    byte[] buffer = env.getByteArrayObject(bufferRef);
+    
+    try {
+      ClassInfo ci = ClassLoaderInfo.getCurrentClassLoader().getResolvedClassInfo( clsName, buffer, offset, length);
+      if (!ci.isRegistered()) {
+        ThreadInfo ti = env.getThreadInfo();
+        ci.registerClass(ti);
+      }
+      return ci.getClassObjectRef();
+      
+    } catch (ClassInfoException cix){
+      env.throwException("java.lang.ClassFormatError", clsName); // <2do> check if this is the right one
+      return MJIEnv.NULL;
+    }
+  }
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLClassLoader.java b/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLClassLoader.java
new file mode 100644 (file)
index 0000000..a85d91d
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+import java.net.MalformedURLException;
+import java.net.URL;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Native peer for java.net.URLClassLoader
+ */
+public class JPF_java_net_URLClassLoader extends JPF_java_lang_ClassLoader{
+
+  static JPFLogger log = JPF.getLogger("class");
+  
+  @MJI
+  public void addURL0__Ljava_lang_String_2__V (MJIEnv env, int objRef, int urlRef) throws MalformedURLException {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    String url = env.getStringObject(urlRef);
+
+    String path = null;
+    URL u = new URL(url);
+    String protocol = u.getProtocol();
+    if(protocol.equals("file")) {
+      path = u.getFile();
+    } else if(protocol.equals("jar")){
+      path = url.substring(url.lastIndexOf(':')+1, url.indexOf('!'));
+    } else {
+      // we don't support other protocols for now!
+      log.warning("unknown path element specification: ", url);
+      return;
+    }
+
+    cl.addClassPathElement(path);
+  }
+
+  @MJI
+  public int findClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objRef, int nameRef) {
+    String typeName = env.getStringObject(nameRef);
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+    ThreadInfo ti = env.getThreadInfo();
+
+    try {
+      ClassInfo ci = cl.getResolvedClassInfo( typeName);
+      if(!ci.isRegistered()) {
+        ci.registerClass(env.getThreadInfo());
+      }
+      // note that we don't initialize yet
+      return ci.getClassObjectRef();
+          
+    } catch (LoadOnJPFRequired rre) { // this classloader has a overridden loadClass 
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+      
+    } catch (ClassInfoException cix){
+      if (cix.getCause() instanceof ClassParseException){
+        env.throwException("java.lang.ClassFormatError", typeName);
+      } else {
+        env.throwException("java.lang.ClassNotFoundException", typeName);
+      }
+      return MJIEnv.NULL;      
+    }
+  }
+
+  @MJI
+  public int findResource0__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef){
+    String rname = env.getStringObject(resRef);
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    String resourcePath = cl.findResource(rname);
+
+    return env.newString(resourcePath);
+  }
+
+  @MJI
+  public int findResources0__Ljava_lang_String_2___3Ljava_lang_String_2 (MJIEnv env, int objRef, int resRef) {
+    String rname = env.getStringObject(resRef);
+
+    ClassLoaderInfo cl = env.getClassLoaderInfo(objRef);
+
+    String[] resources = cl.findResources(rname);
+
+    return env.newStringArray(resources);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLDecoder.java b/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLDecoder.java
new file mode 100644 (file)
index 0000000..c65ab99
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+
+/**
+ * native peer for java.net.URLDecoder forwarding
+ */
+public class JPF_java_net_URLDecoder extends NativePeer {
+
+  @MJI
+  public int decode__Ljava_lang_String_2Ljava_lang_String_2__Ljava_lang_String_2(MJIEnv env, int clsObjRef,
+          int sRef, int encRef){
+    String s = env.getStringObject(sRef);
+    String enc = env.getStringObject(encRef);
+
+    try {
+      String e = URLDecoder.decode(s, enc);
+      return env.newString(e);
+
+    } catch (UnsupportedEncodingException x){
+      env.throwException("java.io.UnsupportedEncodingException", x.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLEncoder.java b/src/peers/gov/nasa/jpf/vm/JPF_java_net_URLEncoder.java
new file mode 100644 (file)
index 0000000..d2d5433
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+
+/**
+ * native peer for java.net.URLEncoder forwarding
+ */
+public class JPF_java_net_URLEncoder extends NativePeer {
+
+  // simple host delegation
+  @MJI
+  public int encode__Ljava_lang_String_2Ljava_lang_String_2__Ljava_lang_String_2(MJIEnv env, int clsObjRef,
+          int sRef, int encRef){
+    String s = env.getStringObject(sRef);
+    String enc = env.getStringObject(encRef);
+
+    try {
+      String e = URLEncoder.encode(s, enc);
+      return env.newString(e);
+
+    } catch (UnsupportedEncodingException x){
+      env.throwException("java.io.UnsupportedEncodingException", x.getMessage());
+      return MJIEnv.NULL;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_security_MessageDigest.java b/src/peers/gov/nasa/jpf/vm/JPF_java_security_MessageDigest.java
new file mode 100644 (file)
index 0000000..f692362
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+public class JPF_java_security_MessageDigest extends NativePeer {
+  
+  MessageDigest[] digests;
+  
+  public JPF_java_security_MessageDigest (Config conf){
+    digests = new MessageDigest[32];
+  }
+  
+  int getNewIndex() {
+    int n = digests.length;
+    for (int i=0; i<n; i++){
+      if (digests[i] == null){
+        return i;
+      }
+    }
+    
+    MessageDigest[] newd = new MessageDigest[n + 32];
+    System.arraycopy(digests,0,newd,0,digests.length);
+    digests = newd;
+    return n;
+  }
+  
+  MessageDigest getDigest (MJIEnv env, int objRef){
+    int id = env.getIntField(objRef, "id");
+    return digests[id];
+  }
+  
+  @MJI
+  public int init0__Ljava_lang_String_2__I (MJIEnv env, int objRef, int algRef) {
+    String algorithm = env.getStringObject(algRef);
+    
+    try {
+      MessageDigest md = MessageDigest.getInstance(algorithm);
+    
+      int id = getNewIndex();
+      digests[id] = md;
+    
+      return id;
+    } catch (NoSuchAlgorithmException x){
+      env.throwException("java.security.NoSuchAlgorithmException", algorithm);
+      return -1;
+    }
+  }
+  
+  @MJI
+  public int digest___3B___3B (MJIEnv env, int objRef, int inputRef){
+    MessageDigest md = getDigest(env, objRef);
+    byte[] input = env.getByteArrayObject(inputRef);
+    
+    byte[] res = md.digest(input);
+    return env.newByteArray(res);
+  }
+
+  @MJI
+  public int digest_____3B (MJIEnv env, int objRef){
+    MessageDigest md = getDigest(env, objRef);    
+    byte[] res = md.digest();
+    return env.newByteArray(res);
+  }
+  
+  @MJI
+  public void finalize____ (MJIEnv env, int objRef){
+    int id = env.getIntField(objRef, "id");
+    digests[id] = null;
+  }
+
+  @MJI
+  public void update___3B__V (MJIEnv env, int objRef, int inputRef){
+    MessageDigest md = getDigest(env, objRef);
+    byte[] input = env.getByteArrayObject(inputRef);
+    md.update(input);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_Bidi.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_Bidi.java
new file mode 100644 (file)
index 0000000..95db6ba
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_java_text_Bidi extends NativePeer {
+
+  @MJI
+  public void $clinit____V (MJIEnv env, int clsObjRef) {
+    // do nothing
+  }
+  
+  @MJI
+  public void nativeBidiChars (MJIEnv env, int clsObjRef,
+                                      int bidiRef, int textRef, int textStart,
+                                      int embeddingsRef, int embeddingsStart,
+                                      int length, int flags) {
+    // <2do> need to forward
+  }
+
+  @MJI
+  public boolean requiresBidi (MJIEnv env, int clsObjRef,
+                                      int textRef, int start, int limit) {
+    // not supported for now
+    return false;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormat.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormat.java
new file mode 100644 (file)
index 0000000..e2e1966
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+/**
+ * this is just the minimal support for DateFormat.parse(String)
+ */
+public class JPF_java_text_DateFormat extends NativePeer {
+
+  DateFormat getInstance (MJIEnv env, int objref) {
+    Format fmt = JPF_java_text_Format.getInstance(env,objref);
+    assert fmt instanceof SimpleDateFormat;
+
+    return (DateFormat)fmt;
+  }
+
+  @MJI
+  public void setTimeZone__Ljava_util_TimeZone_2__V(MJIEnv env, int objref,int timeZoneRef) {
+    String timeZoneId = env.getStringField(timeZoneRef, "ID");
+    TimeZone timeZone = TimeZone.getTimeZone(timeZoneId);
+    DateFormat fmt = getInstance(env,objref);
+    fmt.setTimeZone(timeZone);
+    int calendarRef = env.getReferenceField(objref, "calendar");
+    env.setReferenceField(calendarRef, "zone", timeZoneRef);
+  }
+
+  @MJI
+  public int parse__Ljava_lang_String_2__Ljava_util_Date_2 (MJIEnv env, int objref, int strRef) {
+    DateFormat f = getInstance(env,objref);
+    String s = env.getStringObject(strRef);
+    try {
+      Date d = f.parse(s);
+      long t = d.getTime();
+
+      int dref = env.newObject("java.util.Date");
+      env.setLongField(dref, "fastTime", t);
+      return dref;
+
+    } catch (ClinitRequired x){
+      env.handleClinitRequest(x.getRequiredClassInfo());
+      return 0;
+
+    } catch (ParseException px) {
+      env.throwException("java.text.ParseException", px.getMessage());
+      return 0;
+    }
+  }
+  
+  @MJI
+  public void setLenient__Z__V (MJIEnv env, int objref, boolean isLenient) {
+    DateFormat f = getInstance(env,objref);
+    f.setLenient(isLenient);
+  }
+  
+  @MJI
+  public int format__Ljava_util_Date_2__Ljava_lang_String_2 (MJIEnv env, int objref, int dateRef) {
+    DateFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      Date d = env.getDateObject(dateRef);
+      
+      String s = fmt.format(d);
+      int sref = env.newString(s);
+      return sref;
+    }
+    
+    return MJIEnv.NULL;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormatSymbols.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_DateFormatSymbols.java
new file mode 100644 (file)
index 0000000..dfcbfb7
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.DateFormatSymbols;
+
+public class JPF_java_text_DateFormatSymbols extends NativePeer {
+  @MJI
+  public void initializeData__Ljava_util_Locale_2__V (MJIEnv env, int objRef, int localeRef) {
+    DateFormatSymbols dfs = new DateFormatSymbols();
+    
+    String[] eras = dfs.getEras();
+    env.setReferenceField(objRef, "eras", env.newStringArray(eras));
+    
+    String[] months = dfs.getMonths();
+    env.setReferenceField(objRef, "months", env.newStringArray(months));
+    
+    String[] shortMonths = dfs.getShortMonths();
+    env.setReferenceField(objRef, "shortMonths", env.newStringArray(shortMonths));
+    
+    String[] weekdays = dfs.getWeekdays();
+    env.setReferenceField(objRef, "weekdays", env.newStringArray(weekdays));
+    
+    String[] shortWeekdays = dfs.getShortWeekdays();
+    env.setReferenceField(objRef, "shortWeekdays", env.newStringArray(shortWeekdays));
+    
+    String[] ampms = dfs.getAmPmStrings();
+    env.setReferenceField(objRef, "ampms", env.newStringArray(ampms));
+    
+    String[][] zoneStrings = dfs.getZoneStrings();
+    int aaref = env.newObjectArray("[Ljava.lang.String;", zoneStrings.length);
+    env.setReferenceField(objRef, "zoneStrings", aaref);
+    for (int i=0; i<zoneStrings.length; i++){
+      env.setReferenceArrayElement(aaref, i, env.newStringArray(zoneStrings[i]));
+    }
+    
+    String localPatternChars = dfs.getLocalPatternChars();
+    env.setReferenceField(objRef, "localPatternChars", env.newString(localPatternChars));
+
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormat.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormat.java
new file mode 100644 (file)
index 0000000..c3c500a
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.DecimalFormat;
+import java.text.Format;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+// NOTE - this only works because DecimalFormat is a Format subclass, i.e.
+// the java.text.Format native peer will be initialized first
+// (otherwise we shouldn't depend on static data of other native peers)
+
+public class JPF_java_text_DecimalFormat extends NativePeer {
+
+  static final int INTEGER_STYLE=0;
+  static final int NUMBER_STYLE=1;
+
+  NumberFormat getInstance (MJIEnv env, int objref) {
+    Format fmt = JPF_java_text_Format.getInstance(env,objref);
+    assert fmt instanceof NumberFormat;
+    
+    return (NumberFormat)fmt;
+  }
+  
+  /*
+   * NOTE: if we would directly intercept the ctors, we would have to
+   * explicitly call the superclass ctors here (the 'id' handle gets
+   * initialized in the java.text.Format ctor) 
+   */
+  @MJI
+  public void init0____V (MJIEnv env, int objref) {
+    DecimalFormat fmt = new DecimalFormat();
+    JPF_java_text_Format.putInstance(env,objref,fmt);    
+  }
+  
+  @MJI
+  public void init0__Ljava_lang_String_2__V (MJIEnv env, int objref, int patternref) {
+    String pattern = env.getStringObject(patternref);
+    
+    DecimalFormat fmt = new DecimalFormat(pattern);
+    JPF_java_text_Format.putInstance(env,objref,fmt);    
+  }
+  
+  @MJI
+  public void init0__I__V (MJIEnv env, int objref, int style) {
+    NumberFormat fmt = null;
+    if (style == INTEGER_STYLE) {
+      fmt = NumberFormat.getIntegerInstance();
+    } else if (style == NUMBER_STYLE) {
+      fmt = NumberFormat.getNumberInstance();
+    } else {
+      // unknown style
+      fmt = new DecimalFormat();
+    }
+    
+    JPF_java_text_Format.putInstance(env,objref,fmt);    
+  }
+  
+  @MJI
+  public void setMaximumFractionDigits__I__V (MJIEnv env, int objref, int newValue){
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setMaximumFractionDigits(newValue);
+    }
+  }
+
+  @MJI
+  public void setMaximumIntegerDigits__I__V (MJIEnv env, int objref, int newValue){
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setMaximumIntegerDigits(newValue);
+    }
+  }
+
+  @MJI
+  public void setMinimumFractionDigits__I__V (MJIEnv env, int objref, int newValue){
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setMinimumFractionDigits(newValue);
+    }
+  }
+
+  @MJI
+  public void setMinimumIntegerDigits__I__V (MJIEnv env, int objref, int newValue){
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setMinimumIntegerDigits(newValue);
+    }
+  }
+  
+  @MJI
+  public int format__J__Ljava_lang_String_2 (MJIEnv env, int objref, long number) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      String s = fmt.format(number);
+      int sref = env.newString(s);
+      return sref;
+    }
+    
+    return MJIEnv.NULL;
+  }
+  
+  @MJI
+  public int format__D__Ljava_lang_String_2 (MJIEnv env, int objref, double number) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      String s = fmt.format(number);
+      int sref = env.newString(s);
+      return sref;
+    }
+    
+    return MJIEnv.NULL;
+  }
+
+  @MJI
+  public void setParseIntegerOnly__Z__V(MJIEnv env, int objref, boolean value) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setParseIntegerOnly(value);
+    }
+  }
+
+  @MJI
+  public boolean isParseIntegerOnly____Z(MJIEnv env, int objref) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      return fmt.isParseIntegerOnly();
+    }
+    return false;
+  }
+
+  @MJI
+  public void setGroupingUsed__Z__V(MJIEnv env, int objref, boolean newValue) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      fmt.setGroupingUsed(newValue);
+    }
+  }
+
+  @MJI
+  public boolean isGroupingUsed____Z(MJIEnv env, int objref) {
+    NumberFormat fmt = getInstance(env,objref);
+    if (fmt != null) {
+      return fmt.isGroupingUsed();
+    }
+    return false;
+  }
+
+  @MJI
+  public int parse__Ljava_lang_String_2Ljava_text_ParsePosition_2__Ljava_lang_Number_2(MJIEnv env, int objref,int sourceRef,int parsePositionRef) {
+    String source = env.getStringObject(sourceRef);
+    ParsePosition parsePosition = createParsePositionFromRef(env,parsePositionRef);
+    NumberFormat fmt = getInstance(env,objref);
+    Number number = null;
+    if (fmt != null) {
+      number = fmt.parse(source,parsePosition);
+    }
+    updateParsePositionRef(env,parsePositionRef, parsePosition);
+    return createNumberRefFromNumber(env,number);
+  }
+
+  private static ParsePosition createParsePositionFromRef(MJIEnv env,int parsePositionRef) {
+    int index = env.getIntField(parsePositionRef, "index");
+    int errorIndex = env.getIntField(parsePositionRef, "errorIndex");
+    ParsePosition ps = new ParsePosition(index);
+    ps.setErrorIndex(errorIndex);
+    return ps;
+  }
+
+  private static void updateParsePositionRef(MJIEnv env,int parsePositionRef, ParsePosition parsePosition) {
+    env.setIntField(parsePositionRef, "index", parsePosition.getIndex());
+    env.setIntField(parsePositionRef, "errorIndex", parsePosition.getErrorIndex());
+  }
+
+  private static int createNumberRefFromNumber(MJIEnv env,Number number) {
+    int numberRef = MJIEnv.NULL;
+    if(number instanceof Double) {
+      numberRef = env.newObject("java.lang.Double");
+      env.setDoubleField(numberRef, "value", number.doubleValue());
+    } else if(number instanceof Long) {
+      numberRef = env.newObject("java.lang.Long");
+      env.setLongField(numberRef, "value", number.longValue());
+    }
+    return numberRef;
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormatSymbols.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_DecimalFormatSymbols.java
new file mode 100644 (file)
index 0000000..292a5b4
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.DecimalFormatSymbols;
+
+/**
+ * MJI NativePeer class for java.text.DecimalFormatSymbols library abstraction
+ * 
+ * we need to intercept the initialization because it is requires
+ * file io (properties) based on the Locale
+ */
+public class JPF_java_text_DecimalFormatSymbols extends NativePeer {
+  @MJI
+  public void initialize__Ljava_util_Locale_2__V (MJIEnv env, int objRef, int localeRef) {
+    DecimalFormatSymbols dfs = new DecimalFormatSymbols();
+    
+    env.setCharField(objRef,"patternSeparator", dfs.getPatternSeparator());
+    env.setCharField(objRef,"percent", dfs.getPercent());
+    env.setCharField(objRef,"digit", dfs.getDigit());
+    env.setCharField(objRef,"minusSign", dfs.getMinusSign());
+    env.setCharField(objRef,"perMill", dfs.getPerMill());
+    env.setReferenceField(objRef,"infinity", env.newString(dfs.getInfinity()));
+    env.setReferenceField(objRef,"NaN", env.newString(dfs.getNaN()));
+    env.setReferenceField(objRef,"currencySymbol", env.newString(dfs.getCurrencySymbol()));
+    env.setCharField(objRef,"monetarySeparator", dfs.getMonetaryDecimalSeparator());
+
+    env.setCharField(objRef,"decimalSeparator", dfs.getDecimalSeparator());
+    env.setCharField(objRef,"groupingSeparator", dfs.getGroupingSeparator());
+    env.setCharField(objRef,"exponential", 'E'); // getExponentialSymbol() is not public
+  }
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_Format.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_Format.java
new file mode 100644 (file)
index 0000000..5672481
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.Format;
+import java.util.HashMap;
+
+/**
+ * native peer for java.text.Format delegation. This is the place where
+ * we keep a map between real formatters and their JPF counterparts
+ * (which are just proxies)
+ */
+public class JPF_java_text_Format extends NativePeer {
+
+  static HashMap<Integer,Format> formatters;
+
+  public static boolean init (Config conf){
+    formatters = new HashMap<Integer,Format>();
+    return true;
+  }
+  
+  static void putInstance (MJIEnv env, int objref, Format fmt) {
+    int id = env.getIntField(objref,  "id");
+    formatters.put(new Integer(id), fmt);
+  }
+
+  static Format getInstance (MJIEnv env, int objref) {
+    // <2do> that's braindead
+    
+    int id = env.getIntField(objref,  "id");
+    return formatters.get(id);
+  }
+
+  
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_text_SimpleDateFormat.java b/src/peers/gov/nasa/jpf/vm/JPF_java_text_SimpleDateFormat.java
new file mode 100644 (file)
index 0000000..42b2740
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.text.DateFormat;
+import java.text.Format;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+/**
+ * (incomplete) native peer for SimpleDateFormat. See Format for details
+ * about native formatter delegation
+ */
+public class JPF_java_text_SimpleDateFormat extends NativePeer {
+
+  SimpleDateFormat getInstance (MJIEnv env, int objref) {
+    Format fmt = JPF_java_text_Format.getInstance(env,objref);
+    assert fmt instanceof SimpleDateFormat;
+
+    return (SimpleDateFormat)fmt;
+  }
+
+  @MJI
+  public void init0____V (MJIEnv env, int objref) {
+    SimpleDateFormat fmt = new SimpleDateFormat();
+    JPF_java_text_Format.putInstance(env,objref,fmt);
+  }
+
+  @MJI
+  public void init0__Ljava_lang_String_2__V (MJIEnv env, int objref, int patternref) {
+    String pattern = env.getStringObject(patternref);
+
+    SimpleDateFormat fmt = new SimpleDateFormat(pattern);
+    JPF_java_text_Format.putInstance(env,objref,fmt);
+  }
+
+  @MJI
+  public void init0__II__V (MJIEnv env, int objref, int timeStyle, int dateStyle) {
+    // we are lost here - can't call this SimpleDateFormat ctor because it's package private
+    // (this is called - and has to be intercepted - from the DateFormat.getInstance() factory)
+
+    DateFormat fmt = null;
+
+    if (timeStyle < 0) {
+      fmt = DateFormat.getDateInstance(dateStyle);
+    } else if (dateStyle < 0) {
+      fmt = DateFormat.getTimeInstance(timeStyle);
+    } else {
+      fmt = DateFormat.getDateTimeInstance(dateStyle, timeStyle);
+    }
+
+    JPF_java_text_Format.putInstance(env,objref,fmt);
+  }
+
+  @MJI
+  public int format0 (MJIEnv env, int objref, long dateTime) {
+    Date date = new Date(dateTime);
+    SimpleDateFormat f = getInstance(env,objref);
+    String s = f.format(date);
+    return env.newString(s);
+  }
+  
+  @MJI
+  public void applyPattern__Ljava_lang_String_2__V (MJIEnv env, int objRef, int patternRef) {
+    SimpleDateFormat format = getInstance (env, objRef);
+    String pattern = env.getStringObject(patternRef);
+    format.applyPattern(pattern);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_Calendar.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_Calendar.java
new file mode 100644 (file)
index 0000000..37f2864
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.Calendar;
+import java.util.Locale;
+
+public class JPF_java_util_Calendar extends NativePeer {
+
+  @MJI
+  public void setWeekCountData__Ljava_util_Locale_2__ (MJIEnv env, int objref, int localeRef){
+    Locale locale = JPF_java_util_Locale.getLocale(env, localeRef);
+    Calendar c = Calendar.getInstance(locale);
+    
+    int n = c.getFirstDayOfWeek();
+    env.setIntField(objref, "firstDayOfWeek", n);
+    
+    n = c.getMinimalDaysInFirstWeek();
+    env.setIntField(objref, "minimalDaysInFirstWeek", n);
+  }
+  
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_Date.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_Date.java
new file mode 100644 (file)
index 0000000..6fadaa7
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.Date;
+
+public class JPF_java_util_Date extends NativePeer {
+
+  static Date getDate (MJIEnv env, int dateRef){
+    
+    //<2do> that doesn't handle BaseCalendar.Date cdate yet
+    long t = env.getLongField(dateRef, "fastTime");
+    return new Date(t);
+  }
+
+  // avoid all the Calendar, TimeZone, CharSequence etc. frenzy just because
+  // of a little Date conversion (that probably is only used in a print)
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int dateRef){
+    Date d = getDate(env,dateRef);
+    String s = d.toString();
+
+    int sRef = env.newString(s);
+    return sRef;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_Locale.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_Locale.java
new file mode 100644 (file)
index 0000000..8fa4ae3
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.Locale;
+
+public class JPF_java_util_Locale extends NativePeer {
+
+  static Locale getLocale (MJIEnv env, int locref) {
+
+    //--- check first if it's one of the standard locales (ci is obviously loaded at this point
+    ClassInfo ci = env.getClassInfo(locref);  // Locale is final, so we can do this
+    ElementInfo sei = ci.getStaticElementInfo();
+
+    if (locref == sei.getReferenceField("US")) return Locale.US;
+    if (locref == sei.getReferenceField("GERMAN")) return Locale.GERMAN;
+    if (locref == sei.getReferenceField("ENGLISH")) return Locale.ENGLISH;
+    if (locref == sei.getReferenceField("FRENCH")) return Locale.FRENCH;
+    if (locref == sei.getReferenceField("JAPANESE")) return Locale.JAPANESE;
+    if (locref == sei.getReferenceField("CHINESE")) return Locale.CHINESE;
+    //... we should have a bunch more
+
+
+    //--- if it wasn't any of these, get the fields and just construct it
+
+    String country, language, variant;
+    FieldInfo fiBase = ci.getInstanceField("baseLocale");
+    if (fiBase != null){ // Java >= 1.7
+      int baseLocref = env.getReferenceField(locref, fiBase);
+      country = env.getStringObject(env.getReferenceField(baseLocref,"region"));
+      language = env.getStringObject(env.getReferenceField(baseLocref, "language"));
+      variant = env.getStringObject(env.getReferenceField(baseLocref, "variant"));
+            
+    } else {  // Java < 1.7
+      country = env.getStringObject(env.getReferenceField(locref,"country"));
+      language = env.getStringObject(env.getReferenceField(locref, "language"));
+      variant = env.getStringObject(env.getReferenceField(locref, "variant"));
+    }
+    
+    Locale locale = new Locale(language,country,variant); 
+    return locale;
+  }
+  
+  @MJI
+  public int getDisplayName__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objref, int locref) {
+    Locale locale = getLocale(env, locref);
+    String name = locale.getDisplayName();
+    return env.newString(name);
+  }
+  
+  @MJI
+  public int getDisplayVariant__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objref, int locref) {
+    Locale locale = getLocale(env, locref);
+    String variant = locale.getDisplayVariant();
+    return env.newString(variant);    
+  }
+  
+  @MJI
+  public int getDisplayCountry__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objref, int locref) {
+    Locale locale = getLocale(env, locref);
+    String country = locale.getDisplayCountry();
+    return env.newString(country);
+
+  }
+
+  @MJI
+  public int getDisplayLanguage__Ljava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objref, int locref) {
+    Locale locale = getLocale(env, locref);
+    String language = locale.getDisplayLanguage();
+    return env.newString(language);
+  }
+
+  @MJI
+  public int getISO3Country____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    Locale locale = getLocale(env, objref);
+    String s = locale.getISO3Country();
+    return env.newString(s);    
+  }
+
+  @MJI
+  public int getISO3Language____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    Locale locale = getLocale(env, objref);
+    String s = locale.getISO3Language();
+    return env.newString(s);
+  }
+
+  //--- the static ones
+  @MJI
+  public int getISOCountries_____3Ljava_lang_String_2 (MJIEnv env, int clsref) {
+    String[] s = Locale.getISOCountries();
+
+    int aref = env.newObjectArray("java.lang.String", s.length);
+    for (int i=0; i<s.length; i++) {
+      env.setReferenceArrayElement(aref, i, env.newString(s[i]));
+    }
+    
+    return aref;
+  }
+  
+  @MJI
+  public int getISOLanguages_____3Ljava_lang_String_2 (MJIEnv env, int clsref) {
+    String[] s = Locale.getISOLanguages();
+
+    int aref = env.newObjectArray("java.lang.String", s.length);
+    for (int i=0; i<s.length; i++) {
+      env.setReferenceArrayElement(aref, i, env.newString(s[i]));
+    }
+    
+    return aref;    
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_Random.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_Random.java
new file mode 100644 (file)
index 0000000..0b53ffc
--- /dev/null
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ConfigChangeListener;
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.JPF_gov_nasa_jpf_vm_Verify;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.lang.reflect.Field;
+import java.util.Random;
+import java.util.concurrent.atomic.AtomicLong;
+
+import sun.misc.Unsafe;
+
+/**
+ * MJI NativePeer class for java.util.Random library abstraction
+ *
+ * model - peer delegation is done via restoring a singleton, which unfortunately
+ * has to resort to the rather nasty Unsafe mechanism because the required
+ * random internals are private. We could use per-instance native objects
+ * stored as object attributes, but that would only partly solve the problem
+ * because we still would have to backtrack the internal state of such objects
+ */
+@SuppressWarnings("sunapi")
+public class JPF_java_util_Random extends NativePeer {
+
+  static class Delegatee extends Random {
+    @Override
+       public int next (int nBits){
+      return super.next(nBits);
+    }
+  }
+  
+  // we need this because cg.enumerate_random might be set on demand
+  static class ConfigListener implements ConfigChangeListener {
+    JPF_java_util_Random nativeRandom;
+
+    public ConfigListener(JPF_java_util_Random nativeRandom) {
+      this.nativeRandom = nativeRandom;
+    }
+
+    @Override
+    public void propertyChanged(Config config, String key, String oldValue, String newValue) {
+      if ("cg.enumerate_random".equals(key)) {
+        nativeRandom.setEnumerateRandom(config);
+      }
+    }
+    
+    @Override
+    public void jpfRunTerminated(Config config){
+      config.removeChangeListener(this);
+    }
+  }
+
+  boolean enumerateRandom;
+  
+  // those are only used if enumerateRandom is not set, i.e. we delegate to the host VM
+  boolean reproducibleRandom;
+  long constantSeed;
+  int[] defaultIntSet; // in case we have an nextInt(), i.e. an unspecified upper boundary
+  long[] defaultLongSet;
+  double[] defaultDoubleSet;
+  float[] defaultFloatSet;
+
+  // since peer methods are atomic, we just keep one delegator instead of per-object,
+  // which would have to rely on attributes and still require storing/restoring
+  // the seed state with nasty Unsafe
+  static Delegatee delegatee = new Delegatee();
+  
+  // this is bad stuff we need to set/retrieve the Random.seed value. We only have
+  // a choice between a rock and a hard place here - either we depend on this
+  // field and sun.misc.Unsafe, or we duplicate the algorithms. The hard place
+  // seems worse than the rock
+  private static Unsafe unsafe;
+  private static long seedFieldOffset;
+  
+  static {
+    try {
+      // Unsafe.getUnsafe() can only be called from a SystemClassLoaderContext
+      Field singletonField = Unsafe.class.getDeclaredField("theUnsafe");
+      singletonField.setAccessible(true);
+      unsafe = (Unsafe)singletonField.get(null);
+      
+      seedFieldOffset = unsafe.objectFieldOffset(Random.class.getDeclaredField("seed"));
+    } catch (Exception ex) {
+      throw new JPFException("cannot access java.util.Random internals: " + ex); 
+    }
+  }
+  
+  private static void setNativeSeed (Random rand, long seed) {
+    AtomicLong al = (AtomicLong) unsafe.getObject(rand, seedFieldOffset);
+    al.set(seed);
+  }
+
+  private static long getNativeSeed (Random rand){
+    AtomicLong al = (AtomicLong) unsafe.getObject(rand, seedFieldOffset);
+    return al.longValue();
+  }
+
+  public JPF_java_util_Random (Config conf) {
+    setEnumerateRandom(conf);
+    conf.addChangeListener(new ConfigListener(this));
+    
+    reproducibleRandom = conf.getBoolean("vm.reproducible_random", true);
+    constantSeed = conf.getLong("vm.random_seed", 42);
+    defaultIntSet = conf.getIntArray("vm.random_ints", Integer.MIN_VALUE, 0, Integer.MAX_VALUE);
+    defaultDoubleSet = conf.getDoubleArray("vm.random_doubles", Double.MIN_VALUE, 0, Double.MAX_VALUE);  
+    defaultLongSet = conf.getLongArray("vm.random_longs", Long.MIN_VALUE, 0, Long.MAX_VALUE);  
+    defaultFloatSet = conf.getFloatArray("vm.random_floats", Float.MIN_VALUE, 0, Float.MAX_VALUE);  
+  }
+
+  void setEnumerateRandom (Config conf) {
+    enumerateRandom = conf.getBoolean("cg.enumerate_random", false);
+
+    if (enumerateRandom){
+      JPF_gov_nasa_jpf_vm_Verify.init(conf);
+    }    
+  }
+  
+  long computeDefaultSeed(){
+    Random rand = (reproducibleRandom) ? new Random(constantSeed) : new Random();
+    return getNativeSeed( rand);
+  }
+
+  static void storeSeed (MJIEnv env, int objRef, long seed){
+    env.setLongField(objRef, "seed", seed);
+  }
+
+  static long getSeed (MJIEnv env, int objRef){
+    return env.getLongField(objRef, "seed");
+  }
+  
+  static void restoreRandomState (MJIEnv env, int objRef, Random rand){
+    long seed = getSeed( env, objRef);
+    setNativeSeed( rand, seed);
+  }
+  
+  static void storeRandomState (MJIEnv env, int objRef, Random rand){
+    long seed = getNativeSeed( rand);
+    storeSeed( env, objRef, seed);
+  }
+  
+  
+  //--- the publics
+
+  @MJI
+  public void $init____V (MJIEnv env, int objRef){
+    long seed = computeDefaultSeed();
+    storeSeed( env, objRef, seed);
+  }
+  
+  @MJI
+  public void $init__J__V (MJIEnv env, int objRef, long seedStarter){
+    // note - the provided seedStarter is modified by java.util.Random, it is
+    // NOT the internal value that is consecutively used
+    Random rand = new Random(seedStarter);
+    storeRandomState(env, objRef, rand);    
+  }
+  
+  @MJI
+  public void setSeed__J__V (MJIEnv env, int objRef, long seedStarter){
+    // my, what an effort to change a long.
+    restoreRandomState( env, objRef, delegatee);
+    delegatee.setSeed(seedStarter); // compute the new internal value
+    storeRandomState(env, objRef, delegatee);    
+  }
+  
+  @MJI
+  public boolean nextBoolean____Z (MJIEnv env, int objRef){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getBoolean____Z(env,-1);
+
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      boolean ret = delegatee.nextBoolean();
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }
+  }
+  
+  @MJI
+  public int nextInt__I__I (MJIEnv env, int objRef, int n){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getInt__II__I(env,-1,0,n-1);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      int ret = delegatee.nextInt(n);
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }
+  }
+  
+  @MJI
+  public int nextInt____I (MJIEnv env, int objRef){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getIntFromList(env, defaultIntSet);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      int ret = delegatee.nextInt();
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }
+  }
+  
+  @MJI
+  public int next__I__I (MJIEnv env, int objRef, int nBits){
+    if (enumerateRandom){
+      // <2do> we can't do this with an interval since it most likely would explode our state space
+      return JPF_gov_nasa_jpf_vm_Verify.getIntFromList(env, defaultIntSet);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      int ret = delegatee.next( nBits);
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }
+  }
+  
+  @MJI
+  public void nextBytes___3B__V (MJIEnv env, int objRef, int dataRef){
+    // <2do> this one is an even worse state exploder. We could use cascaded CGs,
+    // but chances are this really kills us, so we just ignore 'enumerateRandom' for now
+    
+    int n = env.getArrayLength(dataRef);
+    byte[] data = new byte[n];
+
+    restoreRandomState(env, objRef, delegatee);
+    delegatee.nextBytes(data);
+    storeRandomState(env, objRef, delegatee);
+
+    for (int i = 0; i < n; i++) {
+      env.setByteArrayElement(dataRef, i, data[i]);
+    }
+  }
+  
+  @MJI
+  public long nextLong____J (MJIEnv env, int objRef){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getLongFromList(env, defaultLongSet);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      long ret = delegatee.nextLong();
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }    
+  }
+
+  @MJI
+  public float nextFloat____F (MJIEnv env, int objRef){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getFloatFromList(env, defaultFloatSet);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      float ret = delegatee.nextFloat();
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }    
+  }
+
+  @MJI
+  public double nextDouble____D (MJIEnv env, int objRef){
+    if (enumerateRandom){
+      return JPF_gov_nasa_jpf_vm_Verify.getDoubleFromList(env, defaultDoubleSet);
+      
+    } else {
+      restoreRandomState(env, objRef, delegatee);
+      double ret = delegatee.nextDouble();
+      storeRandomState(env, objRef, delegatee);
+      return ret;
+    }    
+  }
+
+  @MJI
+  public double nextGaussian____D (MJIEnv env, int objRef){
+    // <2do> we don't support this yet, neither for enumerateRandom nor
+    // delegation (which would require an additional 'haveNextGaussian' state)
+    restoreRandomState(env, objRef, delegatee);
+    double ret = delegatee.nextGaussian();
+    storeRandomState(env, objRef, delegatee);
+    return ret;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_ResourceBundle.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_ResourceBundle.java
new file mode 100644 (file)
index 0000000..9493309
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import java.util.List;
+
+/**
+ * native peer for ResourceBundle
+ */
+public class JPF_java_util_ResourceBundle extends NativePeer {
+
+  @MJI
+  public int getClassContext_____3Ljava_lang_Class_2 (MJIEnv env, int clsRef){
+    ThreadInfo ti = env.getThreadInfo();
+
+    List<StackFrame> list = ti.getInvokedStackFrames();
+    int aRef = env.newObjectArray("java.lang.Class", list.size());
+
+    int j=0;
+    for (StackFrame frame : list){
+      MethodInfo mi = frame.getMethodInfo();
+      ClassInfo ci = mi.getClassInfo();
+      int clsObjRef = ci.getClassObjectRef();
+      env.setReferenceArrayElement(aRef, j++, clsObjRef);
+    }
+
+    return aRef;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_TimeZone.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_TimeZone.java
new file mode 100644 (file)
index 0000000..3ffd665
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * native peer for java.util.TimeZone
+ * 
+ * this is a (mostly) delegating implementation that became necessary because TimeZone has been
+ * considerably changed in Java 1.7 (OpenJDK) and we need a version that is compatible with
+ * pre and post 1.7 releases
+ */
+public class JPF_java_util_TimeZone extends NativePeer {
+
+
+  //--- internals
+  static TimeZone tz; // only used within (atomic) peer methods so that we don't have to instantiate all the time
+  
+  // we have to keep optional defaults here so that we don't change the host environment
+  static String defaultID;
+  static int defaultRawOffset;
+  
+  static {
+    getHostDefaultValues();
+  }
+  
+  private static TimeZone getTimeZone (MJIEnv env, int objRef){
+    int rawOffset = env.getIntField(objRef, "rawOffset");
+    tz.setRawOffset(rawOffset);
+    return tz;
+  }
+
+  //--- native methods
+  
+  //--- the factory methods
+  @MJI
+  public int getTimeZone__Ljava_lang_String_2__Ljava_util_TimeZone_2 (MJIEnv env, int clsObjRef, int idRef){
+    String id = env.getStringObject(idRef);
+    TimeZone tz = TimeZone.getTimeZone(id);
+    
+    int rawOffset = tz.getRawOffset();
+    String realId = tz.getID(); // could have been changed if id was unknown
+    if (!realId.equals(id)){
+      idRef = env.newString(realId);
+    }
+
+    int tzRef = env.newObject("java.util.TimeZone");
+    env.setReferenceField(tzRef, "ID", idRef);
+    env.setIntField(tzRef, "rawOffset", rawOffset);
+    
+    return tzRef;
+  }
+  
+  static void getHostDefaultValues(){
+    tz = TimeZone.getDefault();
+    defaultID = tz.getID();
+    defaultRawOffset = tz.getRawOffset();
+  }
+  
+  @MJI
+  public int createDefaultZone____Ljava_util_TimeZone_2 (MJIEnv env, int clsObjRef){
+    int idRef = env.newString(defaultID);
+
+    int tzRef = env.newObject("java.util.TimeZone");
+    env.setReferenceField(tzRef, "ID", idRef);
+    env.setIntField(tzRef, "rawOffset", defaultRawOffset);
+    
+    return tzRef;
+  }
+
+  @MJI
+  public void setDefaultValues__Ljava_util_TimeZone_2 (MJIEnv env, int clsObjRef, int tzRef){
+    defaultID = env.getStringField(tzRef, "ID");
+    defaultRawOffset = env.getIntField( tzRef, "rawOffset");
+  }
+  
+  //--- the ID queries
+  @MJI
+  public int getAvailableIDs_____3Ljava_lang_String_2 (MJIEnv env, int clsObjRef){
+    String[] ids = TimeZone.getAvailableIDs();
+    return env.newStringArray(ids);
+  }
+  
+  @MJI
+  public int getAvailableIDs__I___3Ljava_lang_String_2 (MJIEnv env, int clsObjRef, int rawOffset){
+    String[] ids = TimeZone.getAvailableIDs(rawOffset);
+    return env.newStringArray(ids);    
+  }
+
+  @MJI
+  public void setID__Ljava_lang_String_2__V (MJIEnv env, int objRef, int idRef){
+    String id = env.getStringObject(idRef);
+    TimeZone tz = TimeZone.getTimeZone(id);
+    
+    int rawOffset = tz.getRawOffset();
+    String realId = tz.getID(); // could have been changed if id was unknown
+    if (!realId.equals(id)){
+      idRef = env.newString(realId);
+    }
+    
+    env.setReferenceField(objRef, "ID", idRef);
+    env.setIntField(objRef, "rawOffset", rawOffset);
+  }
+  
+  @MJI
+  public int getOffset__IIIIII__I (MJIEnv env, int objRef,
+      int era, int year, int month, int day, int dayOfWeek, int milliseconds){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.getOffset(era, year, month, day, dayOfWeek, milliseconds);
+  }
+
+  @MJI
+  public int getOffset__J__I (MJIEnv env, int objRef, long date){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.getOffset(date);
+  }
+  
+  // unfortunately, this is not public in Java 1.7, so we can't delegate w/o reflection
+  @MJI
+  public int getOffsets__J_3I__I (MJIEnv env, int objRef, long date, int offsetsRef){
+    TimeZone tz = getTimeZone( env, objRef);
+    
+    int rawOffset = tz.getRawOffset();
+    int dstOffset = 0;
+    if (tz.inDaylightTime(new Date(date))) {
+      dstOffset = tz.getDSTSavings();
+    }
+    
+    if (offsetsRef != MJIEnv.NULL) {
+      env.setIntArrayElement( offsetsRef, 0, rawOffset);
+      env.setIntArrayElement( offsetsRef, 1, dstOffset);
+    }
+    
+    return (rawOffset + dstOffset);
+  }
+
+  @MJI
+  public boolean inDaylightTime__J__Z (MJIEnv env, int objRef, long time){
+    Date date = new Date(time);
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.inDaylightTime(date);
+  }
+  
+  @MJI
+  public boolean useDaylightTime____Z (MJIEnv env, int objRef){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.useDaylightTime();
+  }
+
+  @MJI
+  public int getDSTSavings____I (MJIEnv env, int objRef){
+    TimeZone tz = getTimeZone( env, objRef);
+    return tz.getDSTSavings();    
+  }
+
+  @MJI
+  public int getDisplayName__ZILjava_util_Locale_2__Ljava_lang_String_2 (MJIEnv env, int objRef,
+                                       boolean daylight, int style, int localeRef) {
+    TimeZone tz = getTimeZone(env, objRef);
+    Locale displayLocale = JPF_java_util_Locale.getLocale(env, localeRef);
+    String s = tz.getDisplayName(daylight, style, displayLocale);
+    
+    int sref = env.newString(s);
+    return sref;
+  }
+  
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_Exchanger.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_Exchanger.java
new file mode 100644 (file)
index 0000000..459851d
--- /dev/null
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.InstructionState;
+
+/**
+ * peer class for java.util.concurrent.Exchanger
+ */
+public class JPF_java_util_concurrent_Exchanger extends NativePeer {
+
+  static class ExchangeState extends InstructionState {
+    int exchangeRef; // has to be a ref because the ElementInfo is modified
+    boolean isWaiter;
+
+    static ExchangeState createWaiterState (MJIEnv env, int exchangeRef){
+      ExchangeState s = new ExchangeState();
+      
+      s.exchangeRef = exchangeRef;
+      s.isWaiter = true;
+      return s;
+    }
+    
+    static ExchangeState createResponderState (MJIEnv env, int exchangeRef){
+      ExchangeState s = new ExchangeState();
+      
+      s.exchangeRef = exchangeRef;
+      s.isWaiter = false;
+      return s;      
+    }
+  }
+  
+  ElementInfo createExchangeObject (MJIEnv env, int waiterDataRef) throws ClinitRequired {
+    ElementInfo ei = env.newElementInfo("java.util.concurrent.Exchanger$Exchange");
+    ei.setReferenceField("waiterData", waiterDataRef);
+    ei.setReferenceField("waiterThread", env.getThreadInfo().getThreadObjectRef());
+    return ei;
+  }
+  
+  private int repeatInvocation (MJIEnv env, StackFrame frame, ElementInfo exchange, ExchangeState state){
+    frame.addFrameAttr(state);
+    env.registerPinDown(exchange);
+    env.repeatInvocation();
+    return MJIEnv.NULL;
+  }
+  
+  //--- native methods
+  
+  @MJI
+  public int exchange__Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int objRef, int dataRef){
+    return exchange0__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, objRef, dataRef, -1L);
+  }
+    
+  @MJI
+  public int exchange0__Ljava_lang_Object_2J__Ljava_lang_Object_2 (MJIEnv env, int objRef, int dataRef, long timeoutMillis) {
+    ThreadInfo ti = env.getThreadInfo();
+    StackFrame frame = ti.getModifiableTopFrame();
+    ExchangeState state = frame.getFrameAttr(ExchangeState.class);
+    long to = (timeoutMillis <0) ? 0 : timeoutMillis;
+
+    if (state == null){ // first time for waiter or responder
+      int eRef = env.getReferenceField(objRef, "exchange");
+      
+      if (eRef == MJIEnv.NULL){ // first waiter, this has to block unless there is a timeout value of 0
+        ElementInfo eiExchanger;
+
+        try {
+          eiExchanger = createExchangeObject(env, dataRef);
+        } catch (ClinitRequired x){
+          // need to come back after class init
+          env.repeatInvocation();
+          return MJIEnv.NULL;
+        }
+
+        eRef = eiExchanger.getObjectRef();
+        env.setReferenceField(objRef, "exchange", eRef);
+        
+        // timeout semantics differ - Object.wait(0) waits indefinitely, whereas here it
+        // should throw immediately. We use -1 as non-timeout value
+        if (timeoutMillis == 0) {
+          env.throwException("java.util.concurrent.TimeoutException");
+          return MJIEnv.NULL;
+        }
+
+        eiExchanger.wait(ti, to, false);  // lockfree wait
+        
+        if (ti.getScheduler().setsWaitCG(ti, to)) {
+          return repeatInvocation(env, frame, eiExchanger, ExchangeState.createWaiterState(env, eRef));
+        } else {
+          throw new JPFException("blocked exchange() waiter without transition break");
+        }
+        
+      } else { // first responder (can reschedule)
+        ElementInfo ei = ti.getModifiableElementInfo(eRef);        
+        ei.setReferenceField("responderData", dataRef);
+        state = ExchangeState.createResponderState(env, eRef);
+        
+        if (ei.getBooleanField("waiterTimedOut")){
+          // depending on own timeout, this might deadlock because the waiter already timed out 
+          ei.wait(ti, to, false);
+
+          if (ti.getScheduler().setsWaitCG(ti, to)) {
+            return repeatInvocation(env, frame, ei, state);
+          } else {
+            throw new JPFException("blocked exchange() responder without transition break");
+          }          
+        }
+
+        // if we get here, the waiter is still waiting and we return right away
+                
+        boolean didNotify = ei.notifies(env.getSystemState(), ti, false); // this changes the tiWaiter status
+        env.setReferenceField(objRef, "exchange", MJIEnv.NULL); // re-arm Exchange object
+                
+        if (ti.getScheduler().setsNotifyCG(ti, didNotify)){
+          return repeatInvocation(env, frame, ei, state);
+        }
+        
+        return ei.getReferenceField("waiterData");
+      }
+      
+    } else { // re-execution(s)
+      ElementInfo eiExchanger = env.getElementInfo(state.exchangeRef);
+
+      int retRef = MJIEnv.NULL;
+      
+      if (ti.isInterrupted(true)) {
+        env.throwException("java.lang.InterruptedException");
+
+      } else if (ti.isTimedOut()){
+        if (state.isWaiter) {
+          eiExchanger = eiExchanger.getModifiableInstance();
+          eiExchanger.setBooleanField("waiterTimedOut", true);
+
+          // we are still lockfree waiting on the exChanger object
+          eiExchanger.notifies(env.getSystemState(), ti, false);
+        }
+
+        env.throwException("java.util.concurrent.TimeoutException");
+        
+      } else {
+        retRef = eiExchanger.getReferenceField( state.isWaiter ? "responderData" : "waiterData");
+      }
+      
+      //-- processed
+      frame.removeFrameAttr(state);
+      eiExchanger = eiExchanger.getModifiableInstance();
+      env.releasePinDown(eiExchanger);
+      return retRef;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicInteger.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicInteger.java
new file mode 100644 (file)
index 0000000..0c229c2
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+* native peer for java.util.concurrent.atomic.AtomicInteger
+* this implementation just cuts off native methods
+*/
+public class JPF_java_util_concurrent_atomic_AtomicInteger extends NativePeer {
+
+  @MJI
+  public void $clinit____V (MJIEnv env, int rcls) {
+    // don't let this one pass, it calls native methods from non-public Sun classes
+  }
+  @MJI
+  public boolean compareAndSet__II__Z (MJIEnv env, int objRef, int expect, int update){
+    int value = env.getIntField(objRef, "value");
+    if (value == expect){
+      env.setIntField(objRef, "value", update);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  
+  @MJI
+  public int getAndAdd__I__I (MJIEnv env, int objRef, int delta) {
+    int value = env.getIntField(objRef, "value");
+    env.setIntField(objRef, "value", value + delta);
+    return value;
+  }
+  
+  @MJI
+  public int getAndIncrement____I (MJIEnv env, int objRef) {
+    int value = env.getIntField(objRef, "value");
+    env.setIntField(objRef, "value", value + 1);
+    return value;
+  }
+  
+  @MJI
+  public int getAndDecrement____I (MJIEnv env, int objRef) {
+    int value = env.getIntField(objRef, "value");
+    env.setIntField(objRef, "value", value - 1);
+    return value;
+  }
+  
+  @MJI
+  public void lazySet__I__V (MJIEnv env, int objRef, int newValue) {
+    env.setIntField(objRef, "value", newValue);
+  }
+
+  @MJI
+  public int getAndSet__I__I (MJIEnv env, int objRef, int newValue) {
+    int value = env.getIntField(objRef, "value");
+    env.setIntField(objRef, "value", newValue);
+    return value;
+  }
+
+  @MJI
+  public boolean weakCompareAndSet__II__Z (MJIEnv env, int objRef, int expect, int update) {
+    int value = env.getIntField(objRef, "value");
+    if (value == expect){
+      env.setIntField(objRef, "value", update);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  
+  @MJI
+  public int incrementAndGet____I (MJIEnv env, int objRef) {
+    int value = env.getIntField(objRef, "value");
+    value++;
+    env.setIntField(objRef, "value", value);
+    return value;
+  }
+  
+  @MJI
+  public int decrementAndGet____I (MJIEnv env, int objRef) {
+    int value = env.getIntField(objRef, "value");
+    value--;
+    env.setIntField(objRef, "value", value);
+    return value;
+  }
+  
+  @MJI
+  public int addAndGet__I__I (MJIEnv env, int objRef, int delta) {
+    int value = env.getIntField(objRef, "value");
+    value += delta;
+    env.setIntField(objRef, "value", value);
+    return value;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerArray.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerArray.java
new file mode 100644 (file)
index 0000000..3864c3b
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for java.util.concurrent.atomic.AtomicIntegerArray
+ */
+public class JPF_java_util_concurrent_atomic_AtomicIntegerArray extends NativePeer {
+
+  @MJI
+  public int getNative__I__I (MJIEnv env, int objRef, int index) {
+    int arrayRef = env.getReferenceField(objRef, "array");
+    return env.getIntArrayElement(arrayRef, index);
+  }
+
+  @MJI
+  public boolean compareAndSetNative__III__Z (MJIEnv env, int objRef, int index, int expect, int update){
+    int arrayRef = env.getReferenceField(objRef, "array");
+    int value = env.getIntArrayElement(arrayRef, index);
+    if (value == expect) {
+      env.setIntArrayElement(arrayRef, index, update);
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerFieldUpdater.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicIntegerFieldUpdater.java
new file mode 100644 (file)
index 0000000..efb18af
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+
+/**
+ * a full peer for the AtomicIntegerFieldUpdater
+ */
+public class JPF_java_util_concurrent_atomic_AtomicIntegerFieldUpdater extends AtomicFieldUpdater {
+
+  @MJI
+  public void $init__Ljava_lang_Class_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int tClsObjRef, int fNameRef) {
+
+    // direct Object subclass, so we don't have to call a super ctor
+
+    ClassInfo ci = env.getReferredClassInfo( tClsObjRef);
+    String fname = env.getStringObject(fNameRef);
+
+    FieldInfo fi = ci.getInstanceField(fname);
+    ClassInfo fci = fi.getTypeClassInfo();
+
+    if (!fci.isPrimitive() || !fci.getName().equals("int")) {
+      // that's also just an approximation, but we need to check
+      env.throwException("java.lang.RuntimeException", "wrong field type");
+    }
+
+    int fidx = fi.getFieldIndex();
+    env.setIntField(objRef, "fieldId", fidx);
+  }
+
+  @MJI
+  public boolean compareAndSet__Ljava_lang_Object_2II__Z (MJIEnv env, int objRef, int tRef, int fExpect, int fUpdate) {    
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return false;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return false;
+    }
+        
+    int v = ei.getIntField(fi);
+    if (v == fExpect) {
+      ei = ei.getModifiableInstance();
+      ei.setIntField(fi, fUpdate);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @MJI
+  public boolean weakCompareAndSet__Ljava_lang_Object_2II__Z (MJIEnv env, int objRef, int tRef, int fExpect, int fUpdate) {
+    return (compareAndSet__Ljava_lang_Object_2II__Z(env, objRef, tRef, fExpect, fUpdate));
+  }
+
+  @MJI
+  public void set__Ljava_lang_Object_2I__V (MJIEnv env, int objRef, int tRef, int fNewValue) {
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return;
+    }
+
+    ei.setIntField(fi, fNewValue);
+  }
+
+  @MJI
+  public void lazySet__Ljava_lang_Object_2I__V (MJIEnv env, int objRef, int tRef, int fNewValue) {
+    set__Ljava_lang_Object_2I__V(env, objRef, tRef, fNewValue);
+  }
+
+  @MJI
+  public int get__Ljava_lang_Object_2__I(MJIEnv env, int objRef, int tRef) {
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+
+
+    return ei.getIntField(fi);
+  }
+
+  @MJI
+  public int getAndSet__Ljava_lang_Object_2I__I (MJIEnv env, int objRef, int tRef, int fNewValue) {
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+
+    int result = ei.getIntField(fi);
+    ei.setIntField(fi, fNewValue);
+
+    return result;
+  }
+
+  @MJI
+  public int getAndAdd__Ljava_lang_Object_2I__I (MJIEnv env, int objRef, int tRef, int fDelta) {
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+
+    int result = ei.getIntField(fi);
+    ei.setIntField(fi, result + fDelta);
+
+    return result;
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLong.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLong.java
new file mode 100644 (file)
index 0000000..6acc9f3
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for java.util.concurrent.atomic.AtomicLong
+ * this implementation just cuts off native methods
+ */
+public class JPF_java_util_concurrent_atomic_AtomicLong extends NativePeer {
+  @MJI
+  public void $clinit____V (MJIEnv env, int rcls) {
+    // don't let this one pass, it calls native methods from non-public Sun classes
+  }
+
+  @MJI
+  public boolean compareAndSet__JJ__Z (MJIEnv env, int objRef, long expect, long update){
+    long value = env.getLongField(objRef, "value");
+    if (value == expect){
+      env.setLongField(objRef, "value", update);
+      return true;
+    } else {
+      return false;
+    }
+  }
+  
+  @MJI
+  public long getAndIncrement____J (MJIEnv env, int objRef){
+    long value = env.getLongField(objRef, "value");
+    env.setLongField(objRef, "value", value + 1);
+    return value;
+  }
+  
+  @MJI
+  public long getAndDecrement____J (MJIEnv env, int objRef){
+    long value = env.getLongField(objRef, "value");
+    env.setLongField(objRef, "value", value - 1);
+    return value;
+  }
+
+  @MJI
+  public long getAndAdd__J__J (MJIEnv env, int objRef, long delta) {
+    long value = env.getLongField(objRef, "value");
+    env.setLongField(objRef, "value", value + delta);
+    return value;
+  }
+  
+  @MJI
+  public long incrementAndGet____J (MJIEnv env, int objRef) {
+    long value = env.getLongField(objRef, "value");
+    value++;
+    env.setLongField(objRef, "value", value);
+    return value;
+  }
+  
+  @MJI
+  public long decrementAndGet____J (MJIEnv env, int objRef) {
+    long value = env.getLongField(objRef, "value");
+    value--;
+    env.setLongField(objRef, "value", value);
+    return value;
+  }
+  
+  @MJI
+  public long addAndGet__J__J (MJIEnv env, int objRef, long delta) {
+    long value = env.getLongField(objRef, "value");
+    value += delta;
+    env.setLongField(objRef, "value", value);
+    return value;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongArray.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongArray.java
new file mode 100644 (file)
index 0000000..0083611
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for java.util.concurrent.atomic.AtomicLongArray
+ */
+public class JPF_java_util_concurrent_atomic_AtomicLongArray extends NativePeer {
+
+  @MJI
+  public long getNative__I__J (MJIEnv env, int objRef, int index) {
+    int arrayRef = env.getReferenceField(objRef, "array");
+    return env.getLongArrayElement(arrayRef, index);
+  }
+
+  @MJI
+  public boolean compareAndSetNative__IJJ__Z (MJIEnv env, int objRef, int index, long expect, long update) {
+    int arrayRef = env.getReferenceField(objRef, "array");
+    long value = env.getLongArrayElement(arrayRef, index);
+    if (value == expect) {
+      env.setLongArrayElement(arrayRef, index, update);
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongFieldUpdater.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicLongFieldUpdater.java
new file mode 100644 (file)
index 0000000..0a11688
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+
+
+/**
+ * a full peer for the AtomicLongFieldUpdater
+ */
+public class JPF_java_util_concurrent_atomic_AtomicLongFieldUpdater extends AtomicFieldUpdater {
+
+  @MJI
+  public void $init__Ljava_lang_Class_2Ljava_lang_String_2__V (MJIEnv env, int objRef,
+                                 int tClsObjRef, int fNameRef) {
+
+    // direct Object subclass, so we don't have to call a super ctor
+
+    ClassInfo ci = env.getReferredClassInfo(tClsObjRef);
+    String fname = env.getStringObject(fNameRef);
+    FieldInfo fi = ci.getInstanceField(fname);
+
+    ClassInfo fci = fi.getTypeClassInfo();
+
+    if (!fci.isPrimitive() || !fci.getName().equals("long")) {
+      // that's also just an approximation, but we need to check
+      env.throwException("java.lang.RuntimeException", "wrong field type");
+    }
+
+    int fidx = fi.getFieldIndex();
+    env.setIntField(objRef, "fieldId", fidx);
+  }
+
+  @MJI
+  public boolean compareAndSet__Ljava_lang_Object_2JJ__Z (MJIEnv env, int objRef, int tRef, long fExpect, long fUpdate){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return false;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return false;
+    }
+
+    long v = ei.getLongField(fi);
+    if (v == fExpect) {
+      ei.setLongField(fi, fUpdate);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @MJI
+  public boolean weakCompareAndSet__Ljava_lang_Object_2JJ__Z (MJIEnv env, int objRef, int tRef, long fExpect, long fUpdate){
+    return(compareAndSet__Ljava_lang_Object_2JJ__Z(env, objRef, tRef, fExpect, fUpdate));
+  }
+
+  @MJI
+  public void set__Ljava_lang_Object_2J__V (MJIEnv env, int objRef, int tRef, long fNewValue){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return;
+    }
+
+    ei.setLongField(fi, fNewValue);
+  }
+
+  @MJI
+  public void lazySet__Ljava_lang_Object_2J__V (MJIEnv env, int objRef, int tRef, long fNewValue){
+     set__Ljava_lang_Object_2J__V(env, objRef, tRef, fNewValue);
+  }
+
+  @MJI
+  public long get__Ljava_lang_Object_2__J (MJIEnv env, int objRef, int tRef){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+
+    return ei.getLongField(fi);
+  }
+
+  @MJI
+  public long getAndSet__Ljava_lang_Object_2J__J (MJIEnv env, int objRef, int tRef, long fNewValue){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+    
+    long result = ei.getLongField(fi);
+    ei.setLongField(fi, fNewValue);
+
+    return result;
+  }
+
+  @MJI
+  public long getAndAdd__Ljava_lang_Object_2J__J (MJIEnv env, int objRef, int tRef, long fDelta){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return 0;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return 0;
+    }
+    
+    long result = ei.getLongField(fi);
+    ei.setLongField(fi, result + fDelta);
+
+    return result;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceArray.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceArray.java
new file mode 100644 (file)
index 0000000..98277da
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for java.util.concurrent.atomic.AtomicReferenceArray
+ */
+public class JPF_java_util_concurrent_atomic_AtomicReferenceArray extends NativePeer {
+
+  @MJI
+  public int getNative__I__Ljava_lang_Object_2 (MJIEnv env, int objRef, int index) {
+    int arrayRef = env.getReferenceField(objRef, "array");
+    return env.getReferenceArrayElement(arrayRef, index);
+  }
+
+  @MJI
+  public boolean compareAndSetNative__ILjava_lang_Object_2Ljava_lang_Object_2__Z (MJIEnv env, int objRef, int index, int fExpect, int fUpdate){
+    int arrayRef = env.getReferenceField(objRef, "array");
+    int value = env.getReferenceArrayElement(arrayRef, index);
+    if (value == fExpect) {
+      env.setReferenceArrayElement(arrayRef, index, fUpdate);
+      return true;
+    } else {
+      return false;
+    }
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceFieldUpdater.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_concurrent_atomic_AtomicReferenceFieldUpdater.java
new file mode 100644 (file)
index 0000000..87e0b1a
--- /dev/null
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+
+/**
+ * a full peer for the AtomicReferenceFieldUpdater
+ */
+public class JPF_java_util_concurrent_atomic_AtomicReferenceFieldUpdater extends AtomicFieldUpdater {
+
+  @MJI
+  public void $init__Ljava_lang_Class_2Ljava_lang_Class_2Ljava_lang_String_2__V (MJIEnv env, int objRef,
+                                 int tClsObjRef, int fClsObjRef, int fNameRef) {
+
+    // direct Object subclass, so we don't have to call a super ctor
+
+    ClassInfo ci = env.getReferredClassInfo(tClsObjRef);
+    String fname = env.getStringObject(fNameRef);
+    FieldInfo fi = ci.getInstanceField(fname);
+
+    ClassInfo fci = fi.getTypeClassInfo();
+
+    ClassInfo fciCheck = env.getReferredClassInfo( fClsObjRef);
+    if (!fci.isInstanceOf(fciCheck)) {
+      // that's also just an approximation, but we need to check
+      env.throwException("java.lang.RuntimeException", "wrong field type");
+    }
+
+    int fidx = fi.getFieldIndex();
+    env.setIntField(objRef, "fieldId", fidx);
+  }
+
+  @MJI
+  public boolean compareAndSet__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_Object_2__Z
+                                  (MJIEnv env, int objRef, int tRef, int fExpect, int fUpdate){
+
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return false;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return false;
+    }
+
+    int v = ei.getReferenceField(fi);
+    if (v == fExpect) {
+      ei.setReferenceField(fi, fUpdate);
+      return true;
+    } else {
+      return false;
+    }
+  }
+
+  @MJI
+  public boolean weakCompareAndSet__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_Object_2__Z
+  (MJIEnv env, int objRef, int tRef, int fExpect, int fUpdate){
+    return(compareAndSet__Ljava_lang_Object_2Ljava_lang_Object_2Ljava_lang_Object_2__Z(env, objRef, tRef, fExpect, fUpdate));
+  }
+
+  @MJI
+  public void set__Ljava_lang_Object_2Ljava_lang_Object_2__V (MJIEnv env, int objRef, int tRef, int fNewValue){
+
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getModifiableElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return;
+    }
+
+    ei.setReferenceField(fi, fNewValue);
+  }
+
+  @MJI
+  public void lazySet__Ljava_lang_Object_2Ljava_lang_Object_2__V (MJIEnv env, int objRef, int tRef, int fNewValue){
+     set__Ljava_lang_Object_2Ljava_lang_Object_2__V(env, objRef, tRef, fNewValue);
+  }
+
+  @MJI
+  public int get__Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int objRef, int tRef){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return MJIEnv.NULL;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+
+    return ei.getReferenceField(fi);
+  }
+
+  @MJI
+  public int getAndSet__Ljava_lang_Object_2Ljava_lang_Object_2__Ljava_lang_Object_2 (MJIEnv env, int objRef, int tRef, int fNewValue){
+    if (tRef == MJIEnv.NULL){
+      env.throwException("java.lang.NullPointerException", "AtomicFieldUpdater called on null object");
+      return MJIEnv.NULL;
+    }
+    
+    ThreadInfo ti = env.getThreadInfo();
+    ElementInfo ei = ti.getElementInfo(tRef);
+    FieldInfo fi = getFieldInfo( ti.getElementInfo(objRef), ei);
+
+    if (reschedulesAccess(ti, ei, fi)){
+      env.repeatInvocation();
+      return MJIEnv.NULL;
+    }
+    
+    int result = ei.getReferenceField(fi);
+    ei.setReferenceField(fi, fNewValue);
+
+    return result;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_logging_Level.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_logging_Level.java
new file mode 100644 (file)
index 0000000..8e5eb41
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.logging.Level;
+
+/**
+ * this is only a skeleton to make basic logging work under JPF
+ */
+public class JPF_java_util_logging_Level extends NativePeer {
+  @MJI
+  public int getLocalizedName____Ljava_lang_String_2 (MJIEnv env, int objRef){
+    Level level = null;    
+    int val = env.getIntField(objRef, "value");
+    
+    switch (val){
+    case Integer.MIN_VALUE : 
+      level = Level.ALL; break; 
+    case 300 :
+      level = Level.FINEST; break;
+    case 400 :
+      level = Level.FINER; break;
+    case 500 :
+      level = Level.FINE; break;
+    case 700 :
+      level = Level.CONFIG; break;
+    case 800 :
+      level = Level.INFO; break;
+    case 900 :
+      level = Level.WARNING; break;
+    case 1000 :
+      level = Level.SEVERE; break;
+    case Integer.MAX_VALUE :
+      level = Level.OFF; break;      
+    }
+    
+    String localizedName = (level != null) ? level.getLocalizedName() : "UNKNOWN";    
+    return env.newString(localizedName); 
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Matcher.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Matcher.java
new file mode 100644 (file)
index 0000000..b514846
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * native peer for a regex Matcher
+ * this is just a delegatee peer
+ */
+public class JPF_java_util_regex_Matcher extends NativePeer {
+
+  HashMap<Integer, Matcher> matchers;
+  public JPF_java_util_regex_Matcher (Config conf) {
+    matchers = new HashMap<Integer,Matcher>();
+  }
+
+  void putInstance (MJIEnv env, int objref, Matcher matcher) {
+    int id = env.getIntField(objref,  "id");
+    matchers.put(id, matcher);
+  }
+
+  Matcher getInstance (MJIEnv env, int objref) {
+    
+    int id = env.getIntField(objref,  "id");
+    return matchers.get(id);
+  }
+  
+  @MJI
+  public void register____V (MJIEnv env, int objref) {
+    int patRef = env.getReferenceField(objref, "pattern");
+    
+    int regexRef = env.getReferenceField(patRef, "regex");
+    String regex = env.getStringObject(regexRef);
+    int flags = env.getIntField(patRef, "flags");
+    
+    Pattern pat = Pattern.compile(regex, flags);
+
+    int inputRef = env.getReferenceField(objref, "input");
+    String input = env.getStringObject(inputRef);
+    
+    Matcher matcher = pat.matcher(input);
+    putInstance(env, objref, matcher);
+  }
+  
+  @MJI
+  public boolean matches____Z (MJIEnv env, int objref) {
+    Matcher matcher = getInstance( env, objref);
+    return matcher.matches();
+  }
+  
+  @MJI
+  public boolean find____Z (MJIEnv env, int objref) {
+       Matcher matcher = getInstance( env, objref);
+    return matcher.find();
+  }
+
+  @MJI
+  public boolean lookingAt____Z(MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.lookingAt();
+  }
+
+  @MJI
+  public int start__I__I(MJIEnv env, int objref, int group) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.start(group);
+  }
+
+  @MJI
+  public int end__I__I(MJIEnv env, int objref, int group) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.end(group);
+  }
+
+  @MJI
+  public int regionStart____I(MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.regionStart();
+  }
+
+  @MJI
+  public int regionEnd____I(MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.regionEnd();
+  }
+
+  @MJI
+  public int region__II__Ljava_util_regex_Matcher_2(MJIEnv env, int objref, int start, int end) {
+    Matcher matcher = getInstance(env, objref);
+    matcher = matcher.region(start, end);
+    putInstance(env, objref, matcher);
+
+    return objref;
+  }
+
+  @MJI
+  public int reset____Ljava_util_regex_Matcher_2 (MJIEnv env, int objref) {
+    Matcher matcher = getInstance( env, objref);
+
+    int inputRef = env.getReferenceField(objref, "input");
+    String input = env.getStringObject(inputRef);
+    
+    matcher = matcher.reset(input);
+    putInstance(env, objref, matcher);
+    
+    return objref;
+  }
+  
+  @MJI
+  public int groupCount____I (MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.groupCount();
+  }
+  
+  @MJI
+  public int group__I__Ljava_lang_String_2 (MJIEnv env, int objref, int i) {
+    Matcher matcher = getInstance( env, objref);
+    String grp = matcher.group(i);
+    
+    return env.newString(grp);
+  }
+
+  @MJI
+  public int quoteReplacement__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int clsObjref, int string) {
+    String parm = env.getStringObject(string);
+    String result = Matcher.quoteReplacement(parm);
+    return env.newString(result);
+  }
+
+  @MJI
+  public int replaceAll__Ljava_lang_String_2__Ljava_lang_String_2 (MJIEnv env, int objref, int string) {
+    Matcher matcher = getInstance(env, objref);
+    String replacement = env.getStringObject(string);
+    String result = matcher.replaceAll(replacement);
+
+    int resultref = env.newString(result);
+    return resultref;
+  }
+  
+  @MJI
+  public int replaceFirst__Ljava_lang_String_2__Ljava_lang_String_2(MJIEnv env, int objref, int string) {
+    Matcher matcher = getInstance(env, objref);
+    String replacement = env.getStringObject(string);
+    String result = matcher.replaceFirst(replacement);
+
+    int resultref = env.newString(result);
+    return resultref;
+  }
+
+  @MJI
+  public boolean hasTransparentBounds____Z(MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.hasTransparentBounds();
+  }
+
+  @MJI
+  public int useTransparentBounds__Z__Ljava_util_regex_Matcher_2(MJIEnv env, int objref, boolean b) {
+    Matcher matcher = getInstance(env, objref);
+    matcher = matcher.useTransparentBounds(b);
+    putInstance(env, objref, matcher);
+     
+    return objref;
+  }
+
+  @MJI
+  public boolean hasAnchoringBounds____Z(MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    return matcher.hasTransparentBounds();
+  }
+
+  @MJI
+  public int useAnchoringBounds__Z__Ljava_util_regex_Matcher_2(MJIEnv env, int objref, boolean b) {
+    Matcher matcher = getInstance(env, objref);
+    matcher = matcher.useAnchoringBounds(b);
+    putInstance(env, objref, matcher);
+
+    return objref;
+  }
+
+  @MJI
+  public int toString____Ljava_lang_String_2 (MJIEnv env, int objref) {
+    Matcher matcher = getInstance(env, objref);
+    String str = matcher.toString();
+
+    return env.newString(str);
+  }
+
+  @MJI
+  public boolean hitEnd____Z (MJIEnv env, int objref) {
+    Matcher matcher = getInstance( env, objref);
+    return matcher.hitEnd();
+  }
+
+  @MJI
+  public boolean requireEnd____Z (MJIEnv env, int objref) {
+    Matcher matcher = getInstance( env, objref);
+    return matcher.requireEnd();
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Pattern.java b/src/peers/gov/nasa/jpf/vm/JPF_java_util_regex_Pattern.java
new file mode 100644 (file)
index 0000000..54eddd4
--- /dev/null
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.regex.Pattern;
+
+public class JPF_java_util_regex_Pattern extends NativePeer {
+
+  @MJI
+  public int split0__Ljava_lang_String_2I___3Ljava_lang_String_2(MJIEnv env,int patRef,int strRef,int limit){
+    String s = env.getStringObject(strRef);
+    String patSpec = env.getStringField(patRef,"regex");
+    int patFlags = env.getIntField(patRef, "flags");
+
+    // <2do> this is not very efficient - it should use a pattern cache
+    Pattern p = Pattern.compile(patSpec,patFlags);
+    String[] result=p.split(s,limit);
+
+    return env.newStringArray(result);
+  }
+
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java b/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_Unsafe.java
new file mode 100644 (file)
index 0000000..bccad5c
--- /dev/null
@@ -0,0 +1,923 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ArrayFields;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+
+/**
+ * we don't want this class! This is a hodgepodge of stuff that shouldn't be in Java, but
+ * is handy for some hacks. The reason we have it here - very rudimentary - is that
+ * java.util.concurrent makes use of the atomic compare&swap which is in it.
+ * The choice was to duplicate a lot of relatively difficult code in the "right" class
+ * (java.util.concurrent.locks.AbstractQueuedSynchronizer) or a small amount of straight forward
+ * code in the "wrong" class (sun.misc.Unsafe). Knowing a bit about the "library chase" game,
+ * we opt for the latter
+ *
+ * <2do> this might change with better modeling of high level java.util.concurrent constructs
+ */
+public class JPF_sun_misc_Unsafe extends NativePeer {
+
+  @MJI
+  public int getUnsafe____Lsun_misc_Unsafe_2 (MJIEnv env, int clsRef) {
+    int objRef = env.getStaticReferenceField("sun.misc.Unsafe", "theUnsafe");
+    return objRef;
+  }
+
+  @MJI
+  public long objectFieldOffset__Ljava_lang_reflect_Field_2__J (MJIEnv env, int unsafeRef, int fieldRef) {
+    return fieldOffset__Ljava_lang_reflect_Field_2__I(env, unsafeRef, fieldRef);
+  }
+
+  /**
+   * we don't really return an offset here, since that would be useless. What we really want is
+   * to identify the corresponding FieldInfo, and that's much easier done with the Field
+   * registration id
+   */
+  @MJI
+  public int fieldOffset__Ljava_lang_reflect_Field_2__I (MJIEnv env, int unsafeRef, int fieldRef) {
+    //FieldInfo fi = JPF_java_lang_reflect_Field.getFieldInfo(env, fieldRef);
+    //return fi.getStorageOffset();
+    return env.getIntField(fieldRef, "regIdx");
+  }
+
+  @MJI
+  public boolean compareAndSwapObject__Ljava_lang_Object_2JLjava_lang_Object_2Ljava_lang_Object_2__Z (MJIEnv env, int unsafeRef,
+                                                                                                             int objRef, long fieldOffset,
+                                                                                                             int expectRef, int updateRef) {
+    int actual = getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2(env, unsafeRef, objRef, fieldOffset);
+    if (actual == expectRef) {
+      putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, updateRef);
+      return true;
+    }
+    return false;
+  }
+
+  @MJI
+  public boolean compareAndSwapInt__Ljava_lang_Object_2JII__Z (MJIEnv env, int unsafeRef,
+                                                                      int objRef, long fieldOffset, int expect, int update) {
+    int actual = getInt__Ljava_lang_Object_2J__I(env, unsafeRef, objRef, fieldOffset);
+    if (actual == expect) {
+      putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, update);
+      return true;
+    }
+    return false;
+  }
+
+  @MJI
+  public boolean compareAndSwapLong__Ljava_lang_Object_2JJJ__Z (MJIEnv env, int unsafeRef,
+                                                                       int objRef, long fieldOffset, long expect, long update) {
+    long actual = getLong__Ljava_lang_Object_2J__J(env, unsafeRef, objRef, fieldOffset);
+    if (actual == expect) {
+      putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, update);
+      return true;
+    }
+    return false;
+  }
+
+
+  // this is a specialized, native wait() for the current thread that does not require a lock, and that can
+  // be turned off by a preceding unpark() call (which is not accumulative)
+  // park can be interrupted, but it doesn't throw an InterruptedException, and it doesn't clear the status
+  // it can only be called from the current (parking) thread
+  @MJI
+  public void park__ZJ__V (MJIEnv env, int unsafeRef, boolean isAbsoluteTime, long timeout) {
+    ThreadInfo ti = env.getThreadInfo();
+    int objRef = ti.getThreadObjectRef();
+    int permitRef = env.getReferenceField( objRef, "permit");
+    ElementInfo ei = env.getModifiableElementInfo(permitRef);
+
+    if (ti.isInterrupted(false)) {
+      // there is no lock, so we go directly back to running and therefore
+      // have to remove ourself from the contender list
+      ei.setMonitorWithoutLocked(ti);
+
+      // note that park() does not throw an InterruptedException
+      return;
+    }
+    
+    if (!ti.isFirstStepInsn()){
+      if (ei.getBooleanField("blockPark")) { // we have to wait, but don't need a lock
+        // running -> waiting | timeout_waiting
+        ei.wait(ti, timeout, false);
+        
+      } else {
+        ei.setBooleanField("blockPark", true); // re-arm for next park
+        return;
+      }
+    }
+    
+    // scheduling point
+    if (ti.getScheduler().setsParkCG( ti, isAbsoluteTime, timeout)) {
+      env.repeatInvocation();
+      return;
+    }
+    
+    switch (ti.getState()) {
+      case WAITING:
+      case TIMEOUT_WAITING:
+        throw new JPFException("blocking park() without transition break");   
+      
+      case NOTIFIED:
+      case TIMEDOUT:
+      case INTERRUPTED:
+        ti.resetLockRef();
+        ti.setRunning();
+        break;
+        
+      default:
+        // nothing
+    } 
+  }
+
+  @MJI
+  public void unpark__Ljava_lang_Object_2__V (MJIEnv env, int unsafeRef, int objRef) {
+    ThreadInfo tiCurrent = env.getThreadInfo();
+    ThreadInfo tiParked = env.getThreadInfoForObjRef(objRef);
+      
+    if (tiParked.isTerminated()){
+      return; // nothing to do
+    }
+    
+    if (!tiCurrent.isFirstStepInsn()){
+      SystemState ss = env.getSystemState();
+      int permitRef = env.getReferenceField( objRef, "permit");
+      ElementInfo eiPermit = env.getModifiableElementInfo(permitRef);
+
+      if (tiParked.getLockObject() == eiPermit){
+        // note that 'permit' is only used in park/unpark, so there never is more than
+        // one waiter, which immediately becomes runnable again because it doesn't hold a lock
+        // (park is a lockfree wait). unpark() therefore has to be a right mover
+        // and we have to register a ThreadCG here
+        eiPermit.notifies(ss, tiCurrent, false);
+        
+      } else {
+        eiPermit.setBooleanField("blockPark", false);
+        return;
+      }
+    }
+    
+    if (tiCurrent.getScheduler().setsUnparkCG(tiCurrent, tiParked)){
+      env.repeatInvocation();
+      return;
+    }
+  }
+  
+  @MJI
+  public void ensureClassInitialized__Ljava_lang_Class_2__V (MJIEnv env, int unsafeRef, int clsObjRef) {
+    // <2do> not sure if we have to do anyting here - if we have a class object, the class should already
+    // be initialized
+  }
+
+  @MJI
+  public int getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2 (MJIEnv env, int unsafeRef,
+                                                                          int objRef, long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getReferenceField(fi);
+    } else {
+      return ei.getReferenceElement((int)fieldOffset);
+    }
+  }
+  
+  @MJI
+  public int getObjectVolatile__Ljava_lang_Object_2J__Ljava_lang_Object_2 (MJIEnv env, int unsafeRef,
+      int objRef, long fieldOffset) {
+    return getObject__Ljava_lang_Object_2J__Ljava_lang_Object_2( env, unsafeRef, objRef, fieldOffset);
+  }  
+
+  @MJI
+  public void putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V (MJIEnv env, int unsafeRef,
+                                                                            int objRef, long fieldOffset, int valRef) {
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setReferenceField(fi, valRef);
+    } else {
+      ei.setReferenceElement((int)fieldOffset, valRef);
+    }
+  }
+  
+  @MJI
+  public void putObjectVolatile__Ljava_lang_Object_2JLjava_lang_Object_2__V (MJIEnv env, int unsafeRef,
+      int objRef, long fieldOffset, int valRef) {
+    putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V( env, unsafeRef, objRef, fieldOffset, valRef);
+  }
+
+  @MJI
+  public void putOrderedObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(
+                                                                                  MJIEnv env,
+                                                                                  int unsafeRef,
+                                                                                  int objRef,
+                                                                                  long fieldOffset,
+                                                                                  int valRef) {
+    putObject__Ljava_lang_Object_2JLjava_lang_Object_2__V(env, unsafeRef, objRef, fieldOffset, valRef);
+  }
+  
+  @MJI
+  public boolean getBoolean__Ljava_lang_Object_2J__Z(MJIEnv env,
+                                                            int unsafeRef,
+                                                            int objRef,
+                                                            long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getBooleanField(fi);
+    } else {
+      return ei.getBooleanElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public boolean getBooleanVolatile__Ljava_lang_Object_2J__Z(MJIEnv env, int unsafeRef,int objRef,long fieldOffset) {
+    return getBoolean__Ljava_lang_Object_2J__Z( env, unsafeRef, objRef, fieldOffset);
+  }
+  
+  @MJI
+  public void putBoolean__Ljava_lang_Object_2JZ__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, boolean val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setBooleanField(fi, val);
+    } else {
+      ei.setBooleanElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putBooleanVolatile__Ljava_lang_Object_2JZ__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, boolean val){
+    putBoolean__Ljava_lang_Object_2JZ__V( env, unsafeRef, objRef, fieldOffset, val);
+  }
+
+  @MJI
+  public byte getByte__Ljava_lang_Object_2J__B(MJIEnv env,
+                                                      int unsafeRef,
+                                                      int objRef,
+                                                      long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getByteField(fi);
+    } else {
+      return ei.getByteElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public byte getByteVolatile__Ljava_lang_Object_2J__B(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
+    return getByte__Ljava_lang_Object_2J__B(env, unsafeRef, objRef, fieldOffset);
+  }
+
+  @MJI
+  public void putByte__Ljava_lang_Object_2JB__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, byte val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setByteField(fi, val);
+    } else {
+      ei.setByteElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putByteVolatile__Ljava_lang_Object_2JB__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, byte val){
+    putByte__Ljava_lang_Object_2JB__V( env, unsafeRef, objRef, fieldOffset, val);
+  }
+
+  @MJI
+  public char getChar__Ljava_lang_Object_2J__C(MJIEnv env,
+                                                      int unsafeRef,
+                                                      int objRef,
+                                                      long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getCharField(fi);
+    } else {
+      return ei.getCharElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public char getCharVolatile__Ljava_lang_Object_2J__C(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
+    return getChar__Ljava_lang_Object_2J__C( env, unsafeRef, objRef, fieldOffset);
+  }
+  
+  @MJI
+  public void putChar__Ljava_lang_Object_2JC__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, char val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setCharField(fi, val);
+    } else {
+      ei.setCharElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putCharVolatile__Ljava_lang_Object_2JC__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, char val){
+    putChar__Ljava_lang_Object_2JC__V( env, unsafeRef, objRef, fieldOffset, val);
+  }
+  
+  @MJI
+  public short getShort__Ljava_lang_Object_2J__S(MJIEnv env,
+                                                        int unsafeRef,
+                                                        int objRef,
+                                                        long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getShortField(fi);
+    } else {
+      return ei.getShortElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public short getShortVolatile__Ljava_lang_Object_2J__S(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
+    return getShort__Ljava_lang_Object_2J__S( env, unsafeRef, objRef, fieldOffset);
+  }
+  
+  @MJI
+  public void putShort__Ljava_lang_Object_2JS__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, short val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setShortField(fi, val);
+    } else {
+      ei.setShortElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putShortVolatile__Ljava_lang_Object_2JS__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, short val){
+    putShort__Ljava_lang_Object_2JS__V( env, unsafeRef, objRef, fieldOffset, val);
+  }  
+
+  @MJI
+  public int getInt__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef,
+                                                    int objRef, long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getIntField(fi);
+    } else {
+      return ei.getIntElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public int getIntVolatile__Ljava_lang_Object_2J__I(MJIEnv env, int unsafeRef, int objRef, long fieldOffset) {
+    return getInt__Ljava_lang_Object_2J__I( env, unsafeRef, objRef, fieldOffset);
+  }
+  
+  @MJI
+  public void putInt__Ljava_lang_Object_2JI__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, int val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setIntField(fi, val);
+    } else {
+      ei.setIntElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putIntVolatile__Ljava_lang_Object_2JI__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, int val){
+    putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val);
+  }  
+
+  @MJI
+  public void putOrderedInt__Ljava_lang_Object_2JI__V(MJIEnv env,
+                                                             int unsafeRef,
+                                                             int objRef,
+                                                             long fieldOffset,
+                                                             int val) {
+    // volatile?
+    putInt__Ljava_lang_Object_2JI__V(env, unsafeRef, objRef, fieldOffset, val);
+  }
+
+  @MJI
+  public float getFloat__Ljava_lang_Object_2J__F(MJIEnv env,
+                                                        int unsafeRef,
+                                                        int objRef,
+                                                        long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getFloatField(fi);
+    } else {
+      return ei.getFloatElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public float getFloatVolatile__Ljava_lang_Object_2J__F(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
+    return getFloat__Ljava_lang_Object_2J__F( env, unsafeRef, objRef, fieldOffset);
+  }  
+
+  @MJI
+  public void putFloat__Ljava_lang_Object_2JF__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, float val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setFloatField(fi, val);
+    } else {
+      ei.setFloatElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putFloatVolatile__Ljava_lang_Object_2JF__V (MJIEnv env, int unsafeRef,int objRef, long fieldOffset, float val){
+    putFloat__Ljava_lang_Object_2JF__V( env, unsafeRef, objRef, fieldOffset, val);
+  }  
+
+  @MJI
+  public long getLong__Ljava_lang_Object_2J__J(MJIEnv env,
+                                                      int unsafeRef,
+                                                      int objRef,
+                                                      long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getLongField(fi);
+    } else {
+      return ei.getLongElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public long getLongVolatile__Ljava_lang_Object_2J__J(MJIEnv env, int unsafeRef, int objRef, long fieldOffset) {
+    return getLong__Ljava_lang_Object_2J__J( env, unsafeRef, objRef, fieldOffset);
+  }
+
+  @MJI
+  public void putLong__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, long val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setLongField(fi, val);
+    } else {
+      ei.setLongElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putLongVolatile__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, long val){
+    putLong__Ljava_lang_Object_2JJ__V( env, unsafeRef, objRef, fieldOffset, val);
+  }  
+
+  @MJI
+  public void putOrderedLong__Ljava_lang_Object_2JJ__V (MJIEnv env, int unsafeRef,
+                                                        int objRef, long fieldOffset, long val) {
+    putLong__Ljava_lang_Object_2JJ__V(env, unsafeRef, objRef, fieldOffset, val);
+  }
+
+  @MJI
+  public double getDouble__Ljava_lang_Object_2J__D(MJIEnv env,
+                                                         int unsafeRef,
+                                                         int objRef,
+                                                         long fieldOffset) {
+    ElementInfo ei = env.getElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      return ei.getDoubleField(fi);
+    } else {
+      return ei.getDoubleElement((int)fieldOffset);
+    }
+  }
+
+  @MJI
+  public double getDoubleVolatile__Ljava_lang_Object_2J__D(MJIEnv env,int unsafeRef,int objRef,long fieldOffset) {
+    return getDouble__Ljava_lang_Object_2J__D( env, unsafeRef, objRef, fieldOffset);
+  }
+  
+  @MJI
+  public void putDouble__Ljava_lang_Object_2JD__V (MJIEnv env, int unsafeRef,
+                                                       int objRef, long fieldOffset, double val){
+    ElementInfo ei = env.getModifiableElementInfo(objRef);
+    if (!ei.isArray()) {
+      FieldInfo fi = getRegisteredFieldInfo(fieldOffset);
+      ei.setDoubleField(fi, val);
+    } else {
+      ei.setDoubleElement((int)fieldOffset, val);
+    }
+  }
+
+  @MJI
+  public void putDoubleVolatile__Ljava_lang_Object_2JD__V (MJIEnv env, int unsafeRef, int objRef, long fieldOffset, double val){
+    putDouble__Ljava_lang_Object_2JD__V( env, unsafeRef, objRef, fieldOffset, val);
+  }
+  
+  @MJI
+  public int arrayBaseOffset__Ljava_lang_Class_2__I (MJIEnv env, int unsafeRef, int clazz) {
+    return 0;
+  }
+
+  @MJI
+  public int arrayIndexScale__Ljava_lang_Class_2__I (MJIEnv env, int unsafeRef, int clazz) {
+    return 1;
+  }
+
+  private static FieldInfo getRegisteredFieldInfo(long fieldOffset) {
+    return JPF_java_lang_reflect_Field.getRegisteredFieldInfo((int)fieldOffset);
+  }
+
+  
+  //--- the explicit memory buffer allocation/free + access methods - evil pointer arithmetic
+
+  /*
+   * we shy away from maintaining our own address table by means of knowing that
+   * the byte[] object stored in the ArrayFields will not be recycled, and hashCode() will
+   * return its address, so the start/endAdr pairs we get from that have to be
+   * non-overlapping. Of course that falls apart if  hashCode() would do something
+   * different, which is the case for any address that exceeds 32bit
+   */
+  
+  static class Alloc {
+    int objRef;
+    
+    int startAdr;
+    int endAdr;
+    
+    Alloc next;
+    
+    Alloc (MJIEnv env, int baRef, long length){
+      this.objRef = baRef;
+
+      ElementInfo ei = env.getElementInfo(baRef);
+      ArrayFields afi = (ArrayFields) ei.getFields();
+      byte[] mem = afi.asByteArray();
+
+      startAdr = mem.hashCode();
+      endAdr = startAdr + (int)length -1;
+    }
+    
+    @Override
+       public String toString(){
+      return String.format("Alloc[objRef=%x,startAdr=%x,endAdr=%x]", objRef, startAdr, endAdr);
+    }
+  }
+  
+  Alloc firstAlloc;
+  
+  // for debugging purposes only
+  private void dumpAllocs(){
+    System.out.println("Unsafe allocated memory blocks:{");
+    for (Alloc a = firstAlloc; a != null; a = a.next){
+      System.out.print("  ");
+      System.out.println(a);
+    }
+    System.out.println('}');
+  }
+  
+  private void sortInAlloc(Alloc newAlloc){
+    int startAdr = newAlloc.startAdr;
+    
+    if (firstAlloc == null){
+      firstAlloc = newAlloc;
+      
+    } else {
+      Alloc prev = null;
+      for (Alloc a = firstAlloc; a != null; prev = a, a = a.next){
+        if (startAdr < a.startAdr){
+          newAlloc.next = a;
+          if (prev == null){
+            firstAlloc = newAlloc;
+          } else {
+            prev.next = newAlloc;
+          }
+        }
+      }
+    }
+  }
+  
+  private Alloc getAlloc (int address){
+    for (Alloc a = firstAlloc; a != null; a = a.next){
+      if (address >= a.startAdr && address <= a.endAdr){
+        return a;
+      }
+    }
+    
+    return null;
+  }
+  
+  private Alloc removeAlloc (int startAddress){
+    Alloc prev = null;
+    for (Alloc a = firstAlloc; a != null; prev = a, a = a.next) {
+      if (a.startAdr == startAddress){
+        if (prev == null){
+          firstAlloc = a.next;
+        } else {
+          prev.next = a.next;
+        }
+        
+        return a;
+      }
+    }
+    
+    return null;
+  }
+  
+  @MJI
+  public long allocateMemory__J__J (MJIEnv env, int unsafeRef, long nBytes) {
+    if (nBytes < 0 || nBytes > Integer.MAX_VALUE) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory block size: " + nBytes);
+      return 0;
+    }
+    
+    // <2do> we should probably also throw OutOfMemoryErrors on configured thresholds 
+    
+    int baRef = env.newByteArray((int) nBytes);
+    // the corresponding objects have to be freed explicitly
+    env.registerPinDown(baRef);
+    
+    Alloc alloc = new Alloc(env, baRef, nBytes);
+    sortInAlloc(alloc);
+    
+    return alloc.startAdr;
+  }
+  
+  @MJI
+  public void freeMemory__J__V (MJIEnv env, int unsafeRef, long startAddress) {
+    int addr = (int)startAddress;
+
+    if (startAddress != MJIEnv.NULL){
+      Alloc a = removeAlloc(addr);
+      if (a == null){
+        env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      } else {
+        env.releasePinDown(a.objRef);
+      }
+    }
+  }
+  
+  @MJI
+  public byte getByte__J__B (MJIEnv env, int unsafeRef, long address) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return 0;
+    }
+    
+    ElementInfo ei = env.getElementInfo(a.objRef);
+    return ei.getByteElement(addr - a.startAdr);
+  }
+
+  @MJI
+  public void putByte__JB__V (MJIEnv env, int unsafeRef, long address, byte val) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return;
+    }
+    
+    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
+    ei.setByteElement(addr - a.startAdr, val);
+  }
+  
+  @MJI
+  public char getChar__J__C (MJIEnv env, int unsafeRef, long address) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return 0;
+    }
+    
+    ElementInfo ei = env.getElementInfo(a.objRef);
+    byte[] ba = ei.asByteArray();
+    
+    byte b0 = ba[addr];
+    byte b1 = ba[addr+1];
+    
+    char val;
+    if (env.isBigEndianPlatform()){
+      val = (char) ((b0 << 8) | b1);
+    } else {
+      val = (char) ((b1 << 8) | b0);      
+    }
+    
+    return val;
+  }
+
+  @MJI
+  public void putChar__JC__V (MJIEnv env, int unsafeRef, long address, char val) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return;
+    }
+        
+    byte b1 = (byte)(0xff & val);
+    byte b0 = (byte)(0xff & (val >>> 8));
+    
+    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
+
+    if (env.isBigEndianPlatform()){
+      ei.setByteElement(addr,   b0);
+      ei.setByteElement(addr+1, b1);
+    } else {
+      ei.setByteElement(addr,   b1);
+      ei.setByteElement(addr+1, b0);      
+    }
+  }
+
+  @MJI
+  public int getInt__J__I (MJIEnv env, int unsafeRef, long address) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return 0;
+    }
+    
+    ElementInfo ei = env.getElementInfo(a.objRef);
+    byte[] ba = ei.asByteArray();
+    
+    byte b0 = ba[addr];
+    byte b1 = ba[addr+1];
+    byte b2 = ba[addr+2];
+    byte b3 = ba[addr+3];
+    
+    int val;
+    if (env.isBigEndianPlatform()){
+      val = b0;
+      val = (val << 8) | b1;
+      val = (val << 8) | b2;
+      val = (val << 8) | b3;
+
+    } else {
+      val = b3;
+      val = (val << 8) | b2;
+      val = (val << 8) | b1;
+      val = (val << 8) | b0;
+    }
+    
+    return val;
+  }
+
+  @MJI
+  public void putInt__JI__V (MJIEnv env, int unsafeRef, long address, int val) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return;
+    }
+        
+    byte b3 = (byte)(0xff & val);
+    byte b2 = (byte)(0xff & (val >>> 8));
+    byte b1 = (byte)(0xff & (val >>> 16));
+    byte b0 = (byte)(0xff & (val >>> 24));    
+    
+    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
+
+    if (env.isBigEndianPlatform()){
+      ei.setByteElement(addr,   b0);
+      ei.setByteElement(addr+1, b1);
+      ei.setByteElement(addr+2, b2);
+      ei.setByteElement(addr+3, b3);
+    } else {
+      ei.setByteElement(addr,   b3);
+      ei.setByteElement(addr+1, b2);
+      ei.setByteElement(addr+2, b1);
+      ei.setByteElement(addr+3, b0);
+    }
+  }
+
+  @MJI
+  public long getLong__J__J (MJIEnv env, int unsafeRef, long address) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return 0;
+    }
+    
+    ElementInfo ei = env.getElementInfo(a.objRef);
+    byte[] ba = ei.asByteArray();
+    int offset = addr - a.startAdr;
+    
+    byte b0 = ba[offset];
+    byte b1 = ba[offset+1];
+    byte b2 = ba[offset+2];
+    byte b3 = ba[offset+3];
+    byte b4 = ba[offset+4];
+    byte b5 = ba[offset+5];
+    byte b6 = ba[offset+6];
+    byte b7 = ba[offset+7];
+    
+    int val;
+    if (env.isBigEndianPlatform()){
+      val = b0;
+      val = (val << 8) | b1;
+      val = (val << 8) | b2;
+      val = (val << 8) | b3;
+      val = (val << 8) | b4;
+      val = (val << 8) | b5;
+      val = (val << 8) | b6;
+      val = (val << 8) | b7;
+
+    } else {
+      val = b7;
+      val = (val << 8) | b6;
+      val = (val << 8) | b5;
+      val = (val << 8) | b4;
+      val = (val << 8) | b3;
+      val = (val << 8) | b2;
+      val = (val << 8) | b1;
+      val = (val << 8) | b0;
+    }
+    
+    return val;
+  }
+
+  @MJI
+  public void putLong__JJ__V (MJIEnv env, int unsafeRef, long address, long val) {
+    int addr = (int)address;
+    Alloc a = getAlloc(addr);
+    
+    if (a == null) {
+      env.throwException("java.lang.IllegalArgumentException", "invalid memory address: " + Integer.toHexString(addr));
+      return;
+    }
+        
+    byte b7 = (byte)(0xff & val);
+    byte b6 = (byte)(0xff & (val >>> 8));
+    byte b5 = (byte)(0xff & (val >>> 16));
+    byte b4 = (byte)(0xff & (val >>> 24));    
+    byte b3 = (byte)(0xff & (val >>> 32));    
+    byte b2 = (byte)(0xff & (val >>> 40));    
+    byte b1 = (byte)(0xff & (val >>> 48));    
+    byte b0 = (byte)(0xff & (val >>> 56));    
+
+    ElementInfo ei = env.getModifiableElementInfo(a.objRef);
+    int offset = addr - a.startAdr;
+    
+    if (env.isBigEndianPlatform()){
+      ei.setByteElement(offset,   b0);
+      ei.setByteElement(offset+1, b1);
+      ei.setByteElement(offset+2, b2);
+      ei.setByteElement(offset+3, b3);
+      ei.setByteElement(offset+4, b4);
+      ei.setByteElement(offset+5, b5);
+      ei.setByteElement(offset+6, b6);
+      ei.setByteElement(offset+7, b7);
+      
+    } else {
+      ei.setByteElement(offset,   b7);
+      ei.setByteElement(offset+1, b6);
+      ei.setByteElement(offset+2, b5);
+      ei.setByteElement(offset+3, b4);
+      ei.setByteElement(offset+4, b3);
+      ei.setByteElement(offset+5, b2);
+      ei.setByteElement(offset+6, b1);
+      ei.setByteElement(offset+7, b0);
+    }
+  }
+
+}
+
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_VM.java b/src/peers/gov/nasa/jpf/vm/JPF_sun_misc_VM.java
new file mode 100644 (file)
index 0000000..b772f14
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * this is just a placeholder for now, we don't support its functionality
+ */
+public class JPF_sun_misc_VM extends NativePeer {
+  
+  @MJI
+  public void initialize____V (MJIEnv env, int clsObjRef){
+    // nothing here yet
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_net_www_protocol_http_Handler.java b/src/peers/gov/nasa/jpf/vm/JPF_sun_net_www_protocol_http_Handler.java
new file mode 100644 (file)
index 0000000..628330e
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.JPFConfigException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+import gov.nasa.jpf.util.StringMatcher;
+
+/**
+ * native peer to configure concrete URLConnection classes for specific URLs
+ *
+ * We use model class configuration in case somebody wants to implement logic
+ * in there that requires backtracking, and doesn't want to do this on the
+ * native peer level
+ *
+ * example config:
+ *   http.connection= http://*.dtd => gov.nasa.jpf.CachedROHttpConnection, http://foo.com/* -- x.y.MyHttpConnection
+ */
+public class JPF_sun_net_www_protocol_http_Handler extends NativePeer {
+
+  static JPFLogger logger = JPF.getLogger("http");
+
+
+  static class MapEntry {
+    StringMatcher matcher;
+    String clsName;
+
+    MapEntry (StringMatcher m, String c){
+      matcher = m;
+      clsName = c;
+    }
+  }
+
+  MapEntry[] map;
+
+  public JPF_sun_net_www_protocol_http_Handler (Config conf){
+    String[] specs = conf.getCompactTrimmedStringArray("http.connection");
+    if (specs != null){
+      map = new MapEntry[specs.length];
+
+      for (int i=0; i<specs.length; i++){
+        String s = specs[i];
+        MapEntry e = null;
+
+        int idx = s.indexOf("--");
+        if (idx > 0 && idx < (s.length() - 3)){
+          String pattern = s.substring(0, idx).trim();
+          String clsName = s.substring(idx+2).trim();
+
+          if (!pattern.isEmpty() && !clsName.isEmpty()){
+            StringMatcher matcher = new StringMatcher(pattern);
+            e = new MapEntry(matcher, clsName);
+
+            logger.info("mapping URL pattern ", pattern, " to ", clsName);
+          }
+        }
+
+        if (e == null){
+          throw new JPFConfigException("not a valid http.connection spec: " + s);
+        }
+
+        map[i] = e;
+      }
+    }
+
+  }
+
+  @MJI
+  public int getConnectionClass__Ljava_lang_String_2__Ljava_lang_Class_2 (MJIEnv env, int objref, int surlRef){
+    String url = env.getStringObject(surlRef);
+
+    if (map != null){
+      for (int i = 0; i < map.length; i++) {
+        if (map[i].matcher.matches(url)) {
+          String clsName = map[i].clsName;
+
+          ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo(clsName);
+
+          // this might re-execute if there is a clinit
+          int clsObjRef = JPF_java_lang_Class.getClassObject(env, ci);
+
+          if (clsObjRef != MJIEnv.NULL){
+            logger.info("using ", clsName, " for URL ", url);
+          }
+          return clsObjRef;
+        }
+      }
+    }
+
+    // didn't find any match
+    return MJIEnv.NULL;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_Reflection.java b/src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_Reflection.java
new file mode 100644 (file)
index 0000000..deddc30
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+
+public class JPF_sun_reflect_Reflection extends NativePeer {
+
+  @MJI
+  public int getCallerClass__I__Ljava_lang_Class_2(MJIEnv env, int clsObjRef, int offset){
+    ThreadInfo ti = env.getThreadInfo();
+    
+    StackFrame frame = ti.getTopFrame();
+    MethodInfo mi = frame.getMethodInfo();
+    
+    while (offset > 0){
+      frame = frame.getPrevious();
+      if (frame == null){
+        return MJIEnv.NULL; // <2do> maybe this throws an exception
+      }
+      
+      if (frame.isDirectCallFrame()){
+        continue; // does not count
+      }
+      
+      mi = frame.getMethodInfo();
+      if (mi.getName().equals("invoke") && mi.getClassName().equals("java.lang.reflect.Method")){
+        continue; // does not count
+      }
+      
+      offset--;
+    }
+    ClassInfo ci = mi.getClassInfo();
+    return ci.getClassObjectRef();
+  }
+  
+  @MJI
+  public int getCallerClass____Ljava_lang_Class_2(MJIEnv env, int clsObjRef){
+    return getCallerClass__I__Ljava_lang_Class_2( env, clsObjRef, 2);
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_ReflectionFactory.java b/src/peers/gov/nasa/jpf/vm/JPF_sun_reflect_ReflectionFactory.java
new file mode 100644 (file)
index 0000000..ac0eccf
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * <2do> hack around a hack - we need to override this as long as we don't
+ * replace ObjectStreamClass 
+ */
+public class JPF_sun_reflect_ReflectionFactory extends NativePeer {
+
+  @MJI
+  public int newConstructorForSerialization__Ljava_lang_Class_2Ljava_lang_reflect_Constructor_2__Ljava_lang_reflect_Constructor_2 (MJIEnv env, int objRef,
+                                                                                                                                          int clsRef, int ctorRef){
+    // NOPE - unnless I miss my guess, this creates an artificial
+    // ctor for the concrete type that explicitly calls the default ctor of the
+    // first non-serializable superclass. Oh my!
+
+    // <2do> we really have to model ObjectStreamClass and ObjectStreamField
+    ClassInfo ci = ClassInfo.getInitializedClassInfo("gov.nasa.jpf.SerializationConstructor", env.getThreadInfo());
+    int sCtorRef = env.newObject(ci);
+    
+    env.setReferenceField(sCtorRef, "mdc", clsRef);
+    env.setReferenceField(sCtorRef, "firstNonSerializableCtor", ctorRef);
+    
+    return sCtorRef;
+  }
+}
diff --git a/src/peers/gov/nasa/jpf/vm/LoggablePeer.java b/src/peers/gov/nasa/jpf/vm/LoggablePeer.java
new file mode 100644 (file)
index 0000000..374d76d
--- /dev/null
@@ -0,0 +1,134 @@
+package gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.JPFLogger;
+
+/**
+ * abstract base for Loggable peers
+ */
+public abstract class LoggablePeer extends NativePeer {
+
+  final JPFLogger delegatee;
+
+  protected LoggablePeer (String loggerId) {
+    delegatee = JPF.getLogger(loggerId);
+  }
+
+
+  @MJI
+  public void severe__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.severe(env.getStringObject(sRef));
+  }
+  @MJI public void severe__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.severe(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void severe__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.severe(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void severe__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.severe(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void severe__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.severe(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void severe___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.severe((Object[]) env.getStringArrayObject(aRef));
+  }
+
+  @MJI public void warning__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.warning(env.getStringObject(sRef));
+  }
+  @MJI public void warning__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.warning(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void warning__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.warning(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void warning__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.warning(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void warning__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.warning(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void warning___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.warning((Object[]) env.getStringArrayObject(aRef));
+  }
+
+  @MJI public void info__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.info(env.getStringObject(sRef));
+  }
+  @MJI public void info__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.info(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void info__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.info(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void info__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.info(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void info__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.info(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void info___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.info((Object[]) env.getStringArrayObject(aRef));
+  }
+
+  @MJI public void fine__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.fine(env.getStringObject(sRef));
+  }
+  @MJI public void fine__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.fine(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void fine__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.fine(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void fine__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.fine(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void fine__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.fine(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void fine___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.fine((Object[]) env.getStringArrayObject(aRef));
+  }
+
+  @MJI public void finer__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.finer(env.getStringObject(sRef));
+  }
+  @MJI public void finer__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.finer(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void finer__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.finer(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void finer__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.finer(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void finer__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.finer(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void finer___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.finer((Object[]) env.getStringArrayObject(aRef));
+  }
+
+  @MJI public void finest__Ljava_lang_String_2__V (MJIEnv env, int objRef, int sRef) {
+    delegatee.finest(env.getStringObject(sRef));
+  }
+  @MJI public void finest__Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref) {
+    delegatee.finest(env.getStringObject(s1Ref), env.getStringObject(s2Ref));
+  }
+  @MJI public void finest__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref) {
+    delegatee.finest(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref));
+  }
+  @MJI public void finest__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref) {
+    delegatee.finest(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref));
+  }
+  @MJI public void finest__Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2Ljava_lang_String_2__V (MJIEnv env, int objRef, int s1Ref, int s2Ref, int s3Ref, int s4Ref, int s5Ref) {
+    delegatee.finest(env.getStringObject(s1Ref), env.getStringObject(s2Ref), env.getStringObject(s3Ref), env.getStringObject(s4Ref), env.getStringObject(s5Ref));
+  }
+  @MJI public void finest___3Ljava_lang_String_2__V (MJIEnv env, int objRef, int aRef) {
+    delegatee.finest((Object[]) env.getStringArrayObject(aRef));
+  }
+
+}
diff --git a/src/tests/TypeNameTest.java b/src/tests/TypeNameTest.java
new file mode 100644 (file)
index 0000000..de15fdc
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * unfortunately this has to be in the unnamed package, since we need
+ * B to be in the unnamed package (so that it has a builtin type name), and as
+ * of Java 1.4 you can't import unnamed package classes into named packages
+ */
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/*
+ * just a helper class for the JavaLangObject raw test that happens to be
+ * named like a builtin typecode (byte)
+ */
+class B {
+
+  int data;
+
+  public B(int d) {
+    data = d;
+  }
+
+  @Override
+  public boolean equals(Object other) {
+    if (other == null || !(other instanceof B)) {
+      return false;
+    }
+
+    return ((B) other).data == data;
+  }
+
+  @Override
+  public String toString() {
+    return "B {data=" + data + "}";
+  }
+}
+
+public class TypeNameTest extends TestJPF {
+
+  @Test
+  public void testArrayCloning() {
+    if (verifyNoPropertyViolation()) {
+      // test for collisions between typecodes of builtin types
+      // and user defined classes (e.g. "B" for byte)
+      B[] b = new B[10];
+      b[3] = new B(42);
+
+      Object o = b.clone();
+      B[] bb = (B[]) o;
+      assert b[3].equals(bb[3]);
+
+      byte[] a = new byte[10];
+      a[3] = 42;
+      o = a.clone();
+      byte[] aa = (byte[]) o;
+      assert a[3] == aa[3];
+    }
+  }
+}
diff --git a/src/tests/classloader_specific_tests/Class1.java b/src/tests/classloader_specific_tests/Class1.java
new file mode 100644 (file)
index 0000000..7714477
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 classloader_specific_tests;
+
+public class Class1 extends Class2 implements Interface1, Interface2 {
+  
+  public static void causeArithmeticException() {
+    System.out.println("Class1 now causing ArithmeticException");
+    int a = 10;
+    a = 10/(a-10);
+  }
+
+  public static void assertFalse() {
+    assert false;
+  }
+}
diff --git a/src/tests/classloader_specific_tests/Class2.java b/src/tests/classloader_specific_tests/Class2.java
new file mode 100644 (file)
index 0000000..ff96d87
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 classloader_specific_tests;
+
+public class Class2 {
+  public static void assertFalse() {
+    assert false;
+  }
+}
diff --git a/src/tests/classloader_specific_tests/Class3.java b/src/tests/classloader_specific_tests/Class3.java
new file mode 100644 (file)
index 0000000..8993ad1
--- /dev/null
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 classloader_specific_tests;
+
+public class Class3 {
+  public static void assertFalse() {
+    assert false;
+  }
+}
diff --git a/src/tests/classloader_specific_tests/Interface1.java b/src/tests/classloader_specific_tests/Interface1.java
new file mode 100644 (file)
index 0000000..daf6457
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 classloader_specific_tests;
+
+public interface Interface1 {
+       final static Class3 f = new Class3();
+}
diff --git a/src/tests/classloader_specific_tests/Interface2.java b/src/tests/classloader_specific_tests/Interface2.java
new file mode 100644 (file)
index 0000000..831fe6b
--- /dev/null
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 classloader_specific_tests;
+
+public interface Interface2 {
+}
diff --git a/src/tests/gov/nasa/jpf/ConfigTest.java b/src/tests/gov/nasa/jpf/ConfigTest.java
new file mode 100644 (file)
index 0000000..3265db5
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.File;
+import java.util.regex.Matcher;
+
+import org.junit.Test;
+
+
+/**
+ * unit test for Config
+ */
+public class ConfigTest extends TestJPF {
+
+  @Test
+  public void testDefaultAppPropertyInit () {
+
+    String dir = "src/tests/gov/nasa/jpf";
+    String[] args = {dir + "/configTestApp.jpf"};
+
+    Config conf = new Config(args);
+
+    String val = conf.getString("vm.class");
+    assert "gov.nasa.jpf.vm.SingleProcessVM".equals(val);
+
+    val = conf.getString("target"); // from configTest.jpf
+    assert "urgh.org.MySystemUnderTest".equals(val);
+
+    // that's testing key expansion and the builtin "config_path"
+    val = conf.getString("mySUT.location");
+    
+    assert val != null;
+    
+    if (!File.separator.equals("/"))
+       dir = dir.replaceAll("/", Matcher.quoteReplacement(File.separator));  // On UNIX Config returns / and on Windows Config returns \\
+
+    assert val.endsWith(dir);
+  }
+
+  @Test
+  public void testDefaultExplicitTargetInit ()  {
+    String[] args = {"urgh.org.MySystemUnderTest"};
+
+    Config conf = new Config( args);
+    String[] freeArgs = conf.getFreeArgs();
+    assertTrue( freeArgs.length == 1);
+    assertTrue( "urgh.org.MySystemUnderTest".equals(freeArgs[0]));
+  }
+
+  @Test
+  public void testExplicitLocations () {
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = {"+site=" + dir + "configTestSite.properties",
+                     "+app=" + dir + "configTestApp.jpf" };
+
+    Config conf = new Config( args);
+    conf.printEntries();
+
+    assert "urgh.org.MySystemUnderTest".equals(conf.getString("target"));
+  }
+
+  @Test
+  public void testTargetArgsOverride () {
+
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = { dir + "configTestApp.jpf",
+                      "x", "y"};
+
+    Config conf = new Config(args);
+    conf.printEntries();
+
+    String[] ta = conf.getStringArray("target.args");
+    assert ta != null;
+    assert ta.length == 3;
+    assert "a".equals(ta[0]);
+    assert "b".equals(ta[1]);
+    assert "c".equals(ta[2]);
+    
+    String[] freeArgs = conf.getFreeArgs();
+    assert freeArgs != null;
+    assert freeArgs.length == 2;
+    assert "x".equals(freeArgs[0]);
+    assert "y".equals(freeArgs[1]);
+  }
+
+  @Test
+  public void testClassPaths () {
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = {"+site=" + dir + "configTestSite.properties",
+                     "+app=" + dir + "configTestApp.jpf" };
+
+    Config conf = new Config( args);
+    conf.printEntries();
+
+    // those properties are very weak!
+    String[] bootCpEntries = conf.asStringArray("boot_classpath");
+    assert bootCpEntries.length > 0;
+
+    String[] nativeCpEntries = conf.asStringArray("native_classpath");
+    assert nativeCpEntries.length > 0;
+  }
+
+  @Test
+  public void testRequiresOk () {
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = { "+site=" + dir + "configTestSite.properties",
+                      dir + "configTestRequires.jpf" };
+
+    Config.enableLogging(true);
+    Config conf = new Config( args);
+    String v = conf.getString("whoa");
+    System.out.println("got whoa = " + v);
+    
+    assert (v != null) && v.equals("boa");
+  }
+
+  @Test
+  public void testRequiresFail () {
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = { "+site=" + dir + "configTestSite.properties",
+                      dir + "configTestRequiresFail.jpf" };
+
+    Config.enableLogging(true);
+    Config conf = new Config( args);
+    String v = conf.getString("whoa");
+    System.out.println("got whoa = " + v);
+
+    assert (v == null);
+  }
+
+  @Test
+  public void testIncludes () {
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = { "+site=" + dir + "configTestSite.properties",
+                      dir + "configTestIncludes.jpf" };
+
+    Config.enableLogging(true);
+    Config conf = new Config( args);
+    String v = conf.getString("my.common");
+    System.out.println("got my.common = " + v);
+
+    assert (v != null) && v.equals("whatever");
+  }
+
+  @Test
+  public void testIntArray(){
+    String dir = "src/tests/gov/nasa/jpf/";
+    String[] args = { "+site=" + dir + "configTestSite.properties",
+                      "+arr=-42,0xff,0" };
+
+    Config.enableLogging(true);
+    Config conf = new Config( args);
+    int[] a = conf.getIntArray("arr");
+    
+    assertTrue(a != null);
+    assertTrue(a.length == 3);
+    assertTrue(a[0] == -42 && a[1] == 0xff && a[2] == 0);
+    
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/configTestApp.jpf b/src/tests/gov/nasa/jpf/configTestApp.jpf
new file mode 100644 (file)
index 0000000..379988b
--- /dev/null
@@ -0,0 +1,9 @@
+# test app properties
+
+target = urgh.org.MySystemUnderTest
+
+target.args = a,b,c
+
+#key expansion
+name = mySUT
+${name}.location = ${config_path}
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/configTestCommon.jpf b/src/tests/gov/nasa/jpf/configTestCommon.jpf
new file mode 100644 (file)
index 0000000..16f4fa0
--- /dev/null
@@ -0,0 +1,4 @@
+# this represents properties that are used from a set of app property files,
+# but cannot put into project props because of breaking other extensions
+
+my.common = whatever
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/configTestIncludes.jpf b/src/tests/gov/nasa/jpf/configTestIncludes.jpf
new file mode 100644 (file)
index 0000000..034d03e
--- /dev/null
@@ -0,0 +1,4 @@
+# test @include pseudo property
+
+# normally, this would probably be in a project dir, like ${jpf-symbc}
+@include = ${config_path}/configTestCommon.jpf
diff --git a/src/tests/gov/nasa/jpf/configTestRequires.jpf b/src/tests/gov/nasa/jpf/configTestRequires.jpf
new file mode 100644 (file)
index 0000000..b81fbb8
--- /dev/null
@@ -0,0 +1,7 @@
+# property file that requires previous property settings
+
+#pseudo property that causes exception if the provided value is not a defined key
+@requires = jpf.core
+
+# just to check if we got loaded
+whoa = boa
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/configTestRequiresFail.jpf b/src/tests/gov/nasa/jpf/configTestRequiresFail.jpf
new file mode 100644 (file)
index 0000000..786de0a
--- /dev/null
@@ -0,0 +1,6 @@
+# this should fail
+
+@requires = thisDoesNotExist
+
+# just to check if we got loaded
+whoa = boa
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/configTestSite.properties b/src/tests/gov/nasa/jpf/configTestSite.properties
new file mode 100644 (file)
index 0000000..8eef7cc
--- /dev/null
@@ -0,0 +1,13 @@
+# just a dummy for the config unit test
+
+jpf.root=/Users/pcmehlitz/projects/jpf-v5
+
+jpf.core = ${jpf.root}/jpf-core
+
+#---------- installed extensions
+
+ext.numeric=${jpf.root}/jpf-numeric
+extensions=,${ext.numeric}
+
+ext.swing=${jpf.root}/jpf-swing
+extensions+=,${ext.swing}
diff --git a/src/tests/gov/nasa/jpf/jvm/ClassInfoTest.java b/src/tests/gov/nasa/jpf/jvm/ClassInfoTest.java
new file mode 100644 (file)
index 0000000..e4940a2
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * unit test for ClassInfo initialization
+ */
+public class ClassInfoTest extends TestJPF {
+
+  @interface X {
+    String value() default "nothing";
+  }
+
+  @interface Y {
+    int[] value();
+  }
+
+  @X
+  public static class MyClass implements Cloneable {
+    public static final int D = 42;
+
+    @X("data") String s;
+
+    public MyClass(String s) {
+      this.s = s;
+      foo();
+    }
+
+    public static int whatIsIt() {
+      int d = D;
+      switch (d) {
+        case 41:
+          d = -1;
+          break;
+        case 42:
+          d = 0;
+          break;
+        case 43:
+          d = 1;
+          break;
+        default:
+          d = 2;
+          break;
+      }
+      return d;
+    }
+
+    public boolean isItTheAnswer (boolean b, @X @Y({1,2,3}) int d, String s){
+      switch (d){
+        case 42: return true;
+        default: return false;
+      }
+    }
+
+    protected void foo() throws IndexOutOfBoundsException {
+      @X int d = D;
+
+      Object[] a = new Object[2];
+      String s = "blah";
+      a[0] = s;
+
+      String x = (String)a[0];
+      Object o = a;
+      if (o instanceof Object[]){
+        o = x;
+      }
+      if (o instanceof String){
+        o = null;
+      }
+
+      Object[][] aa = new Object[2][2];
+
+      try {
+        char c = s.charAt(d);
+      } catch (IndexOutOfBoundsException ioobx) {
+        System.out.println("too big");
+        throw ioobx;
+      }
+    }
+
+    @X
+    String getString() {
+      return s;
+    }
+  }
+
+  @Test
+  public void testClassFileInitialization() {
+    File file = new File("build/tests/gov/nasa/jpf/jvm/ClassInfoTest$MyClass.class");
+
+    try {
+      ClassInfo ci = new NonResolvedClassInfo( "gov.nasa.jpf.jvm.ClassInfoTest$MyClass", file);
+
+      assert ci.getName().equals("gov.nasa.jpf.jvm.ClassInfoTest$MyClass");
+
+      System.out.println("-- declared instance fields");
+      for (FieldInfo fi : ci.getDeclaredInstanceFields()){
+        System.out.print(fi.getType());
+        System.out.print(' ');
+        System.out.println(fi.getName());
+      }
+
+      assert ci.getNumberOfDeclaredInstanceFields() == 1;
+      assert ci.getNumberOfStaticFields() == 1;
+
+      System.out.println();
+      System.out.println("-- methods");
+      for (MethodInfo mi : ci){
+        System.out.println(mi.getUniqueName());
+      }
+
+
+    } catch (ClassParseException cfx){
+      //cfx.printStackTrace();
+      fail("ClassParseException: " + cfx);
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/jvm/JVMStackFrameTest.java b/src/tests/gov/nasa/jpf/jvm/JVMStackFrameTest.java
new file mode 100644 (file)
index 0000000..15500f3
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import org.junit.Test;
+
+
+/**
+ * unit test for StackFrame operations
+ */
+public class JVMStackFrameTest extends TestJPF {
+
+  @Test
+  public void testDup2_x1() {
+    // 1 2 3  => 2 3.1 2 3
+
+    JVMStackFrame frame = new JVMStackFrame(0, 10);
+
+    frame.push(1);
+    frame.push(2);
+    frame.push(3);
+    frame.printOperands(System.out);
+
+    frame.dup2_x1();
+    frame.printOperands(System.out);
+
+    assert frame.getTopPos() == 4;
+    assert frame.peek(4) == 2;
+    assert frame.peek(3) == 3;
+    assert frame.peek(2) == 1;
+    assert frame.peek(1) == 2;
+    assert frame.peek(0) == 3;
+  }
+
+  @Test
+  public void testDup2_x1_Attrs() {
+    // 1 2 3  => 2 3.1 2 3
+
+    JVMStackFrame frame = new JVMStackFrame(0, 10);
+
+    frame.push(1); frame.setOperandAttr("1");
+    frame.push(2); frame.setOperandAttr("2");
+    frame.push(3); frame.setOperandAttr("3");
+    frame.printOperands(System.out);
+
+    frame.dup2_x1();
+    frame.printOperands(System.out);
+
+    assert frame.getTopPos() == 4;
+    assert frame.peek(4) == 2 && frame.getOperandAttr(4) == "2"; // same const pool string
+    assert frame.peek(3) == 3 && frame.getOperandAttr(3) == "3";
+    assert frame.peek(2) == 1 && frame.getOperandAttr(2) == "1";
+    assert frame.peek(1) == 2 && frame.getOperandAttr(1) == "2";
+    assert frame.peek(0) == 3 && frame.getOperandAttr(0) == "3";
+  }
+
+
+  @Test
+  public void testDup2_x2() {
+    // 1 2 3 4  => 3 4.1 2 3 4
+
+    JVMStackFrame frame = new JVMStackFrame(0, 10);
+
+    frame.push(1);
+    frame.push(2);
+    frame.push(3);
+    frame.push(4);
+    frame.printOperands(System.out);
+
+    frame.dup2_x2();
+    frame.printOperands(System.out);
+
+    assert frame.getTopPos() == 5;
+    assert frame.peek(5) == 3;
+    assert frame.peek(4) == 4;
+    assert frame.peek(3) == 1;
+    assert frame.peek(2) == 2;
+    assert frame.peek(1) == 3;
+    assert frame.peek(0) == 4;
+  }
+
+  @Test
+  public void testDup2_x2_Attrs() {
+    // 1 2 3 4  => 3 4.1 2 3 4
+
+    JVMStackFrame frame = new JVMStackFrame(0, 10);
+
+    frame.push(1); frame.setOperandAttr("1");
+    frame.push(2); frame.setOperandAttr("2");
+    frame.push(3); frame.setOperandAttr("3");
+    frame.push(4); frame.setOperandAttr("4");
+    frame.printOperands(System.out);
+
+    frame.dup2_x2();
+    frame.printOperands(System.out);
+
+    assert frame.getTopPos() == 5;
+    assert frame.peek(5) == 3 && frame.getOperandAttr(5) == "3";  // same const pool string
+    assert frame.peek(4) == 4 && frame.getOperandAttr(4) == "4";
+    assert frame.peek(3) == 1 && frame.getOperandAttr(3) == "1";
+    assert frame.peek(2) == 2 && frame.getOperandAttr(2) == "2";
+    assert frame.peek(1) == 3 && frame.getOperandAttr(1) == "3";
+    assert frame.peek(0) == 4 && frame.getOperandAttr(0) == "4";
+  }
+
+  @Test
+  public void testPushLong() {
+    // Push/Pop long value and also  JVMStackFrame.getLocalValueObject
+
+    JVMStackFrame frame = new JVMStackFrame(0, 2);
+
+    long value = 0x123456780ABCDEFL;
+    frame.pushLong(value);
+
+    Object obj_Long = frame.getLocalValueObject(new LocalVarInfo("testLong", "J", "J", 0, 0, 0));
+    assert obj_Long != null;
+    assert obj_Long instanceof Long;
+
+    long result_getLocValObj = (Long) obj_Long;
+    long result_popLong = frame.popLong();
+
+    assert result_getLocValObj == value;
+    assert result_popLong == value;
+  }
+
+  @Test
+  public void testPushDouble() {
+    // Push/Pop double value and also  JVMStackFrame.getLocalValueObject
+
+    JVMStackFrame frame = new JVMStackFrame(2, 10);
+    // Initialize local values and the stack frame
+    frame.push(1);
+    frame.push(2);
+    frame.push(3);
+
+    double value = Math.PI;
+
+    frame.pushDouble(value);
+
+    Object obj_Double = frame.getLocalValueObject(new LocalVarInfo("testDouble", "D", "D", 0, 0, frame.getTopPos() - 1));
+    assert obj_Double != null;
+    assert obj_Double instanceof Double;
+
+    double result_getLocValObj = (Double) obj_Double;
+    double result_popLong = frame.popDouble();
+
+    assert result_getLocValObj == value;
+    assert result_popLong == value;
+
+    assert frame.peek(0) == 3;
+    assert frame.peek(1) == 2;
+    assert frame.peek(2) == 1;
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/jvm/MethodInfoTest.java b/src/tests/gov/nasa/jpf/jvm/MethodInfoTest.java
new file mode 100644 (file)
index 0000000..26732c9
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * unit test for MethodInfos
+ */
+public class MethodInfoTest extends TestJPF {
+
+  static class MyClass {
+    static double staticNoArgs() {int a=42; double b=42.0; b+=a; return b;}
+    static double staticInt (int intArg) {int a=42; double b=42.0; b+=a; return b;}
+    static double staticIntString (int intArg, String stringArg) {int a=42; double b=42.0; b+=a; return b;}
+    
+    double instanceNoArgs() {int a=42; double b=42.0; b+=a; return b;}
+    double instanceInt( int intArg) {int a=42; double b=42.0; b+=a; return b;}
+    double instanceIntString  (int intArg, String stringArg) {int a=42; double b=42.0; b+=a; return b;}
+    
+    int instanceCycleMethod (int intArg, int int2Arg) {
+      for (int i = 0; i < int2Arg; ++i) {
+        // it's important to have a for cycle because it breaks the instruction per line monotony
+        intArg += intArg;
+      }
+      return intArg;
+    }
+  }
+  
+  @Test
+  public void testMethodArgs() {
+    File file = new File("build/tests/gov/nasa/jpf/jvm/MethodInfoTest$MyClass.class");
+
+    try {
+      ClassInfo ci = new NonResolvedClassInfo( "gov.nasa.jpf.jvm.MethodInfoTest$MyClass",  file);
+      MethodInfo mi;
+      LocalVarInfo[] args;
+
+      //--- the statics
+      mi = ci.getMethod("staticNoArgs", "()D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args not empty or null", args != null && args.length == 0);
+
+      mi = ci.getMethod("staticInt", "(I)D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args null", args != null);
+      for (LocalVarInfo lvi : args){
+        System.out.println("     " + lvi);
+      }
+      assertTrue(args.length == 1 && args[0].getName().equals("intArg"));
+
+      mi = ci.getMethod("staticIntString", "(ILjava/lang/String;)D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args null", args != null);
+      for (LocalVarInfo lvi : args){
+        System.out.println("     " + lvi);
+      }
+      assertTrue(args.length == 2 && args[0].getName().equals("intArg") && args[1].getName().equals("stringArg"));
+
+      
+      //--- the instances
+      mi = ci.getMethod("instanceNoArgs", "()D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args null", args != null);
+      for (LocalVarInfo lvi : args){
+        System.out.println("     " + lvi);
+      }
+      assertTrue(args.length == 1 && args[0].getName().equals("this"));
+      
+      mi = ci.getMethod("instanceInt", "(I)D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args null", args != null);
+      for (LocalVarInfo lvi : args){
+        System.out.println("     " + lvi);
+      }
+      assertTrue(args.length == 2 && args[0].getName().equals("this") && args[1].getName().equals("intArg"));
+
+      mi = ci.getMethod("instanceIntString", "(ILjava/lang/String;)D", false);
+      System.out.println("-- checking: " + mi);
+      args = mi.getArgumentLocalVars();
+      assertTrue("args null", args != null);
+      for (LocalVarInfo lvi : args){
+        System.out.println("     " + lvi);
+      }
+      assertTrue(args.length == 3 && args[0].getName().equals("this") 
+          && args[1].getName().equals("intArg") && args[2].getName().equals("stringArg"));
+
+    } catch (NullPointerException npe){
+      npe.printStackTrace();
+      fail("method not found");
+    } catch (ClassParseException cfx){
+      cfx.printStackTrace();
+      fail(cfx.toString());
+    }
+  }
+  
+  @Test
+  public void testGetInstructionsForLine () {
+    File file = new File(
+            "build/tests/gov/nasa/jpf/jvm/MethodInfoTest$MyClass.class");
+    try {
+      ClassInfo ci = new NonResolvedClassInfo("gov.nasa.jpf.jvm.MethodInfoTest$MyClass", file);
+      MethodInfo mi = ci.getMethod("instanceCycleMethod", "(II)I", false);
+
+      nextInstruction:
+      for (Instruction instruction : mi.getInstructions()) {
+        int l = instruction.getLineNumber();
+        Instruction[] foundInstructions = mi.getInstructionsForLine(l);
+        System.out.printf("%d : %s\n", l, instruction);
+
+        for (int j=0; j<foundInstructions.length; j++){
+          if (foundInstructions[j] == instruction){
+            continue nextInstruction;
+          }
+        }
+        
+        fail("instruction not in list: " + instruction);
+      }
+    } catch (ClassParseException cfx) {
+      cfx.printStackTrace();
+      fail(cfx.toString());
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/jvm/NonResolvedClassInfo.java b/src/tests/gov/nasa/jpf/jvm/NonResolvedClassInfo.java
new file mode 100644 (file)
index 0000000..a9d9abe
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.jvm;
+
+import gov.nasa.jpf.jvm.bytecode.InstructionFactory;
+import gov.nasa.jpf.vm.ClassParseException;
+import gov.nasa.jpf.vm.NativePeer;
+import java.io.File;
+
+/**
+ * just a helper construct to create ClassInfos that can be used in unit tests
+ * (without superclasses, clinit calls and the other bells and whistles)
+ */
+class NonResolvedClassInfo extends JVMClassInfo {
+    
+  NonResolvedClassInfo (String clsName, File file) throws ClassParseException {
+    super( clsName, null, new ClassFile(file), file.getAbsolutePath(), new JVMCodeBuilder(new InstructionFactory()));
+  }
+
+  //--- these are overridden so that we can create instances without the whole JPF ClassInfo environment
+  
+  @Override
+  protected void resolveClass() {
+    linkFields();
+  }
+
+  @Override
+  protected NativePeer loadNativePeer(){
+    return null;
+  }
+  
+  @Override
+  protected void setAssertionStatus(){
+    // nothing
+  }
+  
+}
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/test/basic/HarnessTest.java b/src/tests/gov/nasa/jpf/test/basic/HarnessTest.java
new file mode 100644 (file)
index 0000000..77789d6
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * basic test to test the test harness
+ */
+public class HarnessTest extends TestJPF {
+
+  int d;
+
+  @Test
+  public void noViolation() {
+    if (verifyNoPropertyViolation()) {
+      d += 42;
+
+      System.out.println("** this is noViolation() - it should succeed");
+    }
+  }
+
+  @Test
+  public void verifyAssert() {
+    if (verifyAssertionErrorDetails("java.lang.AssertionError : wrong answer..")) {
+      System.out.println("** this is verifyAssert() - JPF should find an AssertionError");
+
+      assert d == 42 : "wrong answer..";
+    }
+  }
+
+  @Test
+  public void verifyNullPointerException() {
+    if (verifyUnhandledException("java.lang.NullPointerException")) {
+      System.out.println("** this is verifyNullPointerException() - JPF should find an NPE");
+
+      String s = null;
+
+      s.length();
+    }
+  }
+
+  @Test
+  public void verifyRuntimeException() {
+    if (verifyPropertyViolation(new TypeRef("gov.nasa.jpf.vm.NoUncaughtExceptionsProperty"))) {
+      System.out.println("** this is verifyRuntimeException() - JPF should find an unhandled exception");
+
+      throw new RuntimeException("Bang!");
+    }
+  }
+
+  @Test
+  public void verifyJPFExcept() {
+    if (verifyJPFException(new TypeRef("gov.nasa.jpf.JPFConfigException"), "+vm.class=InvalidVMClass", "+pass_exceptions")) {
+      fail("** JPF should not run");
+    }
+  }
+
+  // low level TestJPF API test
+  @Test
+  public void testLowLevelAPI() {
+    
+    JPF jpf = noPropertyViolation();
+
+    if (jpf == null) {
+      System.out.println("** this is low level API test - it should succeed");
+    } else {
+      assert jpf.getSearchErrors().isEmpty() : "unexpected JPF search errors";
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/InstructionFactoryTest.java b/src/tests/gov/nasa/jpf/test/basic/InstructionFactoryTest.java
new file mode 100644 (file)
index 0000000..ccefe55
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import org.junit.Test;
+
+/**
+ * basic test for InstructionFactories
+ */
+public class InstructionFactoryTest extends TestJPF {
+
+  /**
+   * Add double
+   * ..., value1, value2 => ..., result
+   */
+  public static class DADD extends gov.nasa.jpf.jvm.bytecode.DADD {
+    @Override
+    public Instruction execute (ThreadInfo ti) {
+      StackFrame frame = ti.getModifiableTopFrame();
+      
+      double v1 = frame.popDouble();
+      double v2 = frame.popDouble();
+      
+      double r = v1 + v2;
+      System.out.printf("DADD %f + %f => %f\n", v1, v2, r);
+      
+      System.out.println(" ..but we negate it just for kicks..");
+      r = -r;
+
+      frame.pushDouble(r);
+
+      return getNext(ti);
+    }
+  }
+  
+  public static class MyInsnFactory extends gov.nasa.jpf.jvm.bytecode.InstructionFactory {
+    public  MyInsnFactory (Config conf){
+      // nothing here
+    }
+    
+    @Override
+    public Instruction dadd() {
+      return new DADD();
+    }
+  }
+  
+  @Test
+  public void testDadd() {
+    if (verifyNoPropertyViolation("+jvm.insn_factory.class=.test.basic.InstructionFactoryTest$MyInsnFactory")) {
+      double a = 41.0;
+      double b = a + 1.0;
+      System.out.print("b=");
+      System.out.println(b);
+      assertTrue( b < 0); // since we used our own (twisted) DADD
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/JPF_gov_nasa_jpf_test_basic_MJITest.java b/src/tests/gov/nasa/jpf/test/basic/JPF_gov_nasa_jpf_test_basic_MJITest.java
new file mode 100644 (file)
index 0000000..1af02b0
--- /dev/null
@@ -0,0 +1,240 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.DirectCallStackFrame;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.UncaughtException;
+
+/**
+ * native peer class for unit testing MJI
+ */
+public class JPF_gov_nasa_jpf_test_basic_MJITest extends NativePeer {
+
+  // intercept <clinit>
+  @MJI
+  public void $clinit (MJIEnv env, int rcls) {
+    System.out.println("# entering native <clinit>");
+    env.setStaticIntField(rcls, "sdata", 42);
+  }
+
+  // intercept MJITest(int i) ctor
+  @MJI
+  public void $init__I__V (MJIEnv env, int robj, int i) {
+    // NOTE : if we directly intercept the ctor, then we also have
+    // to take care of calling the proper superclass ctors
+    // better approach is to refactor this into a separate native method
+    // (say init0(..))
+    System.out.println("# entering native <init>(I)");
+    env.setIntField(robj, "idata", i);
+  }
+
+  @MJI
+  public int nativeCreate2DimIntArray__II___3_3I (MJIEnv env, int robj, int size1,
+                                              int size2) {
+    System.out.println("# entering nativeCreate2DimIntArray()");
+    int ar = env.newObjectArray("[I", size1);
+
+    for (int i = 0; i < size1; i++) {
+      int ea = env.newIntArray(size2);
+
+      if (i == 1) {
+        env.setIntArrayElement(ea, 1, 42);
+      }
+
+      env.setReferenceArrayElement(ar, i, ea);
+    }
+
+    return ar;
+  }
+
+  // check if the non-mangled name lookup works
+  @MJI
+  public int nativeCreateIntArray (MJIEnv env, int robj, int size) {
+    System.out.println("# entering nativeCreateIntArray()");
+
+    int ar = env.newIntArray(size);
+
+    env.setIntArrayElement(ar, 1, 1);
+
+    return ar;
+  }
+
+  @MJI
+  public int nativeCreateStringArray (MJIEnv env, int robj, int size) {
+    System.out.println("# entering nativeCreateStringArray()");
+
+    int ar = env.newObjectArray("Ljava/lang/String;", size);
+    env.setReferenceArrayElement(ar, 1, env.newString("one"));
+
+    return ar;
+  }
+
+  @MJI
+  public void nativeException____V (MJIEnv env, int robj) {
+    System.out.println("# entering nativeException()");
+    env.throwException("java.lang.UnsupportedOperationException", "caught me");
+  }
+
+  @SuppressWarnings("null")
+  @MJI
+  public int nativeCrash (MJIEnv env, int robj) {
+    System.out.println("# entering nativeCrash()");
+    String s = null;
+    return s.length();
+  }
+
+  @MJI
+  public int nativeInstanceMethod (MJIEnv env, int robj, double d,
+                                          char c, boolean b, int i) {
+    System.out.println("# entering nativeInstanceMethod() d=" + d +
+            ", c=" + c + ", b=" + b + ", i=" + i);
+
+    if ((d == 2.0) && (c == '?') && b) {
+      return i + 2;
+    }
+
+    return 0;
+  }
+
+  @MJI
+  public long nativeStaticMethod__JLjava_lang_String_2__J (MJIEnv env, int rcls, long l,
+                                                                  int stringRef) {
+    System.out.println("# entering nativeStaticMethod()");
+
+    String s = env.getStringObject(stringRef);
+
+    if ("Blah".equals(s)) {
+      return l + 2;
+    }
+
+    return 0;
+  }
+
+
+  /*
+   * nativeRoundtripLoop shows how to
+   *
+   *  (1) round trip into JPF executed code from within native methods
+   *
+   *  (2) loop inside of native methods that do round trips (using the
+   *      DirectCallStackFrame's local slots)
+   *
+   * the call chain is:
+   *
+   *   JPF: testRoundtripLoop
+   *     VM: nativeRoundTripLoop  x 3
+   *       JPF: roundtrip
+   *         VM: nativeInnerRoundtrip
+   */
+
+  @MJI
+  public int nativeInnerRoundtrip__I__I (MJIEnv env, int robj, int a){
+    System.out.println("# entering nativeInnerRoundtrip()");
+
+    return a+2;
+  }
+
+  @MJI
+  public int nativeRoundtripLoop__I__I (MJIEnv env, int robj, int a) {
+    System.out.println("# entering nativeRoundtripLoop(): " + a);
+
+    MethodInfo mi = env.getClassInfo(robj).getMethod("roundtrip(I)I",false);
+    ThreadInfo ti = env.getThreadInfo();
+    DirectCallStackFrame frame = ti.getReturnedDirectCall();
+
+    if (frame == null){ // first time
+      frame = mi.createDirectCallStackFrame(ti, 1);
+      frame.setLocalVariable( 0, 0);
+      
+      int argOffset = frame.setReferenceArgument(0, robj, null);
+      frame.setArgument( argOffset, a+1, null);
+      
+      ti.pushFrame(frame);
+
+      return 42; // whatever, we come back
+
+    } else { // direct call returned
+
+      // this method can't be executed unless the class is already initialized,
+      // i.e. we don't have to check for overlayed clinit calls and the frame
+      // has to be the one we pushed
+      assert frame.getCallee() == mi;
+      
+      // this shows how to get information back from the JPF roundtrip into
+      // the native method
+      int r = frame.getResult(); // the return value of the direct call above
+      int i = frame.getLocalVariable(0);
+
+      if (i < 3) { // repeat the round trip
+        // we have to reset so that the PC is re-initialized
+        frame.reset();
+        frame.setLocalVariable(0, i + 1);
+        
+        int argOffset = frame.setReferenceArgument( 0, robj, null);
+        frame.setArgument( argOffset, r+1, null);
+        
+        ti.pushFrame(frame);
+        return 42;
+
+      } else { // done, return the final value
+        return r;
+      }
+    }
+  }
+
+  /**
+   * this shows how to synchronously JPF-enter a method from native peer or
+   * listener code
+   */
+  @MJI
+  public int nativeHiddenRoundtrip__I__I (MJIEnv env, int robj, int a){
+    ThreadInfo ti = env.getThreadInfo();
+    
+    System.out.println("# entering nativeHiddenRoundtrip: " + a);
+    MethodInfo mi = env.getClassInfo(robj).getMethod("atomicStuff(I)I",false);
+
+    DirectCallStackFrame frame = mi.createDirectCallStackFrame(ti, 0);
+    
+    int argOffset = frame.setReferenceArgument( 0, robj, null);
+    frame.setArgument( argOffset, a, null);
+    frame.setFireWall();
+
+    try {
+      ti.executeMethodHidden(frame);
+      //ti.advancePC();
+
+    } catch (UncaughtException ux) {  // frame's method is firewalled
+      System.out.println("# hidden method execution failed, leaving nativeHiddenRoundtrip: " + ux);
+      ti.clearPendingException();
+      ti.popFrame(); // this is still the DirectCallStackFrame, and we want to continue execution
+      return -1;
+    }
+
+    // get the return value from the (already popped) frame
+    int res = frame.getResult();
+
+    System.out.println("# exit nativeHiddenRoundtrip: " + res);
+    return res;
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/ListenerTest.java b/src/tests/gov/nasa/jpf/test/basic/ListenerTest.java
new file mode 100644 (file)
index 0000000..04b214b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.SingleProcessVM;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class ListenerTest extends TestJPF {
+
+  public static class Listener extends ListenerAdapter {
+    @Override
+    public void searchStarted (Search search){
+      System.out.println("-- listener got searchStarted() notification");
+      Verify.incrementCounter(0);
+    }
+  }
+  
+  public static class TestVM extends SingleProcessVM {
+    public TestVM (JPF jpf, Config config){
+      super(jpf, config);
+      
+      jpf.addListener( new Listener());
+    }
+  }
+  
+  @Test
+  public void testPendingListeners (){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation("+vm.class=gov.nasa.jpf.test.basic.ListenerTest$TestVM")){
+      System.out.println("this is verified by JPF");
+    }
+    
+    if (!isJPFRun()){
+      assertTrue("init listener got no searchStarted() notification", Verify.getCounter(0) == 1);
+    }
+  }
+  
+  // <2do> ... and tons more to follow
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/MJITest.java b/src/tests/gov/nasa/jpf/test/basic/MJITest.java
new file mode 100644 (file)
index 0000000..cd6e1a6
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * model class for MJI test
+ */
+public class MJITest extends TestJPF {
+
+  static int sdata;
+
+  static {
+    // only here to be intercepted
+    sdata = 0; // dummy insn required for the Eclipse compiler (skips empty methods)
+  }
+
+  int idata = 0;
+
+  public MJITest () {
+    // not intercepted
+  }
+
+  MJITest (int data) {
+    // only here to be intercepted
+  }
+
+  @Test
+  public void testNativeClInit () {
+    if (verifyNoPropertyViolation()){
+      assert (sdata == 42) : "native '<clinit>' failed";
+    }
+  }
+
+  @Test
+  public void testNativeInit () {
+    if (verifyNoPropertyViolation()){
+      MJITest t = new MJITest(42);
+      assert (t.idata == 42)  : "native '<init>' failed";
+    }
+  }
+
+  @Test
+  public void testNativeCreate2DimIntArray () {
+    if (verifyNoPropertyViolation()){
+      int[][] a = nativeCreate2DimIntArray(2, 3);
+
+      assert (a != null) : "native int[][]  creation failed: null";
+
+      assert (a.getClass().isArray()) : "native int[][] creation failed: not an array";
+
+      assert (a.getClass().getComponentType().getName().equals("[I")) :
+              "native int[][] creation failed: wrong component type";
+
+      assert ((a[1][1] == 42)) : "native int[][] element init failed";
+    }
+  }
+
+  @Test
+  public void testNativeCreateIntArray () {
+    if (verifyNoPropertyViolation()){
+      int[] a = nativeCreateIntArray(3);
+
+      assert (a != null) : "native int array creation failed: null";
+
+      assert (a.getClass().isArray()) : "native int array creation failed: not an array";
+
+      assert (a.getClass().getComponentType() == int.class) :
+              "native int array creation failed: wrong component type";
+
+      assert ((a[1] == 1)) : "native int array element init failed";
+    }
+  }
+
+  @Test
+  public void testNativeCreateStringArray () {
+    if (verifyNoPropertyViolation()){
+      String[] a = nativeCreateStringArray(3);
+
+      assert (a != null) : "native String array creation failed: null";
+
+      assert (a.getClass().isArray()) :
+              "native String array creation failed: not an array";
+
+      assert (a.getClass().getComponentType() == String.class) :
+              "native String array creation failed: wrong component type";
+
+      assert ("one".equals(a[1])) : "native String array element init failed";
+    }
+  }
+
+  @Test
+  public void testNativeException () {
+    if (verifyNoPropertyViolation()){
+      try {
+        nativeException();
+      } catch (UnsupportedOperationException ux) {
+        String details = ux.getMessage();
+
+        if ("caught me".equals(details)) {
+          ux.printStackTrace();
+          return;
+        } else {
+          assert false : "wrong native exception details: " + details;
+        }
+      } catch (Throwable t) {
+        assert false : "wrong native exception type: " + t.getClass();
+      }
+
+      assert false : "no native exception thrown";
+    }
+  }
+
+  @Test
+  public void testNativeCrash () {
+    if (verifyJPFException(new TypeRef("gov.nasa.jpf.JPFNativePeerException"))){
+      nativeCrash();
+    }
+  }
+
+  @Test
+  public void testNativeInstanceMethod () {
+    if (verifyNoPropertyViolation()){
+      int res = nativeInstanceMethod(2.0, '?', true, 40);
+
+      assert (res == 42) : "native instance method failed: " + res;
+    }
+  }
+
+  @Test
+  public void testNativeStaticMethod () {
+    if (verifyNoPropertyViolation()){
+      long res = nativeStaticMethod(40, "Blah");
+
+      assert (res == 42L) : "native static method failed";
+    }
+  }
+
+
+  int roundtrip (int a){ // that's called from the native testRoundtripLoop0
+    System.out.println("### roundtrip " + a);
+    return nativeInnerRoundtrip(a);
+  }
+
+  @Test
+  public void testRoundtripLoop () {
+    if (verifyNoPropertyViolation()){
+      int res = nativeRoundtripLoop(42);
+
+      assert (res == 54) : ("roundtrip loop failed (expected 54) : " + res);
+    }
+  }
+
+  @Test
+  public void testHiddenRoundtrip () {
+    if (verifyNoPropertyViolation()){
+      System.out.println("## entering testHiddenroundtrip()");
+      int res = echo(20) + nativeHiddenRoundtrip(21); // get something on the operand stack
+      assert (res == 42) : ("atomic roundtrip failed (expected 42): " + res);
+
+      System.out.println("## exiting testHiddenroundtrip()");
+    }
+  }
+
+  @Test
+  public void testHiddenRoundtripException () {
+    if (verifyNoPropertyViolation()){
+      System.out.println("## entering testHiddenroundtripException()");
+      int res = echo(20) + nativeHiddenRoundtrip(-1); // get something on the operand stack
+      assert (res == 19) : "atomic roundtrip exception not caught";
+
+      System.out.println("## exiting testHiddenroundtripException()");
+    }
+  }
+
+  int atomicStuff (int a) {  // this is called from nativeAtomicRoundtrip()
+    System.out.print("## in atomicStuff : ");
+    System.out.println(a);
+
+    if (a < 0) {
+      System.out.println("## atomicStuff throwing IllegalArgumentException");
+      throw new IllegalArgumentException("negative atomicStuff argument");
+    }
+
+    int res = echo(a + 1);
+    return res;
+  }
+  int echo (int a) {
+    System.out.print("## in echo : ");
+    System.out.println(a);
+    return a;
+  }
+
+  native int nativeHiddenRoundtrip (int a);
+
+  native int nativeInnerRoundtrip (int a);
+
+  native int nativeRoundtripLoop (int a);
+
+  native int[][] nativeCreate2DimIntArray (int s1, int s2);
+
+  native int[] nativeCreateIntArray (int size);
+
+  native String[] nativeCreateStringArray (int size);
+
+  native void nativeException ();
+
+  native int nativeCrash ();
+
+  native int nativeInstanceMethod (double d, char c, boolean b, int i);
+
+  native long nativeStaticMethod (long l, String s);
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/TestJPFMainTest.java b/src/tests/gov/nasa/jpf/test/basic/TestJPFMainTest.java
new file mode 100644 (file)
index 0000000..d84d750
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class TestJPFMainTest extends TestJPF {
+  
+  public static void main(String testMethods[]) throws Throwable {
+    runTestsOfThisClass(testMethods);
+  }
+
+  @Test
+  public void ensureCompatibility() {
+    if (!Verify.isRunningInJPF()) {
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      System.out.println("incrementing test counter");
+      Verify.incrementCounter(0);
+      
+    } else { // Runs after JPF finishes
+      assertEquals(1, Verify.getCounter(0));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/basic/TestJPFNoMainTest.java b/src/tests/gov/nasa/jpf/test/basic/TestJPFNoMainTest.java
new file mode 100644 (file)
index 0000000..74bd619
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class TestJPFNoMainTest extends TestJPF
+{
+   @Test
+   public void ensureCompatibility()
+   {
+      if (!Verify.isRunningInJPF()){
+        Verify.resetCounter(0);
+      }
+      
+      if (verifyNoPropertyViolation()){
+        System.out.println("incrementing test counter");
+        Verify.incrementCounter(0);
+      } else  { // Runs after JPF finishes
+        assertEquals(1, Verify.getCounter(0));
+      }
+   }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicIntegerFieldUpdaterTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicIntegerFieldUpdaterTest.java
new file mode 100644 (file)
index 0000000..202ac68
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
+
+import org.junit.Test;
+
+/**
+ * raw test for java.util.concurrent.atomic.AtomicIntegerFieldUpdater
+ */
+public class AtomicIntegerFieldUpdaterTest extends TestJPF {
+
+  int value;
+
+  @Test
+  public void testField() {
+
+    if (verifyNoPropertyViolation()) {
+      AtomicIntegerFieldUpdater<AtomicIntegerFieldUpdaterTest> upd =
+              AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdaterTest.class, "value");
+
+      final int v1 = 98734534;
+      final int v2 = 79304843;
+      final int nogo = 46907854;
+      value = v1;
+
+      assert upd.compareAndSet(this, v1, v2);
+      assert value == v2;
+
+      assert !upd.compareAndSet(this, v1, nogo);
+      assert value == v2;
+
+      assert value == upd.get(this);
+
+      assert v2 == upd.getAndSet(this, v1);
+      assert value == v1;
+
+      upd.set(this, v2);
+      assert value == v2;
+
+      upd.lazySet(this, v1);
+      assert value == v1;
+
+      assert upd.weakCompareAndSet(this, v1, v2);
+      assert value == v2;
+
+      assert !upd.weakCompareAndSet(this, v1, nogo);
+      assert value == v2;
+
+      assert v2 == upd.getAndAdd(this, 5);
+      assert v2 + 5 == value;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicLongFieldUpdaterTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicLongFieldUpdaterTest.java
new file mode 100644 (file)
index 0000000..5aebb00
--- /dev/null
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.util.concurrent.atomic.AtomicLongFieldUpdater;
+
+import org.junit.Test;
+
+/**
+ * raw test for java.util.concurrent.atomic.AtomicLongFieldUpdater
+ */
+public class AtomicLongFieldUpdaterTest extends TestJPF {
+
+  static {
+    Verify.setProperties("cg.enumerate_cas=true");
+  }
+  long value;
+
+  @Test
+  public void testField() {
+    if (verifyNoPropertyViolation("+cg.enumerate_cas=true")) {
+      AtomicLongFieldUpdater<AtomicLongFieldUpdaterTest> upd =
+              AtomicLongFieldUpdater.newUpdater(AtomicLongFieldUpdaterTest.class, "value");
+
+      final long v1 = 723489234098734534L;
+      final long v2 = 256092348679304843L;
+      final long nogo = 823468902346907854L;
+      value = v1;
+
+      assert upd.compareAndSet(this, v1, v2);
+      assert value == v2;
+
+      assert !upd.compareAndSet(this, v1, nogo);
+      assert value == v2;
+
+      assert value == upd.get(this);
+
+      assert v2 == upd.getAndSet(this, v1);
+      assert value == v1;
+
+      upd.set(this, v2);
+      assert value == v2;
+
+      upd.lazySet(this, v1);
+      assert value == v1;
+
+      assert upd.weakCompareAndSet(this, v1, v2);
+      assert value == v2;
+
+      assert !upd.weakCompareAndSet(this, v1, nogo);
+      assert value == v2;
+
+      assert v2 == upd.getAndAdd(this, 5);
+      assert v2 + 5 == value;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicReferenceFieldUpdaterTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/AtomicReferenceFieldUpdaterTest.java
new file mode 100644 (file)
index 0000000..e37bbe8
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
+
+import org.junit.Test;
+
+/**
+ * raw test for java.util.concurrent.atomic.AtomicReferenceFieldUpdater
+ */
+public class AtomicReferenceFieldUpdaterTest extends TestJPF {
+
+  static final String[] JPF_ARGS = {"+cg.enumreate_cas=true"};
+
+  //--- the test methods
+  String str;
+  byte[] buf;
+
+  @Test
+  public void testStringField() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, String> upd =
+              AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, String.class, "str");
+
+      String s1 = "one";
+      String s2 = "two";
+      str = s1;
+
+      System.out.println(str);
+      assert upd.compareAndSet(this, s1, s2);
+      System.out.println(str);
+      assert str == s2;
+
+      assert !upd.compareAndSet(this, s1, "nogo");
+      assert str == s2;
+      assert str == upd.get(this);
+
+      assert s2 == upd.getAndSet(this, s1);
+      assert str == s1;
+
+      upd.set(this, s2);
+      assert str == s2;
+
+      upd.lazySet(this, s1);
+      assert str == s1;
+
+      assert upd.weakCompareAndSet(this, s1, s2);
+      assert str == s2;
+
+      assert !upd.weakCompareAndSet(this, s1, "nogo");
+      assert str == s2;
+    }
+  }
+
+  @Test
+  public void testByteArrayField() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      AtomicReferenceFieldUpdater<AtomicReferenceFieldUpdaterTest, byte[]> upd =
+              AtomicReferenceFieldUpdater.newUpdater(AtomicReferenceFieldUpdaterTest.class, byte[].class, "buf");
+
+      byte[] b1 = new byte[10];
+      byte[] b2 = new byte[5];
+
+      buf = b1;
+      System.out.println(buf);
+      assert upd.compareAndSet(this, b1, b2);
+      System.out.println(buf);
+      assert (buf == b2);
+
+      assert !upd.compareAndSet(this, b1, new byte[3]);
+      assert (buf == b2);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/CountDownLatchTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/CountDownLatchTest.java
new file mode 100644 (file)
index 0000000..9432d3e
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Test;
+
+public class CountDownLatchTest extends TestJPF {
+
+  private static final int N = 2;                    // Too many states to test if set higher than 2.  jpf-concurrent's gov.nasa.jpf.test.java.concurrent.CountDownLatchTest can handle more threads.
+  private static final int COUNTER_SUCCESS   = 0;
+  private static final int COUNTER_EXCHANGED = 1;
+
+
+  //@Ignore("detects deadlock with exposure CG??")
+  @Test
+  public void testCountDown() throws InterruptedException {
+    if (verifyNoPropertyViolation("+vm.time.model=ConstantZero", "+vm.por.break_on_exposure=true")) {
+
+      final CountDownLatch    latch     = new CountDownLatch(N);
+      final Exchanger<Object> exchanger = new Exchanger<Object>();
+      final ExecutorService   service   = Executors.newFixedThreadPool(N);
+
+      Runnable task = new Runnable() {
+        @Override
+               public void run() {
+          try {
+            Object source = new Object();
+            Object result = exchanger.exchange(source);
+            //Object result = exchanger.exchange(source, 1L, TimeUnit.SECONDS); // If N==2 and without jpf-concurrent, the timeout causes 149,808 states to be explored.
+            assert source != result : "source != result";
+            assert result != null : "result != null";
+            latch.countDown();
+            Verify.incrementCounter(COUNTER_EXCHANGED);
+          } catch (InterruptedException e) {
+            throw new Error(e);
+          }
+        }
+      };
+
+      for (int i = 0; i < N; i++) {
+        service.execute(task);
+      }
+
+      latch.await();
+      service.shutdown();
+
+      Verify.incrementCounter(COUNTER_SUCCESS);
+
+    } else { // outside JPF
+      assert Verify.getCounter(COUNTER_SUCCESS) > 0 : "never succeeded";
+      assert Verify.getCounter(COUNTER_EXCHANGED) > 0 : "never exchanged";
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/ExchangerTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/ExchangerTest.java
new file mode 100644 (file)
index 0000000..715b436
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import java.util.concurrent.Exchanger;
+import java.util.concurrent.TimeUnit;
+import org.junit.Test;
+
+/**
+ * regression test for java.util.concurrent.Exchanger
+ */
+public class ExchangerTest extends TestJPF  {
+
+  static Exchanger<String> exchanger = new Exchanger<String>();
+  
+  static class ExTo extends Thread {
+    @Override
+       public void run() {
+      try {
+        //interrupt();
+        System.out.println("T now exchanging..");
+
+        String response = exchanger.exchange("hi", 1000, TimeUnit.MILLISECONDS);
+
+        System.out.print("T got: ");
+        System.out.println(response);
+        
+        assertTrue(response.equals("there"));
+        Verify.setBitInBitSet(0, 0, true);
+        
+      } catch (Throwable x) {
+        System.out.print("T got exception: ");
+        System.out.println(x);
+        Verify.setBitInBitSet(0, 1, true);
+      }
+    }
+  }
+  
+  @Test
+  public void testTimeoutExchange() {
+    
+    if (verifyNoPropertyViolation()){
+      Thread t = new ExTo();
+      t.start();
+    
+      try {
+        System.out.println("M now exchanging..");
+
+        String response = exchanger.exchange("there", 100, TimeUnit.MILLISECONDS);
+
+        System.out.print("M got: ");
+        System.out.println(response);
+        assertTrue(response.equals("hi"));
+        Verify.setBitInBitSet(0, 2, true);
+
+        
+      } catch (Throwable x){
+        System.out.print("M got exception: ");
+        System.out.println( x);
+        Verify.setBitInBitSet(0, 3, true);
+      }     
+      
+    } else { // not executed under JPF
+      // check if we saw each path at least once
+      assertTrue( Verify.getBitInBitSet(0, 0));
+      assertTrue( Verify.getBitInBitSet(0, 1));
+      assertTrue( Verify.getBitInBitSet(0, 2));
+      assertTrue( Verify.getBitInBitSet(0, 3));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/ExecutorServiceTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/ExecutorServiceTest.java
new file mode 100644 (file)
index 0000000..978415d
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import org.junit.Test;
+
+/**
+ * basic test for java.util.concurrent.ExecutorService
+ * (but it really is a stress test for sun.misc.Unsafe.park() and
+ * Thread.interrupt())
+ */
+public class ExecutorServiceTest extends TestJPF {
+
+  //--- the test methods
+  @Test
+  public void testShutdown() {
+    if (verifyNoPropertyViolation()) {
+      ExecutorService pool = Executors.newFixedThreadPool(1);
+
+      pool.execute(new Runnable() {
+
+        @Override
+               public void run() {
+          System.out.println("run");
+
+        }
+      });
+
+      pool.shutdown();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/concurrent/SemaphoreTest.java b/src/tests/gov/nasa/jpf/test/java/concurrent/SemaphoreTest.java
new file mode 100644 (file)
index 0000000..6505cd4
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.concurrent;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.concurrent.Semaphore;
+
+import org.junit.Test;
+
+/**
+ * simple test for Java 1.5 java.util.concurrent support
+ */
+public class SemaphoreTest extends TestJPF {
+
+  //--- test methods
+
+  static final int MAX = 1;
+  static final Semaphore avail = new Semaphore(MAX, true);
+  static Resource[] items = new Resource[MAX];
+  static boolean[] isUsed = new boolean[MAX];
+  static final Object lock = new Object();
+
+
+  static {
+    for (int i = 0; i < items.length; i++) {
+      items[i] = new Resource(i);
+    }
+  }
+
+  static class Resource {
+
+    String id;
+    String user;
+
+    Resource(int id) {
+      this.id = "Resource-" + id;
+    }
+
+    public void use(String newUser) {
+      assert user == null : "resource " + id + " in use by " + user +
+              ", but attempted to be acquired by: " + newUser;
+      user = newUser;
+    }
+
+    public void release() {
+      user = null;
+    }
+
+    @Override
+       public String toString() {
+      return id;
+    }
+  }
+
+  public static Resource getItem() throws InterruptedException {
+    avail.acquire();
+
+    synchronized (lock) {
+      for (int i = 0; i < MAX; i++) {
+        if (!isUsed[i]) {
+          isUsed[i] = true;
+          return items[i];
+        }
+      }
+    }
+    assert false : "couldn't find unused resource";
+    return null;
+  }
+
+  public static void putItem(Resource o) {
+    synchronized (lock) {
+      for (int i = 0; i < MAX; i++) {
+        if (items[i] == o) {
+          if (isUsed[i]) {
+            isUsed[i] = false;
+            avail.release();
+          }
+          break;
+        }
+      }
+    }
+  }
+
+  static class Client implements Runnable {
+
+    @Override
+       public void run() {
+      String id = Thread.currentThread().getName();
+
+      try {
+        System.out.println(id + " acquiring resource..");
+        Resource r = SemaphoreTest.getItem();
+        System.out.println(id + " got resource: " + r);
+
+        r.use(id);
+        //.. more stuff here
+        r.release();
+
+        System.out.println(id + " releasing resource: " + r);
+        SemaphoreTest.putItem(r);
+        System.out.println(id + " released");
+
+      } catch (InterruptedException ix) {
+        System.out.println("!! INTERRUPTED");
+      }
+    }
+  }
+
+  //--------------- the test cases
+  @Test
+  public void testResourceAcquisition() {
+    if (verifyNoPropertyViolation()) {
+      for (int i = 0; i <= MAX; i++) {
+        Thread t = new Thread(new Client());
+        t.start();
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/io/BufferedInputStreamTest.java b/src/tests/gov/nasa/jpf/test/java/io/BufferedInputStreamTest.java
new file mode 100644 (file)
index 0000000..56ad66a
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.io;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * raw test for java.io.BufferedInputStream
+ */
+public class BufferedInputStreamTest extends TestJPF {
+
+  @Before
+  public void setUp() {
+    System.out.println("setUp() creating test file");
+    createTestFile();
+  }
+
+  @After
+  public void tearDown() {
+    System.out.println("setUp() deleting test file");
+    deleteTestFile();
+  }
+
+  public static void createTestFile() {
+    try {
+      FileOutputStream fo = new FileOutputStream(testFile);
+      fo.write(TEST_DATA);
+      fo.close();
+    } catch (Throwable t) {
+      throw new RuntimeException("failed to create test file", t);
+    }
+  }
+
+  public static void deleteTestFile() {
+    if (testFile.exists()) {
+      testFile.delete();
+    }
+  }
+
+  //--- the tests
+  static File testFile = new File("__test__");
+  static final byte[] TEST_DATA = {42, 42, 42};
+
+  @Test
+  public void testSimpleRead() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        FileInputStream fis = new FileInputStream(testFile);
+        BufferedInputStream bis = new BufferedInputStream(fis);
+        int n = bis.available();
+
+        assert n == TEST_DATA.length : "wrong available count: " + n;
+
+        for (int i = 0; i < n; i++) {
+          int d = bis.read();
+          System.out.print(d);
+          System.out.print(',');
+          assert d == TEST_DATA[i] : "wrong read data";
+        }
+        System.out.println();
+
+        bis.close();
+
+      } catch (Throwable t) {
+        assert false : "BufferedInputStream test failed: " + t;
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/io/FileIOStreamTest.java b/src/tests/gov/nasa/jpf/test/java/io/FileIOStreamTest.java
new file mode 100644 (file)
index 0000000..25a8be6
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.io;
+
+import org.junit.Test;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileInputStream;
+
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+import java.util.Arrays;
+
+public class FileIOStreamTest extends TestJPF {
+
+    @Test
+    public void testRead() throws Exception{
+        //Verify the methods read and write used by the FileChannel of a FileInput/OutputStream
+        if (verifyNoPropertyViolation()) {
+            File testFile   = new File("_my_test_fileFOSFC_W_FISFC_R.txt");
+            byte[] testData = {42, 42, 42};
+
+            //Preparation of the test file with FileOutputStream
+            FileOutputStream fos  = new FileOutputStream(testFile);
+            FileChannel fos_fc    = fos.getChannel();
+            ByteBuffer buf_write  = ByteBuffer.wrap(testData);
+
+            fos_fc.write(buf_write);
+            fos.close();
+
+            //Now we read what is in the file
+            FileInputStream fis   = new FileInputStream(testFile);
+            FileChannel fis_fc    = fis.getChannel();
+            ByteBuffer buf_read   = ByteBuffer.allocate(testData.length);
+            fis_fc.read(buf_read);
+            fis.close();
+            
+            assert Arrays.toString(testData).equals(Arrays.toString(buf_read.array())) : "Wrong read data";
+
+            deleteTestFile(testFile);
+        }
+    }
+
+    public void deleteTestFile(File fileToClose) {
+        if (fileToClose.exists()) {
+            fileToClose.delete();
+        }   
+    }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/io/FileIOTest.java b/src/tests/gov/nasa/jpf/test/java/io/FileIOTest.java
new file mode 100644 (file)
index 0000000..c36b603
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.io;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStreamWriter;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * raw test for Writers, Readers, FileOutputStream and FileInputStream
+ */
+public class FileIOTest extends TestJPF {
+
+  public static final String fname = "_test_";
+
+  @Test
+  public void testRoundtrip() throws IOException, FileNotFoundException {
+    if (verifyNoPropertyViolation()) {
+      Random r = new Random(42);
+      File file = new File(fname);
+      String[] lines = {"one", "two", "three", "four", "five"};
+
+      //--- write part
+      System.out.println("##---- writing: " + file.getName());
+      FileOutputStream os = new FileOutputStream(file);
+      OutputStreamWriter ow = new OutputStreamWriter(os);
+      PrintWriter pw = new PrintWriter(ow);
+      int a, b;
+
+      for (int i = 0; i < lines.length; i++) {
+        pw.println(lines[i]);
+        if (i == 2) {
+          // add a CG here
+          a = r.nextInt(1);
+          System.out.println("## write got here: " + a);
+        }
+      }
+
+      pw.close();
+      os.close(); // without this, Windows/Cygwin doesn't delete the file
+
+      System.out.println("##---- checking file system attributes");
+
+      assert file.exists() : "File.exits() failed on " + fname;
+
+      assert file.isFile() : "File.isFile() failed on " + fname;
+
+      assert !file.isDirectory() : "!File.isDirectory() failed on " + fname;
+
+      assert isInCurrentDirList(fname) : "dir list test failed on " + fname;
+
+
+      //--- read part
+      System.out.println("##---- reading: " + file.getName());
+      ArrayList<String> contents = new ArrayList<String>();
+      String line;
+      FileInputStream is = new FileInputStream(file);
+      InputStreamReader ir = new InputStreamReader(is);
+      BufferedReader br = new BufferedReader(ir);
+
+      for (int i = 0; (line = br.readLine()) != null; i++) {
+        if (i == 2) {
+          b = r.nextInt(1);
+          System.out.println("## read got here: " + b);
+        }
+        contents.add(line);
+      }
+
+      br.close();
+      is.close(); // without this, Windows/Cygwin doesn't delete the file
+
+      //--- check part
+      System.out.println("##---- comparing");
+      assert lines.length == contents.size() : "file length differs: " + lines.length + " / " + contents.size();
+
+      for (int i = 0; i < lines.length; i++) {
+        assert lines[i].equals(contents.get(i)) : "line " + i + " differs, expected: \"" + lines[i] + "\", got: \"" + contents.get(i) + "\"";
+      }
+
+
+      if (file.delete()) {
+        assert !file.exists() : "File.delete() failed (supposedly deleted but file exists) on " + fname;
+      } else {
+        assert false : "File.delete() failed to delete file (can happen on Windows/Cygwin)";
+      }
+
+      System.out.println("##---- done");
+    }
+  }
+
+  private boolean isInCurrentDirList(String fn) {
+    for (String s : new File(".").list()) {
+      if (fn.equals(s)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/io/FileTest.java b/src/tests/gov/nasa/jpf/test/java/io/FileTest.java
new file mode 100644 (file)
index 0000000..42db2b2
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.io;
+
+import gov.nasa.jpf.util.FileUtils;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ *
+ * @author proger
+ */
+public class FileTest extends TestJPF {
+
+  public FileTest() {
+  }
+
+  @BeforeClass
+  public static void setUpClass() throws Exception {
+    // Creating sandbox for java.io.File testing
+    File subdirs = new File("fileSandbox/parent/child");
+    if (!subdirs.mkdirs())
+      throw new RuntimeException("Unable to create sandbox directories");
+  }
+
+  @AfterClass
+  public static void tearDownClass() throws Exception {
+    if (!FileUtils.removeRecursively(new File("fileSandbox")))
+      throw new RuntimeException("Unable to remove sandbox directories");
+  }
+
+  @Before
+  public void setUp() {
+  }
+
+  @After
+  public void tearDown() {
+  }
+
+  @Test
+  public void testGetParentFile() {
+    if (verifyNoPropertyViolation()) {
+      File file = new File("fileSandbox/parent");
+      File expectedParent = new File("fileSandbox");
+      File resultParent = file.getParentFile();
+      
+      assert expectedParent.equals(resultParent) == true;
+    }
+  }
+
+  @Test
+  public void testGetCanonical() throws IOException {
+    if (verifyNoPropertyViolation()) {
+      File file = new File("fileSandbox/../fileSandbox/../fileSandbox");
+      File root = new File(".");
+
+      File expectedCanonical = new File(root, "fileSandbox").getCanonicalFile();
+      File resultCanonical = file.getCanonicalFile();
+      assert expectedCanonical.equals(resultCanonical) == true;
+
+      String expectedCanonicalName = expectedCanonical.getCanonicalPath();
+      String resultCanonicalName = resultCanonical.getCanonicalPath();
+      assert expectedCanonicalName.equals(resultCanonicalName) == true;      
+    }
+  }
+
+  @Test
+  public void testEquals() {
+    if (verifyNoPropertyViolation()) {
+      File file = new File("fileSandbox");
+      File sameFile = new File("fileSandbox");
+      File otherFile = new File("fileSandbox/parent");
+
+      assert file.equals(file) == true;
+      assert file.equals(sameFile) == true;
+      assert file.equals(otherFile) == false;
+      assert file.equals(null) == false;
+      assert file.equals(new Object()) == false;
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/io/ObjectStreamTest.java b/src/tests/gov/nasa/jpf/test/java/io/ObjectStreamTest.java
new file mode 100644 (file)
index 0000000..a6c4c13
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.io;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+
+import org.junit.Test;
+
+/**
+ * regression test for object streams
+ */
+public class ObjectStreamTest extends TestJPF {
+
+  static class X implements Serializable {
+    String q = "the ultimate question";
+    Y a = new Y(-42);
+
+    @Override
+       public String toString() {
+      return "X{q=\""+q+"\",a="+a+'}';
+    }
+  }
+
+  static class Y implements Serializable {
+    static final long serialVersionUID = -42;
+
+    boolean z = true;
+    byte b = 42;
+    char c = '!';
+    short s = 42;
+    int i;
+    long l = 42000;
+    float f = 42.0f;
+    double d = 4.2e5;
+
+    Y (int answer){
+      i = answer;
+    }
+
+    @Override
+       public String toString() {
+      return "Y{z="+z+",b="+b+",c="+c+",s="+s+",i="+i+",l="+l+",f="+f+",d="+d+ '}';
+    }
+  }
+
+  @Test
+  public void testSimpleReadbackOk () {
+    String fname = "tmp.ser";
+
+    if (!isJPFRun()){
+      try {
+        X x = new X();
+        FileOutputStream fos = new FileOutputStream(fname);
+        ObjectOutputStream oos = new ObjectOutputStream(fos);
+        oos.writeObject(x);
+        oos.close();
+      } catch (Throwable t){
+        fail("serialization failed: " + t);
+      }
+    }
+
+    if (verifyNoPropertyViolation()){
+      try {
+        FileInputStream fis = new FileInputStream(fname);
+        ObjectInputStream ois = new ObjectInputStream(fis);
+        Object o = ois.readObject();
+        ois.close();
+
+        System.out.println(o);
+
+        assert o instanceof X : "wrong object type: " + o.getClass().getName();
+        X x = (X) o;
+        assert x.a.i == -42;
+      } catch (Throwable t){
+        //t.printStackTrace();
+        fail("serialization readback failed: " + t);
+      }
+
+      File f = new File(fname);
+      f.delete();
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/BoxObjectCacheTest.java b/src/tests/gov/nasa/jpf/test/java/lang/BoxObjectCacheTest.java
new file mode 100644 (file)
index 0000000..73bd574
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * regression test for java.lang.Integer
+ */
+public class BoxObjectCacheTest extends TestJPF {
+  private final static String[] JPF_ARGS = { "+vm.cache.low_byte=-100",
+                                             "+vm.cache.high_byte=100",
+                                             "+vm.cache.high_char=100",
+                                             "+vm.cache.low_short=-100",
+                                             "+vm.cache.high_short=100",
+                                             "+vm.cache.low_int=-100",
+                                             "+vm.cache.high_int=100",
+                                             "+vm.cache.low_long=-100",
+                                             "+vm.cache.high_long=100"}; 
+
+  @Test
+  public void testIntCache(){
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Integer i1 = Integer.valueOf( 1);        // should be cached
+      assertTrue( i1.intValue() == 1);
+      
+      Integer i2 = Integer.parseInt("1");
+      assertTrue( i1 == i2);
+      
+      i1 = Integer.valueOf(110); // should be too large for cache
+      assertTrue( i1.intValue() == 110);
+      
+      i2 = Integer.parseInt("110");
+      assertTrue(i1 != i2);
+    }
+  }
+  
+  @Test
+  public void testCharacterCache(){
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Character c1 = Character.valueOf( '?');        // should be cached
+      assertTrue( c1.charValue() == '?');
+      
+      Character c2 = '?'; // compiler does the boxing
+      assertTrue( c1 == c2);
+      
+      c1 = Character.valueOf( '\u2107' ); // epsilon, if I'm not mistaken
+      assertTrue( c1.charValue() == '\u2107');
+      
+      c2 = '\u2107'; // compiler does the boxing
+      assertTrue(c1 != c2);
+    }
+  }
+
+  @Test
+  public void testByteCache(){
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Byte b1 = Byte.valueOf( (byte)1);        // should be cached
+      assertTrue( b1.byteValue() == 1);
+      
+      Byte b2 = Byte.parseByte("1");
+      assertTrue( b1 == b2);
+    }
+  }
+
+  @Test
+  public void testShortCache(){
+   if (verifyNoPropertyViolation(JPF_ARGS)){
+     Short s1 = Short.valueOf((short)1);        // should be cached
+     assertTrue( s1.shortValue() == 1);
+     
+     Short s2 = Short.parseShort("1");
+     assertTrue( s1 == s2);
+      
+     s1 = Short.valueOf((short)110); // should be too large for cache
+     assertTrue( s1.shortValue() == (short)110);
+      
+     s2 = Short.parseShort("110");
+     assertTrue(s1 != s2);
+    }
+  }
+
+  @Test
+  public void testLongCache(){
+   if (verifyNoPropertyViolation(JPF_ARGS)){
+     Long l1 = Long.valueOf(1);        // should be cached
+     assertTrue( l1.longValue() == 1);
+     
+     Long l2 = Long.parseLong("1");
+     assertTrue( l1 == l2);
+      
+     l1 = Long.valueOf(110); // should be too large for cache
+     assertTrue( l1.longValue() == 110);
+      
+     l2 = Long.parseLong("110");
+     assertTrue(l1 != l2);
+    }
+  }  
+
+  @Test
+  public void testBooleanCache(){
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Boolean b1 = Boolean.valueOf(true);        // should be cached
+      assertTrue( b1.booleanValue() == true);
+      
+      Boolean b2 = Boolean.parseBoolean("true");
+      assertTrue( b1 == b2);
+    }
+  }
+
+  @Test
+  public void testIntCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Integer i1 = Integer.valueOf( 1);        // should be cached
+      assertTrue( i1.intValue() == 1);
+      
+      Integer i2 = new Integer(1);
+      Method m = Integer.class.getMethod("intValue", new Class[0]);
+      Integer i3 = (Integer) m.invoke(i2, new Object[0]);
+      assertTrue( i1 == i3);
+    }
+  }
+
+  @Test
+  public void testCharacterCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Character c1 = Character.valueOf( '?');        // should be cached
+      assertTrue( c1.charValue() == '?');
+      
+      Character c2 = new Character('?');
+      Method m = Character.class.getMethod("charValue", new Class[0]);
+      Character c3 = (Character) m.invoke(c2, new Object[0]);
+      assertTrue( c1 == c3);
+    }
+  }
+
+  @Test
+  public void testByteCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Byte b1 = Byte.valueOf( (byte)1);        // should be cached
+      assertTrue( b1.byteValue() == 1);
+      
+      Byte b2 = new Byte((byte)1);
+      Method m = Byte.class.getMethod("byteValue", new Class[0]);
+      Byte b3 = (Byte) m.invoke(b2, new Object[0]);
+      assertTrue( b1 == b3);
+    }
+  }
+
+  @Test
+  public void testShortCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Short s1 = Short.valueOf((short)1);        // should be cached
+      assertTrue( s1.shortValue() == 1);
+      
+      Short s2 = new Short((short)1);
+      Method m = Short.class.getMethod("shortValue", new Class[0]);
+      Short s3 = (Short) m.invoke(s2, new Object[0]);
+      assertTrue( s1 == s3);
+    }
+  }
+
+  @Test
+  public void testLongCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Long l1 = Long.valueOf(1);        // should be cached
+      assertTrue( l1.longValue() == 1);
+      
+      Long l2 = new Long(1);
+      Method m = Long.class.getMethod("longValue", new Class[0]);
+      Long l3 = (Long) m.invoke(l2, new Object[0]);
+      assertTrue( l1 == l3);
+    }
+  }
+
+  @Test
+  public void testBooleanCacheBoxObject() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException{
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Boolean b1 = Boolean.valueOf(true);        // should be cached
+      assertTrue( b1.booleanValue() == true);
+      
+      Boolean b2 = new Boolean(true);
+      Method m = Boolean.class.getMethod("booleanValue", new Class[0]);
+      Boolean b3 = (Boolean) m.invoke(b2, new Object[0]);
+      assertTrue( b1 == b3);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/ClassLoaderTest.java b/src/tests/gov/nasa/jpf/test/java/lang/ClassLoaderTest.java
new file mode 100644 (file)
index 0000000..5e164fc
--- /dev/null
@@ -0,0 +1,185 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Enumeration;
+
+import org.junit.Test;
+
+/**
+ * test of java.lang.ClassLoader API
+ */
+public class ClassLoaderTest extends TestJPF {
+  
+  @Test
+  public void testGetResource() {
+    if(verifyNoPropertyViolation()) {
+      testGetResourceImpl(new TestClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetResources() throws IOException{
+    if(verifyNoPropertyViolation()) {
+      testGetResourcesImpl(new TestClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetResourceAsStream() throws IOException{
+    if(verifyNoPropertyViolation()) {
+      testGetResourceAsStreamImpl(new TestClassLoader());
+    }
+  }
+
+  @Test
+  public void testLoadClass() {
+    if(verifyNoPropertyViolation()) {
+      ClassLoader classLoader = new TestClassLoader();
+      try {
+        classLoader.loadClass("non_existing_class");
+        fail();
+      }catch(ClassNotFoundException e) {}
+    }
+  }
+
+  @Test
+  public void testLoadClass2() {
+    if(verifyNoPropertyViolation()) {
+      ClassLoader classLoader = new TestClassLoader();
+      try {
+        classLoader.loadClass(ClassLoader.class.getName());
+      }catch(ClassNotFoundException e) {
+        fail(e.getMessage());
+      }
+    }
+  }
+
+  @Test
+  public void testGetSystemResource() {
+    if(verifyNoPropertyViolation()) {
+      testGetResourceImpl( ClassLoader.getSystemClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetSystemResources() throws IOException{
+    if(verifyNoPropertyViolation()) {
+      testGetResourcesImpl( ClassLoader.getSystemClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetSystemResourceAsStream() throws IOException{
+    if(verifyNoPropertyViolation()) {
+      testGetResourceAsStreamImpl( ClassLoader.getSystemClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetSystemClassLoader() {
+    if(verifyNoPropertyViolation()) {
+      ClassLoader classLoader = new TestClassLoader();
+      assertNotNull(ClassLoader.getSystemClassLoader());
+      assertNull(ClassLoader.getSystemClassLoader().getParent());
+      assertFalse(classLoader.equals(ClassLoader.getSystemClassLoader()));
+    }
+  }
+
+  @Test
+  public void testGetParent() {
+    if(verifyNoPropertyViolation()) {
+      ClassLoader classLoader = new TestClassLoader();
+      assertNotNull(classLoader.getParent());
+      assertEquals(classLoader.getParent(),ClassLoader.getSystemClassLoader());
+    }
+  }
+
+  @Test
+  public void testGetParent2() {
+    if(verifyNoPropertyViolation()) {
+      ClassLoader parentClassLoader = new TestClassLoader();
+      ClassLoader classLoader = new TestClassLoader(parentClassLoader);
+      assertEquals(parentClassLoader, classLoader.getParent());
+    }
+  }
+
+  @Test
+  public void testFoundResources() throws IOException {
+    if(verifyNoPropertyViolation()) {
+      TestClassLoader classLoader = new TestClassLoader();
+      Enumeration<URL> enm = classLoader.findResources("not_existing_resource"); 
+      assertNotNull(enm);
+      assertFalse(enm.hasMoreElements());
+    }
+  }
+
+  private void testGetResourceImpl(ClassLoader classLoader) {
+    assertNull(classLoader.getResource("not_existing_resource"));
+    assertNotNull(classLoader.getResource("DiningPhil.class"));
+    assertNull(classLoader.getResource("ClassLoader.class"));
+    assertNotNull(classLoader.getResource("java/lang/ClassLoader.class"));
+  }
+
+  private void testGetResourcesImpl(ClassLoader classLoader) throws IOException{
+    assertFalse(classLoader.getResources("not_existing_resources").hasMoreElements());
+
+    Enumeration<?> e = classLoader.getResources("DiningPhil.class");
+    assertTrue(e.hasMoreElements());
+    assertNotNull(e.nextElement());
+    assertFalse(e.hasMoreElements());
+
+    e = classLoader.getResources("ClassLoader.class");
+    assertFalse(e.hasMoreElements());
+
+    // It should find at least two resources: 1. model class, 2. JDK class
+    e = classLoader.getResources("java/lang/ClassLoader.class");
+    assertTrue(e.hasMoreElements());
+    assertNotNull(e.nextElement());
+    assertTrue(e.hasMoreElements());
+    assertNotNull(e.nextElement());
+  }
+
+  private void testGetResourceAsStreamImpl(ClassLoader classLoader) throws IOException{
+    assertNull(classLoader.getResourceAsStream("not_existing_resources"));
+    InputStream is = classLoader.getResourceAsStream("DiningPhil.class");
+    assertNotNull(is);
+    assertTrue(is.read() > 0);
+  }
+
+  class TestClassLoader extends ClassLoader {
+      
+    public TestClassLoader() {
+      super();
+    }
+
+    public TestClassLoader(ClassLoader parent) {
+      super(parent);
+    }
+
+    @Override
+       protected Enumeration<URL> findResources(String name) throws IOException {
+      return super.findResources(name);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/ClassTest.java b/src/tests/gov/nasa/jpf/test/java/lang/ClassTest.java
new file mode 100644 (file)
index 0000000..5d7e1d6
--- /dev/null
@@ -0,0 +1,585 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * This is a raw test class, which produces AssertionErrors for all
+ * cases we want to catch. Make double-sure we don't refer to any
+ * JPF class in here, or we start to check JPF recursively.
+ * To turn this into a Junt test, you have to write a wrapper
+ * TestCase, which just calls the testXX() methods.
+ * The Junit test cases run JPF.main explicitly by means of specifying
+ * which test case to run, but be aware of this requiring proper
+ * state clean up in JPF !
+ *
+ * KEEP IT SIMPLE - it's already bad enough we have to mimic unit tests
+ * by means of system tests (use whole JPF to check if it works), we don't
+ * want to make the observer problem worse by means of enlarging the scope
+ * JPF has to look at
+ *
+ * Note that we don't use assert expressions, because those would already
+ * depend on working java.lang.Class APIs
+ */
+package gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.io.Serializable;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+/**
+ * test of java.lang.Class API
+ */
+public class ClassTest extends TestJPF implements Cloneable, Serializable {
+  
+  /**************************** tests **********************************/
+  static String clsName = ClassTest.class.getName();
+
+  int data = 42; // that creates a default ctor for our newInstance test
+
+
+  @Test 
+  public void testClassForName () throws ClassNotFoundException {
+    if (verifyNoPropertyViolation()) {
+
+      Class<?> clazz = Class.forName(clsName);
+      System.out.println("loaded " + clazz.getName());
+
+      if (clazz == null) {
+        throw new RuntimeException("Class.forName() returned null object");
+      }
+
+      if (!clsName.equals(clazz.getName())) {
+        throw new RuntimeException(
+                "getName() wrong for Class.forName() acquired class");
+      }
+    }
+  }
+  
+  @Test
+  public void testClassForNameException () throws ClassNotFoundException {
+    if (verifyUnhandledException("java.lang.ClassNotFoundException")) {
+      Class<?> clazz = Class.forName("x.y.NonExisting");
+    }
+  }
+
+  
+  static class X {
+    static {
+      System.out.println("ClassTest$X initialized");
+      Verify.incrementCounter(0);
+    }
+  }
+  
+  @Test 
+  public void testClassForNameInit () throws ClassNotFoundException {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation()) {
+      Class<?> cls = Class.forName( "gov.nasa.jpf.test.java.lang.ClassTest$X", true,  this.getClass().getClassLoader());
+      System.out.println("Class.forName() returned");
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 1);
+    }
+  }
+  
+  
+  @Test 
+  public void testGetClass () {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz = this.getClass();
+
+      if (clazz == null) {
+        throw new RuntimeException("Object.getClass() failed");
+      }
+
+      if (!clsName.equals(clazz.getName())) {
+        throw new RuntimeException(
+                "getName() wrong for getClass() acquired class");
+      }
+    }
+  }
+
+  @Test 
+  public void testIdentity () {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz1 = null;
+      Class<?> clazz2 = ClassTest.class;
+      Class<?> clazz3 = this.getClass();
+
+      try {
+        clazz1 = Class.forName(clsName);
+      } catch (Throwable x) {
+        x = null;  // Get rid of IDE warning
+      }
+
+      if (clazz1 != clazz2) {
+        throw new RuntimeException(
+                "Class.forName() and class field not identical");
+      }
+
+      if (clazz2 != clazz3) {
+        throw new RuntimeException(
+                "Object.getClass() and class field not identical");
+      }
+    }
+  }
+  
+  @Test 
+  public void testNewInstance () throws InstantiationException, IllegalAccessException {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz = ClassTest.class;
+      ClassTest o = (ClassTest) clazz.newInstance();
+      
+      System.out.println("new instance: " + o);
+      
+      if (o.data != 42) {
+        throw new RuntimeException(
+          "Class.newInstance() failed to call default ctor");        
+      }
+    }
+  }
+  
+  static class InAccessible {
+    private InAccessible() {}
+  }
+  
+  @Test 
+  public void testNewInstanceFailAccess () throws IllegalAccessException, InstantiationException {
+    if (verifyUnhandledException("java.lang.IllegalAccessException")){
+      Class<?> clazz = InAccessible.class;
+      clazz.newInstance();
+    }
+  }
+  
+  static abstract class AbstractClass {
+  }
+    
+  @Test 
+  public void testNewInstanceFailAbstract () throws IllegalAccessException, InstantiationException {
+    if (verifyUnhandledException("java.lang.InstantiationException")){
+      Class<?> clazz = AbstractClass.class;
+      clazz.newInstance();
+    }
+  }
+  
+  
+  @Test 
+  public void testIsAssignableFrom () {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz1 = Integer.class;
+      Class<?> clazz2 = Object.class;
+    
+      assert clazz2.isAssignableFrom(clazz1);
+  
+      assert !clazz1.isAssignableFrom(clazz2); 
+    }
+  }  
+  
+  @Test 
+  public void testInstanceOf () {
+    if (verifyNoPropertyViolation()) {
+      assert this instanceof Cloneable;
+      assert this instanceof TestJPF;
+      assert this instanceof Object;
+
+      if (this instanceof Runnable) {
+        assert false : "negative instanceof test failed";
+      }
+    }
+  }
+  
+  @Test
+  public void testAsSubclass () {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz1 = Float.class;
+    
+      Class<? extends Number> clazz2 = clazz1.asSubclass(Number.class); 
+      assert clazz2 != null;
+      
+      try {
+        clazz1.asSubclass(String.class);
+        assert false : "testAsSubclass() failed to throw ClassCastException";
+      } catch (ClassCastException ccx) {
+        ccx = null;  // Get rid of IDE warning
+      } 
+    }    
+  }
+  
+  @SuppressWarnings("null")
+  @Test 
+  public void testClassField () {
+    if (verifyNoPropertyViolation()) {
+
+      Class<?> clazz = ClassTest.class;
+
+      if (clazz == null) {
+        throw new RuntimeException("class field not set");
+      }
+
+      if (!clsName.equals(clazz.getName())) {
+        throw new RuntimeException("getName() wrong for class field");
+      }
+    }
+  }
+
+  @Test 
+  public void testInterfaces () {
+    if (verifyNoPropertyViolation()) {
+      Class<?>[] ifc = ClassTest.class.getInterfaces();
+      if (ifc.length != 2) {
+        throw new RuntimeException("wrong number of interfaces: " + ifc.length);
+      }
+
+      int n = ifc.length;
+      String[] ifcs = {Cloneable.class.getName(), Serializable.class.getName()};
+      for (int i = 0; i < ifcs.length; i++) {
+        for (int j = 0; j < ifc.length; j++) {
+          if (ifc[j].getName().equals(ifcs[i])) {
+            n--;
+            break;
+          }
+        }
+      }
+
+      if (n != 0) {
+        throw new RuntimeException("wrong interface types: " + ifc[0].getName() + ',' + ifc[1].getName());
+      }
+    }
+  }
+  
+  
+  static class TestClassBase {
+    protected TestClassBase() {}
+    public void foo () {}
+  }
+  
+  interface TestIfc {
+    void boo();                        // 4
+    void foo();
+  }
+  
+  static abstract class TestClass extends TestClassBase implements TestIfc {
+    static {
+      System.out.println("why is TestClass.<clinit>() executed?");
+    }
+    public TestClass() {}
+    public TestClass (int a) {a = 0;}
+    @Override
+       public void foo() {}               // 1
+    void bar() {}                      // 2
+    public static void baz () {}       // 3
+    
+  }
+  
+  @Test
+  public void testMethods() {
+    if (verifyNoPropertyViolation()) {
+
+      Class<?> cls = TestClass.class;
+      Method[] methods = cls.getMethods();
+
+      boolean fooSeen=false, bazSeen=false, booSeen=false;
+
+      for (int i = 0; i < methods.length; i++) {
+        Method m = methods[i];
+        Class<?> declCls = m.getDeclaringClass();
+        String mname = m.getName();
+
+        // we don't care about the Object methods
+        if (declCls == Object.class) {
+          methods[i] = null;
+          continue;
+        }
+
+        // non-publics, <clinit> and <init> are filtered out
+
+        if (declCls == TestClass.class) {
+          if (mname.equals("foo")) {
+            methods[i] = null;
+            fooSeen = true;
+            continue;
+          }
+          if (mname.equals("baz")) {
+            methods[i] = null;
+            bazSeen = true;
+            continue;
+          }
+        }
+
+        // TestClass is abstract and doesn't implement TestIfc.boo()
+        if (declCls == TestIfc.class) {
+          if (mname.equals("boo")) {
+            methods[i] = null;
+            booSeen = true;
+            continue;
+          }
+        }
+      }
+
+      assert fooSeen : "no TestClass.foo() seen";
+      assert bazSeen : "no TestClass.baz() seen";
+      assert booSeen : "no TestIfc.boo() seen";
+
+      for (int i = 0; i < methods.length; i++) {
+        assert (methods[i] == null) : ("unexpected method in getMethods(): " +
+                  methods[i].getDeclaringClass().getName() + " : " + methods[i]);
+      }
+    }
+  }
+
+  private static class NestedClass {}
+
+  @Test 
+  public void testGetEnclosingClassExist() {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clz = NestedClass.class;
+      Class<?> enclosingClass = clz.getEnclosingClass();
+      assert enclosingClass == ClassTest.class;
+    }
+  }
+
+  @Test
+  public void testGetEnclosingClassNotExist() {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clz = this.getClass();
+      Class<?> enclosingClass = clz.getEnclosingClass();
+      assert enclosingClass == null;
+    }
+  }
+  
+  @Retention(RetentionPolicy.RUNTIME)
+  @Inherited
+  public @interface TestAnnotation {
+  }
+
+  @TestAnnotation()
+  public static class ParentAnnotated<E> {
+  }
+
+  public static class ChildAnnotated<E> extends ParentAnnotated {
+  }
+
+  public enum TestEnum{
+    item;
+  }
+
+  @TestAnnotation()
+  public static class TestEnclosedClass {
+    public Object foo;
+
+    public TestEnclosedClass () {
+      class LocalClass {
+      }
+      ;
+      foo = new LocalClass();
+    }
+
+    public static class MemberClass {
+    }
+
+    public Object getLocalClassObj (){
+
+      class LocalClass {
+      }
+      ;
+
+      return new LocalClass();
+    }
+
+    public Object getAnonymousClassObj (){
+      return new Object() {
+      };
+    }
+  }
+
+  @Test
+  public void localClassEnclosingClassTest (){
+    if (verifyNoPropertyViolation()){
+      TestEnclosedClass testObj = new ClassTest.TestEnclosedClass();
+      assertEquals(testObj.foo.getClass().getEnclosingClass(), TestEnclosedClass.class);
+    }
+  }
+
+  @Test
+  public void getCanonicalNameTest (){
+    if (verifyNoPropertyViolation()){
+      assertEquals(ArrayList.class.getCanonicalName(), "java.util.ArrayList");
+      assertEquals(Class.class.getCanonicalName(), "java.lang.Class");
+      assertEquals(String.class.getCanonicalName(), "java.lang.String");
+      assertEquals((new Object[0]).getClass().getCanonicalName(), "java.lang.Object[]");
+    }
+  }
+
+  @Test
+  public void getDeclaredAnnotationsTest (){
+    if (verifyNoPropertyViolation()){
+      assertTrue(ClassTest.ParentAnnotated.class.getDeclaredAnnotations().length == 1);
+      assertTrue(ChildAnnotated.class.getDeclaredAnnotations().length == 0);
+      assertTrue(ClassTest.ParentAnnotated.class.getAnnotations().length == 1);
+      assertTrue(ChildAnnotated.class.getAnnotations().length == 1);
+    }
+  }
+
+  @Test
+  public void getEnclosingConstructor () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      Class cls = (new ClassTest.TestEnclosedClass()).foo.getClass();
+      assertTrue(cls.getEnclosingConstructor().getDeclaringClass() == ClassTest.TestEnclosedClass.class);
+      assertEquals(cls.getEnclosingConstructor().getName(), "<init>");
+      assertNull(cls.getEnclosingMethod());
+    }
+  }
+
+  @Test
+  public void getEnclosingMethod () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      Class cls = (new ClassTest.TestEnclosedClass()).getLocalClassObj().getClass();
+      assertTrue(cls.getEnclosingMethod().getDeclaringClass() == ClassTest.TestEnclosedClass.class);
+      assertNull(cls.getEnclosingConstructor());
+      assertEquals(cls.getEnclosingMethod().getName(), "getLocalClassObj");
+      Method m1 = ClassTest.TestEnclosedClass.class.getMethod("getLocalClassObj", new Class[0]);
+      Method m2 = cls.getEnclosingMethod();
+      assertEquals(m1, m2);
+      assertTrue(cls.getEnclosingMethod().equals(ClassTest.TestEnclosedClass.class.getMethod("getLocalClassObj", new Class[0])));
+    }
+  }
+
+  @Test
+  public void isAnonymousClassTest (){
+    if (verifyNoPropertyViolation()){
+      Class cls = (new ClassTest.TestEnclosedClass()).getAnonymousClassObj().getClass();
+      assertTrue(cls.isAnonymousClass());
+      assertFalse(Class.class.isAnonymousClass());
+    }
+  }
+
+  @Test
+  public void isEnumTest (){
+    if (verifyNoPropertyViolation()){
+      assertTrue(TestEnum.class.isEnum());
+      assertFalse(Class.class.isEnum());
+    }
+  }
+
+  @Test
+  public void getDeclaringClassTest (){
+    if (verifyNoPropertyViolation()){
+      assertTrue(TestEnclosedClass.class.getDeclaringClass() == ClassTest.class);
+      assertNull(Class.class.getDeclaringClass());
+      Class anonymousCls = (new ClassTest.TestEnclosedClass()).getAnonymousClassObj().getClass();
+      assertNull(anonymousCls.getDeclaringClass());
+      Class localCls = (new ClassTest.TestEnclosedClass()).foo.getClass();
+      assertNull(localCls.getDeclaringClass());
+    }
+  }
+
+  @Test
+  public void isLocalClassTest (){
+    if (verifyNoPropertyViolation()){
+      TestEnclosedClass testObj = new ClassTest.TestEnclosedClass();
+      assertTrue(testObj.foo.getClass().isLocalClass());
+      assertTrue(testObj.getLocalClassObj().getClass().isLocalClass());
+      assertFalse(Class.class.isLocalClass());
+    }
+  }
+
+  @Test
+  public void isMemberClassTest (){
+    if (verifyNoPropertyViolation()){
+      assertTrue(TestEnclosedClass.MemberClass.class.isMemberClass());
+      assertFalse(Class.class.isMemberClass());
+      assertFalse(((new TestEnclosedClass()).getLocalClassObj().getClass().isMemberClass()));
+    }
+  }
+
+  @Test
+  public void isSyntheticTest (){
+    if (verifyNoPropertyViolation()){
+      assertFalse(Class.class.isSynthetic());
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Inherited
+  public @interface A9 {
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A10 {
+  }
+
+  @A9()
+  public static class Parent {
+  }
+
+  @A10
+  public static class Child1 extends Parent {
+  }
+
+  public static class Child2 extends Child1 {
+  }
+
+  @Test
+  public void getAnnotationsTest (){
+    if (verifyNoPropertyViolation()){
+      assertTrue(Parent.class.getAnnotations().length == 1);
+      assertTrue(Child1.class.getAnnotations().length == 2);
+      assertTrue(Child2.class.getAnnotations().length == 1);
+    }
+  }
+  
+  @Test
+  public void testIsAnnotation(){
+    if (verifyNoPropertyViolation()){
+      assertFalse( Child2.class.isAnnotation());
+      assertTrue( A9.class.isAnnotation());
+    }
+  }
+  
+  @Test
+  public void testIsAnnotationPresent(){
+    if (verifyNoPropertyViolation()){
+      assertFalse( Child2.class.isAnnotationPresent(SuppressWarnings.class));
+      assertTrue( Child1.class.isAnnotationPresent(A10.class));
+      assertTrue( Child1.class.isAnnotationPresent(A9.class));
+      assertTrue( Child2.class.isAnnotationPresent(A9.class));
+    }    
+  }
+
+  @Test
+  public void getResourceTest() {
+    if (verifyNoPropertyViolation()){
+      Class c = ClassLoader.class;
+      assertNotNull(c.getResource("Class.class"));
+      assertNotNull(c.getResource("/java/lang/Class.class"));
+      assertNull(c.getResource("java/lang/Class.class"));
+      assertEquals(c.getResource("Class.class"),c.getResource("/java/lang/Class.class"));
+      assertNull(c.getResource("not_existing_resources"));
+    }
+  }  
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/CloneTest.java b/src/tests/gov/nasa/jpf/test/java/lang/CloneTest.java
new file mode 100644 (file)
index 0000000..bf23101
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * clone related tests
+ */
+public class CloneTest extends TestJPF {
+
+  @Test
+  public void testCloneArray() {
+    if (verifyNoPropertyViolation()){
+      int[] a = new int[3];
+      int[] b = a.clone();
+      for (int i = 0; i < b.length; i++) {
+        b[i]++;
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/RuntimeTest.java b/src/tests/gov/nasa/jpf/test/java/lang/RuntimeTest.java
new file mode 100644 (file)
index 0000000..e6e3d96
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class RuntimeTest extends TestJPF {
+
+  @Test
+  public void testAvailableProcessors() {
+
+    if (!isJPFRun()) {
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+cg.max_processors=2")) {
+      Runtime rt = Runtime.getRuntime();
+
+      int n = rt.availableProcessors();
+      System.out.println("-- available processors: " + n);
+      Verify.incrementCounter(0);
+    }
+
+    if (!isJPFRun()) {
+      if (Verify.getCounter(0) != 2) {
+        fail("wrong number of backtracks: " + Verify.getCounter(0));
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/StringTest.java b/src/tests/gov/nasa/jpf/test/java/lang/StringTest.java
new file mode 100644 (file)
index 0000000..efbe6d7
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.io.UnsupportedEncodingException;
+import java.nio.charset.Charset;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.TreeSet;
+
+import org.junit.Test;
+
+/**
+ * test of java.lang.String APIs
+ */
+public class StringTest extends TestJPF {
+
+
+       @Test
+       public void testIntern() {
+               if (verifyNoPropertyViolation()) {
+                       boolean c1 = Verify.getBoolean(); // to do some state storing / backtracking
+                       String a = "Blah".intern();
+                       String b = new String("Blah");
+
+                       assert (a != b) : "'new String(intern) != intern' failed";
+
+                       boolean c2 = Verify.getBoolean(); // to do some more storing / backtracking
+
+                       String c = b.intern();
+
+                       assert (a == c) : "'(new String(intern)).intern() == intern' failed";
+               }
+       }
+
+       @Test
+       public void testToCharArray() {
+               if (verifyNoPropertyViolation()) {
+                       String s = "42";
+                       char[] c = s.toCharArray();
+
+                       assert c.length == 2;
+                       assert c[0] == '4';
+                       assert c[1] == '2';
+               }
+       }
+
+       @Test
+       public void testEquals() {
+               if (verifyNoPropertyViolation()) {
+                       String a = "one two";
+                       String b = "one" + " two";
+                       String c = "one three";
+
+                       assert a.equals(b);
+                       assert !a.equals(c);
+               }
+       }
+
+       @Test
+       public void testIndexOf() {
+               if (verifyNoPropertyViolation()) {
+                       String a = "bla.bla";
+                       int i1 = a.indexOf('.');
+                       int i2 = a.indexOf('@');
+
+                       assert i1 == 3;
+                       assert i2 == -1;
+               }
+       }
+
+       @Test
+       public void testCompareTo() {
+               if (verifyNoPropertyViolation()) {
+                       String a = "aaa";
+                       String b = "bbb";
+
+                       assert a.compareTo(b) < 0;
+                       assert b.compareTo(a) > 0;
+                       assert a.compareTo(a) == 0;
+
+                       String longer = "aaaaaa";
+
+                       assert a.compareTo(longer) < 0;
+               }
+       }
+
+       @Test
+       public void testStartsWith() {
+               if (verifyNoPropertyViolation()) {
+                       String str = "TestString";
+
+                       assert str.startsWith("Test") == true;
+                       assert str.startsWith("String", 4) == true;
+                       assert str.startsWith("StringString", 4) == false;
+                       assert str.startsWith("StrUng", 4) == false;
+                       assert str.startsWith("Test", -5) == false;
+               }
+       }
+
+       @Test
+       public void testEndsWith() {
+               if (verifyNoPropertyViolation()) {
+                       String str = "TestString";
+
+                       assert str.endsWith("String") == true;
+                       assert str.endsWith("") == true;
+                       assert str.endsWith("StrUng") == false;
+               }
+       }
+
+       @Test
+       public void testTrim() {
+               if (verifyNoPropertyViolation()) {
+                       assert "   Test    ".trim().equals("Test");
+                       assert "   Test".trim().equals("Test");
+                       assert "Test    ".trim().equals("Test");
+                       assert "Test".trim().equals("Test");
+                       assert "       ".trim().equals("");
+
+               }
+       }
+
+       @Test
+       public void testReplace() {
+               if (verifyNoPropertyViolation()) {
+                       assert "hello".replace('l', 'a').equals("heaao") == true;
+                       assert "".replace('l', 'a').equals("") == true;
+                       assert "hello".replace('f', 'a').equals("hello") == true;
+                       assert "eve".replace('e', 'a').equals("ava") == true;
+               }
+       }
+
+       @Test
+       public void testNullChar(){
+               if (verifyNoPropertyViolation()){
+                       String s = "\u0000";
+                       assertTrue( s.length() == 1);
+                       char c = s.charAt(0);
+                       assertTrue( Character.isISOControl(c));
+               }
+       }
+
+       @Test
+  @SuppressWarnings("deprecation")
+  public void testConstructors(){
+               if (verifyNoPropertyViolation()){
+                       String s=new String();
+                       new String(s);
+                       assertTrue("empty test",s.isEmpty());
+                       char[]value=new char[]{'a','b','c','d','e'};
+                       assertTrue("String([]abcde=abcde","abcde".equals(new String(value)));
+                       assertTrue("String([]abcde,2,3)=cde","cde".equals(new String(value,2,3)));
+                       int[]codePoints=new int[]{48,49,50,51,52,53,54,55,56,57};
+                       assertTrue("codePoints0,4=0123","0123".equals(new String(codePoints,0,4)));
+                       byte[]bytes=new byte[]{65,66,67,68};
+                       byte[]data=new byte[]{69,70,71,72};
+                       byte[]more=new byte[]{73,74,75,76};
+                       byte[]yow=new byte[]{77,78,79};
+                       assertTrue("bytes0,1,3=BCD","BCD".equals(new String(bytes,0,1,3)));
+                       assertTrue("bytes=ABCD","ABCD".equals(new String(bytes,0)));
+                       try {
+                               Charset d = Charset.forName("ISO-8859-1");
+                               String dname=d.name();
+                               assertTrue("bytes1,2,ISO=BC","BC".equals(new String(bytes,1,2,dname)));
+                               assertTrue("bytes2,2,ISO=CD","CD".equals(new String(bytes,2,2,d)));
+
+                               assertTrue("data,ISO=EFGH","EFGH".equals(new String(data,dname)));
+                               assertTrue("more,ISO=IJKL","IJKL".equals(new String(more,d)));
+                       } catch (UnsupportedEncodingException e) {
+                               // TODO Auto-generated catch block
+                               fail("default encoding failure");
+                       }
+                       assertTrue("more1,3=JKL","JKL".equals(new String(more,1,3)));
+                       assertTrue("yow=MNO","MNO".equals(new String(yow)));
+                       StringBuffer buf=new StringBuffer();
+                       buf.append("yogi");
+                       assertTrue("buf=yogi","yogi".equals(new String(buf)));
+                       StringBuilder build=new StringBuilder();
+                       build.append("boo-boo");
+                       assertTrue("build=boo-boo","boo-boo".equals(new String(build)));
+               }
+       }
+  
+  
+       // Test new modelled methods
+       @Test
+  @SuppressWarnings("deprecation")
+  public void testMethods() {
+    if (verifyNoPropertyViolation()) {
+      // 97 ...
+      String x = new String("abcdefg");
+      assertTrue("abcdefg[3]=100", x.codePointAt(3) == 100);
+      assertTrue("abcdefg[2]=99", x.codePointBefore(3) == 99);
+      assertTrue("abcdefg(2,4)", x.codePointCount(2, 4) == 2);
+      assertTrue("abcdefg(2+2)", x.offsetByCodePoints(2, 2) == 4);
+      char[] dst = new char[]{0, 0, 0, 0, 0};
+      x.getChars(4, 7, dst, 1);
+      assertTrue("abcdefg(4,7->1)=0efg0", dst[0] == 0);
+      assertTrue("abcdefg(4,7->1)=0efg0", dst[1] == 'e');
+      assertTrue("abcdefg(4,7->1)=0efg0", dst[2] == 'f');
+      assertTrue("abcdefg(4,7->1)=0efg0", dst[3] == 'g');
+      assertTrue("abcdefg(4,7->1)=0efg0", dst[4] == 0);
+      byte[] bdst = new byte[]{0, 0, 0, 0, 0};
+      x.getBytes(3, 6, bdst, 1);
+      assertTrue("abcdefg(4,7->1)=0efg0", bdst[0] == 0);
+      assertTrue("abcdefg(4,7->1)=0efg0", bdst[1] == 'd');
+      assertTrue("abcdefg(4,7->1)=0efg0", bdst[2] == 'e');
+      assertTrue("abcdefg(4,7->1)=0efg0", bdst[3] == 'f');
+      assertTrue("abcdefg(4,7->1)=0efg0", bdst[4] == 0);
+      String y = new String("qrs");
+      try {
+        bdst = y.getBytes("ISO-8859-1");
+      } catch (UnsupportedEncodingException e) {
+        fail("getBytes failed: " + e);
+      }
+      assertTrue("qrs 0", bdst[0] == 'q');
+      assertTrue("qrs 1", bdst[1] == 'r');
+      assertTrue("qrs 2", bdst[2] == 's');
+      Charset charSet = Charset.forName("ISO-8859-1");
+      String z = new String("tuv");
+      bdst = z.getBytes(charSet);
+      assertTrue("tuv 0", bdst[0] == 't');
+      assertTrue("tuv 1", bdst[1] == 'u');
+      assertTrue("tuv 2", bdst[2] == 'v');
+      String s = new String("wxy");
+      bdst = s.getBytes();
+      assertTrue("wxy 0", bdst[0] == 'w');
+      assertTrue("wxy 1", bdst[1] == 'x');
+      assertTrue("wxy 2", bdst[2] == 'y');
+      StringBuffer buf = new StringBuffer();
+      buf.append("xyz");
+      assertTrue("xyz".contentEquals(buf));
+      assertTrue("a<b", "a".compareTo("b") < 0);
+      TreeSet<String> set = new TreeSet<String>();
+      set.add("b");
+      set.add("a");
+      Iterator<String> iter = set.iterator();
+      String a = iter.next();
+      assertTrue("set0=a", a.equals("a"));
+      String b = iter.next();
+      assertTrue("set1=b", b.equals("b"));
+      assertTrue("AaBb=aabb", "AaBb".compareToIgnoreCase("aabb") == 0);
+      assertTrue("abcdef1,3-->bcdef", "abcde".regionMatches(2, "bcdef", 1, 3));
+      assertTrue("abcdef1,3-->BCDEF", "abcde".regionMatches(true, 2, "BCDEF", 1, 3));
+      assertTrue("xyz->yz", "xyz".startsWith("yz", 1));
+      assertTrue("abc->a", "abc".startsWith("a"));
+      assertTrue("xyz->yz", "xyz".endsWith("yz"));
+      buf.delete(0, buf.length());
+      buf.append("hash");
+      assertTrue("hashCode", "hash".hashCode() == buf.toString().hashCode());
+      assertTrue("abc->1", "abc".indexOf('b') == 1);
+      assertTrue("ababa->3", "ababa".indexOf('b', 2) == 3);
+      assertTrue("ababa(z)->-1", "ababa".lastIndexOf('z') == -1);
+      assertTrue("aacdabcd(a,3)", "aacdabcd".lastIndexOf('a', 3) == 1);
+      assertTrue("abcabca->bca so 1", "abcabca".indexOf("bca") == 1);
+      assertTrue("abcabca->bca 2, so 4", "abcabca".indexOf("bca", 2) == 4);
+      assertTrue("lovelovelove->8", "lovelovelove".lastIndexOf("love") == 8);
+      assertTrue("crazycrazy->0", "crazycrazy".lastIndexOf("crazy", 4) == 0);
+      assertTrue("yellowblue->6", "yellowblue".substring(6).equals("blue"));
+      assertTrue("yellowbluebluegreen->6,14", "yellowbluebluegreen".substring(6, 14).equals("blueblue"));
+      assertTrue("x+y", "x".concat("y").equals("xy"));
+      assertTrue("xyz(xy)", "xyz".contains("xy"));
+      assertTrue("abcabc->abaaba", "abcabc".replace("c", "a").equals("abaaba"));
+      Locale l = Locale.ENGLISH;
+      String result = String.format(l, "Gimme the %s and save my soul", "FORTRAN");
+      assertTrue("fortran", result.equals("Gimme the FORTRAN and save my soul"));
+      assertTrue("1", String.valueOf(1).equals("1"));
+      assertTrue("1111111111111111111L", String.valueOf(1111111111111111111L).equals("1111111111111111111"));
+      assertTrue("3.14159", String.valueOf(3.14159).equals("3.14159"));
+      System.out.println(String.valueOf(3.14159265358979324D));
+      assertTrue("3.141592653589793", String.valueOf(3.141592653589793D).equals("3.141592653589793"));
+    }
+  }
+  
+  @Test
+  public void testContentEquals(){
+    if (verifyNoPropertyViolation()){
+      String s = "fortyTwo";
+      StringBuilder sb = new StringBuilder();
+      sb.append("fortyTwo");
+            
+      assertTrue( s.contentEquals(sb));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/SystemTest.java b/src/tests/gov/nasa/jpf/test/java/lang/SystemTest.java
new file mode 100644 (file)
index 0000000..836a169
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * raw test for java.lang.System functionality
+ */
+public class SystemTest extends TestJPF {
+
+  final Object lock = new Object(); // need a shared object
+  boolean exitCalled;
+
+  @Test
+  public void testExit() {
+    if (verifyNoPropertyViolation()) {
+      Thread t = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          while (true) {
+            assert !exitCalled : "thread not stopped by System.exit()";
+
+            synchronized (lock) {
+              try {
+                lock.wait();
+              } catch (InterruptedException x) {
+                System.out.println("wait interrupted");
+              }
+            }
+          }
+        }
+      });
+
+      t.start();
+
+      synchronized (lock) {
+        exitCalled = true;
+        System.out.println("calling System.exit(0)");
+        System.exit(0);
+      }
+
+      assert false : "main not stopped by System.exit()";
+    }
+  }
+
+  /**
+   * just needs a gazillion more cases (different sizes, slices, overruns,
+   * incompatible types etc.)
+   */
+  @Test
+  public void testSimpleArrayCopy() {
+    if (verifyNoPropertyViolation()) {
+      int[] a = {0, 1, 2, 3, 4, 5, 6, 7};
+      int[] b = new int[a.length];
+
+      System.arraycopy(a, 0, b, 0, a.length);
+
+      for (int i = 0; i < a.length; i++) {
+        assert b[i] == i;
+      }
+    }
+  }
+
+  @Test
+  public void testSelfArrayCopy(){
+    if (verifyNoPropertyViolation()){
+      int[] a = {0, 1, 2, 3, 4, 5, 6, 7};
+
+      System.arraycopy(a, 3, a, 0, 5);
+
+      // the overwritten ones
+      assertTrue(a[0] == 3);
+      assertTrue(a[1] == 4);
+      assertTrue(a[2] == 5);
+      assertTrue(a[3] == 6);
+      assertTrue(a[4] == 7);
+
+      // the old ones
+      assertTrue(a[5] == 5);
+      assertTrue(a[6] == 6);
+      assertTrue(a[7] == 7);
+    }
+  }
+
+  @Test
+  public void testOverlappingSelfArrayCopy(){
+    if (verifyNoPropertyViolation()){
+      int[] a = {0, 1, 2, 3, 4, 5, 6, 7};
+
+      System.arraycopy(a, 0, a, 2, 3);
+
+      // copying should proceed as if using a temporary destination
+      assertTrue(a[0] == 0);
+      assertTrue(a[1] == 1);
+      assertTrue(a[2] == 0);
+      assertTrue(a[3] == 1);
+      assertTrue(a[4] == 2);
+      assertTrue(a[5] == 5);
+      assertTrue(a[6] == 6);
+      assertTrue(a[7] == 7);
+    }
+  }
+
+  @Test
+  public void testIncompatibleReferencesArrayCopy(){
+    if (verifyUnhandledException("java.lang.ArrayStoreException")){
+      String[] dst = new String[2];
+      Object[] src = { "one", new Integer(2) };
+
+      System.arraycopy(src,0,dst,0,src.length);
+    }
+  }
+
+  @Test
+  public void testRestoredArrayCopy(){
+    if (verifyNoPropertyViolation()){
+      Object[] src = { "one", "two" };
+      Object[] dst = new Object[2];
+
+      int n = Verify.getInt(0, 1);
+      System.out.println("processing choice: " + n);
+
+      if (n == 0){
+        System.out.println("copying array");
+        System.arraycopy(src,0,dst,0,src.length);
+      } else if (n == 1){
+        System.out.println("checking if non-copied dst[0] is still null");
+        assertTrue( dst[0] == null);
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/ref/WeakReferenceTest.java b/src/tests/gov/nasa/jpf/test/java/lang/ref/WeakReferenceTest.java
new file mode 100644 (file)
index 0000000..b8f5fda
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang.ref;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.lang.ref.WeakReference;
+
+import org.junit.Test;
+
+public class WeakReferenceTest extends TestJPF
+{
+   @Test
+   public void testGCClearsRef()
+   {
+      WeakReference<Target> ref;
+
+      if (verifyNoPropertyViolation())
+      {
+         ref = new WeakReference<Target>(new Target());
+
+         forceGC();
+         
+         assertNull(ref.get());
+      }
+   }
+
+   @Test
+   public void testStrongReferenceKeepsWeakReference()
+   {
+      WeakReference<Target> ref;
+      Target target;
+
+      if (verifyNoPropertyViolation())
+      {
+         target = new Target();
+         ref    = new WeakReference<Target>(target);
+
+         forceGC();
+
+         assertSame(target, ref.get());
+      }
+   }
+
+   /* ClassInfo.refClassInfo wasn't being set to null between JPF runs.  Thus, 
+    * refClassInfo wasn't being updated.  Hence, the WeakReference below would 
+    * be treated as a normal object in GC.  Re-run testGCClearsRef() to 
+    * reproduce the issue.
+    */
+   @Test
+   public void testClearClassInfoRefClassInfo()
+   {
+      testGCClearsRef();
+   }
+   
+   private static void forceGC()
+   {
+      System.gc();         // Mark that GC is needed
+      Verify.breakTransition("testForceGC"); // Cause a state to be captured and hence GC to run
+   }
+   
+   private static class Target   // Make this object easy to find in JPF heap
+   {
+   }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/reflect/ConstructorTest.java b/src/tests/gov/nasa/jpf/test/java/lang/reflect/ConstructorTest.java
new file mode 100644 (file)
index 0000000..21db267
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang.reflect;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.lang.reflect.Constructor;
+import org.junit.Test;
+
+public class ConstructorTest extends TestJPF {
+
+  public static void main (String[] args){
+    runTestsOfThisClass(args);
+  }
+
+  public static class Test1 {
+    public Test1 (Class<?>... argTypes) {
+    }
+
+    public Test1 (Object[] argTypes) {
+    }
+  }
+
+  @Test
+  public void equalsTest () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      Constructor ctor1 = String.class.getConstructor();
+      Constructor ctor2 = String.class.getConstructor();
+      assertTrue(ctor1.equals(ctor2));
+      assertFalse(ctor1 == ctor2);
+    }
+  }
+
+  @Test
+  public void isVarArgsTest () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      assertTrue(Test1.class.getConstructors()[0].isVarArgs());
+      assertFalse(Test1.class.getConstructors()[1].isVarArgs());
+    }
+  }
+
+  @Test
+  public void hashCodeTest (){
+    if (verifyNoPropertyViolation()){
+      Constructor ctor1 = Test1.class.getConstructors()[0];
+      Constructor ctor2 = Test1.class.getConstructors()[1];
+      assertTrue(ctor1.hashCode() == ctor2.hashCode());
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/reflect/FieldTest.java b/src/tests/gov/nasa/jpf/test/java/lang/reflect/FieldTest.java
new file mode 100644 (file)
index 0000000..ed29c71
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang.reflect;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
+public class FieldTest extends TestJPF {
+
+  public static final int testField1 = 1;
+
+  public String testField2;
+
+  public static void main (String[] args) throws SecurityException, NoSuchFieldException{
+    runTestsOfThisClass(args);
+  }
+
+  @Test
+  public void equalsTest () throws SecurityException, NoSuchFieldException{
+    if (verifyNoPropertyViolation()){
+      Field f1 = FieldTest.class.getField("testField1");
+      Field f2 = FieldTest.class.getField("testField1");
+      Field f3 = FieldTest.class.getField("testField2");
+      Field f4 = FieldTest.class.getField("testField2");
+      assertTrue(f1.equals(f2));
+      assertTrue(f3.equals(f4));
+      assertFalse(f1.equals(f3));
+    }
+  }
+
+  @Test
+  public void toStringTest () throws SecurityException, NoSuchFieldException{
+    if (verifyNoPropertyViolation()){
+      Field f1 = FieldTest.class.getField("testField1");
+      System.out.println(f1);
+      assertEquals(f1.toString(), "public static final int gov.nasa.jpf.test.java.lang.reflect.FieldTest.testField1");
+      Field f2 = FieldTest.class.getField("testField2");
+      assertEquals(f2.toString(), "public java.lang.String gov.nasa.jpf.test.java.lang.reflect.FieldTest.testField2");
+    }
+  }
+
+  enum TestEnum{
+    f1, f2, f3
+  }
+
+  @Test
+  public void isEnumConstantTest (){
+    if (verifyNoPropertyViolation()){
+      for (Field f : TestEnum.class.getFields())
+        assertTrue(f.isEnumConstant());
+    }
+  }
+
+  @Test
+  public void hashCodeTest () throws SecurityException, NoSuchFieldException{
+    if (verifyNoPropertyViolation()){
+      Field f1 = FieldTest.class.getField("testField1");
+      Field f2 = FieldTest.class.getField("testField1");
+      Field f3 = FieldTest.class.getField("testField2");
+      assertTrue(f1.hashCode() == f2.hashCode());
+      assertFalse(f1.hashCode() == f3.hashCode());
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Inherited
+  public @interface A1 {
+  }
+
+  public class Super {
+    @A1
+    public int f;
+  }
+
+  public class Sub {
+    public int f;
+  }
+
+  @Test
+  public void getDeclaredAnnotationsTest () throws SecurityException, NoSuchFieldException{
+    if (verifyNoPropertyViolation()){
+      Field f1 = Sub.class.getField("f");
+      Field f2 = Super.class.getField("f");
+      assertEquals(f1.getDeclaredAnnotations().length, 0);
+      assertEquals(f2.getDeclaredAnnotations().length, 1);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/lang/reflect/MethodTest.java b/src/tests/gov/nasa/jpf/test/java/lang/reflect/MethodTest.java
new file mode 100644 (file)
index 0000000..692b60d
--- /dev/null
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.lang.reflect;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.lang.reflect.Method;
+import org.junit.Test;
+
+public class MethodTest extends TestJPF {
+
+  public static void main (String[] args){
+    runTestsOfThisClass(args);
+  }
+
+  @Test
+  public void equalsTest () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      Method m1 = MethodTest.class.getMethod("equalsTest", new Class[0]);
+      Method m2 = MethodTest.class.getMethod("equalsTest", new Class[0]);
+      assertTrue(m1.equals(m2));
+      assertFalse(m1 == m2);
+    }
+  }
+
+  public void testIsVarArg1s (Class<?>... argTypes){
+  }
+
+  public void testIsVarArgs2 (Class<?>[] argTypes){
+  }
+
+  @Test
+  public void isVarArgsTest () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      for (Method m : MethodTest.class.getDeclaredMethods()){
+        if (m.getName().equals("testIsVarArg1s"))
+          assertTrue(m.isVarArgs());
+        else if (m.getName().equals("testIsVarArg1s")) {
+          assertFalse(m.isVarArgs());
+        }
+      }
+    }
+  }
+
+  @Test
+  public void hashCodeTest () throws SecurityException, NoSuchMethodException{
+    if (verifyNoPropertyViolation()){
+      Method m1 = MethodTest.class.getMethod("hashCodeTest", new Class[0]);
+      Method m2 = MethodTest.class.getMethod("hashCodeTest", new Class[0]);
+      Method m3 = MethodTest.class.getMethod("equalsTest", new Class[0]);
+      assertTrue(m1.equals(m2));
+      assertTrue(m1.hashCode() == m2.hashCode());
+      assertFalse(m1.hashCode() == m3.hashCode());
+    }
+  }
+
+  public static class A {
+    public A foo (int a){
+      return new A();
+    }
+  }
+
+  public static class B extends A {
+    @Override
+       public B foo (int x){
+      return new B();
+    }
+  }
+
+  @Test
+  public void isBridgeTest (){
+    if (verifyNoPropertyViolation()){
+      assertFalse(B.class.getDeclaredMethods()[0].isBridge());
+      assertTrue(B.class.getDeclaredMethods()[1].isBridge());
+    }
+  }
+  
+  //--- aux
+  
+  void recordSeen (boolean[] seen, String[] expected, Method m){
+    String mname = m.toString();
+    for (int i=0; i<expected.length; i++){
+      if (expected[i].equals(mname)){
+        seen[i] = true;
+      }
+    }
+  }
+  
+  boolean checkSeen(boolean[] seen, String[] expected){
+    for (int i=0; i<expected.length; i++){
+      if (!seen[i]){
+        System.out.println("NOT seen: " + expected[i]);
+        return false;
+      }
+    }
+    return true;
+  }
+  
+  //------------ getMethods() on interfaces
+  interface I1 {
+    void i1();
+  }
+  
+  interface I2 extends I1 {
+    void i2();
+  }
+  
+  @Test
+  public void testGetMethodsOnIfc(){
+    if (verifyNoPropertyViolation()){
+      String[] expected = {
+        "public abstract void gov.nasa.jpf.test.java.lang.reflect.MethodTest$I2.i2()",
+        "public abstract void gov.nasa.jpf.test.java.lang.reflect.MethodTest$I1.i1()"
+      };
+      boolean[] seen = new boolean[expected.length];
+      
+      Method[] methods = I2.class.getMethods();
+      
+      for (Method m : methods){
+        System.out.println(m);
+        recordSeen(seen, expected, m);
+      }
+      assertTrue(methods.length == expected.length);
+      //assertTrue(checkSeen(seen, expected));
+    }
+  }
+  
+  
+  //------------ getMethods() on classes
+  public static class C extends B {
+    static {
+      System.out.println("C.<clinit>");
+    }
+    
+    // non-public method
+    void nope(){
+    }
+    
+    // ctor
+    C (){
+      System.out.println("C.<init>");      
+    }
+  } 
+  
+  @Test
+  public void testGetMethodsOnClass(){
+    if (verifyNoPropertyViolation()){
+      String[] expected = {
+        "public native int java.lang.Object.hashCode()",
+        "public final native void java.lang.Object.notify()",
+        "public final native void java.lang.Object.notifyAll()",
+        "public java.lang.String java.lang.Object.toString()",
+        "public final native java.lang.Class java.lang.Object.getClass()",
+        "public final native void java.lang.Object.wait(long)",
+        "public final void java.lang.Object.wait(long,int)",
+        "public gov.nasa.jpf.test.java.lang.reflect.MethodTest$B gov.nasa.jpf.test.java.lang.reflect.MethodTest$B.foo(int)",
+        "public boolean java.lang.Object.equals(java.lang.Object)",
+        "public volatile gov.nasa.jpf.test.java.lang.reflect.MethodTest$A gov.nasa.jpf.test.java.lang.reflect.MethodTest$B.foo(int)",
+        "public final void java.lang.Object.wait()"
+      };
+      boolean[] seen = new boolean[expected.length];
+      
+      Method[] methods = C.class.getMethods();
+      for (Method m : methods){
+        System.out.println(m);
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/math/BigIntegerTest.java b/src/tests/gov/nasa/jpf/test/java/math/BigIntegerTest.java
new file mode 100644 (file)
index 0000000..b77a770
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.math;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.math.BigInteger;
+
+import org.junit.Test;
+
+public class BigIntegerTest extends TestJPF {
+
+  /************************** test methods ************************/
+  @Test
+  public void testArithmeticOps() {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("testing arithmetic operations of BigInteger objects");
+
+      BigInteger big = new BigInteger("4200000000000000000");
+      BigInteger o = new BigInteger("100000000000000");
+      BigInteger notSoBig = new BigInteger("1");
+
+      BigInteger x = big.add(notSoBig);
+      String s = x.toString();
+      System.out.println("x = " + s);
+      assert s.equals("4200000000000000001");
+
+      x = big.divide(o);
+      int i = x.intValue();
+      assert i == 42000;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/net/LoadUtility.java b/src/tests/gov/nasa/jpf/test/java/net/LoadUtility.java
new file mode 100644 (file)
index 0000000..90f1d18
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.net;
+
+import java.io.File;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+/**
+ * This is used to hide the package "classloader_specific_tests" from JPF 
+ * which is need to test costum class loaders
+ * 
+ * @author Nastaran Shafiei
+ */
+public class LoadUtility extends TestJPF{
+
+  protected String user_dir = System.getProperty("user.dir");
+  protected String pkg = "classloader_specific_tests";
+
+  protected String originalPath = user_dir + "/build/tests/" + pkg;
+  protected String tempPath = user_dir + "/build/" + pkg;
+
+  protected String jarUrl = "jar:file:" + user_dir + "/build/" + pkg + ".jar!/";
+  protected String dirUrl = "file:" + user_dir + "/build";
+
+  /**
+   * move the package, to avoid systemClassLoader loading its classes
+   */
+  protected void movePkgOut() {
+    if(!TestJPF.isJPFRun()) {
+      movePkg(originalPath, tempPath);
+    }
+  }
+
+  /**
+   * move the package back to its original place
+   */
+  protected void movePkgBack() {
+    if(!TestJPF.isJPFRun()) {
+      movePkg(tempPath, originalPath);
+    }
+  }
+
+  protected void movePkg(String from, String to) {
+    File dstFile = new File(to);
+    if(!dstFile.exists()) {
+      dstFile = new File(from);
+      assertTrue(dstFile.renameTo(new File(to)));
+    } else {
+      File srcFile = new File(from);
+      if(srcFile.exists()) {
+        // empty the directory
+        for(String name: srcFile.list()) {
+          assertTrue((new File(from + "/" + name)).delete());
+        }
+        // remove the directory
+        assertTrue(srcFile.delete());
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/net/URLClassLoaderTest.java b/src/tests/gov/nasa/jpf/test/java/net/URLClassLoaderTest.java
new file mode 100644 (file)
index 0000000..2615e5f
--- /dev/null
@@ -0,0 +1,521 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.net;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * test of java.lang.ClassLoader API
+ */
+public class URLClassLoaderTest extends LoadUtility {
+
+  public class TestClassLoader extends URLClassLoader {
+
+    public TestClassLoader(URL[] urls) {
+        super(urls);
+    }
+
+    public TestClassLoader(URL[] urls, ClassLoader parent) {
+      super(urls, parent);
+    }
+    
+    @Override
+       public Class<?> findClass(String name) throws ClassNotFoundException {
+        return super.findClass(name);
+    }
+
+    public Class<?> getLoadedClass(String name) {
+      return findLoadedClass(name);
+    }
+
+    public Class<?> delegateTofindSystemClass(String cname) throws ClassNotFoundException {
+      return this.findSystemClass(cname);
+    }
+
+    @Override
+       protected Package[] getPackages() {
+      return super.getPackages();
+    }
+
+    @Override
+       protected Package getPackage(String name) {
+      return super.getPackage(name);
+    }
+  }
+
+  @Test
+  public void testConstructor_NullPointerException() {
+    if (verifyUnhandledException("java.lang.NullPointerException")) {
+      new URLClassLoader(null);
+    }
+  }
+
+  @Test 
+  public void testConstructorEmptyURLs () {
+    if (verifyNoPropertyViolation()) {
+      URLClassLoader cl = new URLClassLoader(new URL[0]);
+      assertNotNull(cl.getParent());
+      assertEquals(cl.getParent(), ClassLoader.getSystemClassLoader());
+    }
+  }
+
+  @Test
+  public void testConstructorParent() {
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[0];
+      ClassLoader parent = new TestClassLoader(urls);
+      URLClassLoader cl =  new URLClassLoader(urls, parent);
+
+      assertNotNull(parent.getParent());
+      assertEquals(parent.getParent(), ClassLoader.getSystemClassLoader());
+
+      assertNotNull(cl.getParent());
+      assertEquals(cl.getParent(), parent);
+    }
+  }
+
+  @Test
+  public void testLoadClass_NoClassDefFoundError() throws ClassNotFoundException {
+    if (verifyUnhandledException("java.lang.NoClassDefFoundError")) {
+      URL[] urls = new URL[0];
+      URLClassLoader cl = new URLClassLoader(urls);
+      cl.loadClass("java/lang/Class");
+    }
+  }
+
+  @Test
+  public void testLoadClass_ClassNotFoundException() throws ClassNotFoundException {
+    if (verifyUnhandledException("java.lang.ClassNotFoundException")) {
+      URL[] urls = new URL[0];
+      URLClassLoader cl =  new URLClassLoader(urls);
+      cl.loadClass("java.lang.Does_Not_Exist");
+    }
+  }
+
+  @Test
+  public void testLoadClass_ClassNotFoundException2() throws ClassNotFoundException {
+    if (verifyUnhandledException("java.lang.ClassNotFoundException")) {
+      URL[] urls = new URL[0];
+      URLClassLoader cl =  new URLClassLoader(urls);
+      cl.loadClass("java.lang.Class.class");
+    }
+  }
+
+  @Test
+  public void testSystemLoaderLoadClass() throws ClassNotFoundException {
+   if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[0];
+      ClassLoader systemCl = ClassLoader.getSystemClassLoader();
+      ClassLoader parent = new TestClassLoader(urls);
+      URLClassLoader cl =  new URLClassLoader(urls, parent);
+
+      String cname = "java.lang.Class";
+      Class<?> c1 = systemCl.loadClass(cname);
+      Class<?> c2 = parent.loadClass(cname);
+      Class<?> c3 = cl.loadClass(cname);
+
+      assertSame(c1, c2);
+      assertSame(c1, c3);
+      // this test fails on the host VM, cause java.lang.Class is loaded by
+      // bootstrap classloader and therefore c1.getClassLoader() returns null,
+      // but the test passes on JPF.
+      assertSame(c1.getClassLoader(), systemCl);
+    }
+  }
+
+  @Test
+  public void testFindLoadedClass() throws ClassNotFoundException, MalformedURLException {
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[0];
+      TestClassLoader ucl1 = new TestClassLoader(urls);
+      TestClassLoader ucl2 = new TestClassLoader(urls, ucl1);
+
+      String cname = "java.lang.Class";
+
+      Class<?> c = ucl2.loadClass(cname);
+      assertNotNull(c);
+      assertEquals(c.getName(), cname);
+
+      // systemClassLoader is going to be the defining classloader
+      assertNull(ucl2.getLoadedClass(cname));
+      assertNull(ucl1.getLoadedClass(cname));
+    }
+  }
+
+  @Test
+  public void testNonSystemLoaderLoadClass() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      // create a url from a dir
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader cl =  new URLClassLoader(urls);
+
+      String cname = pkg + ".Class1";
+      Class<?> cls = cl.loadClass(cname);
+
+      assertEquals(cls.getClassLoader(), cl);
+      assertFalse(cls.getClassLoader() == ClassLoader.getSystemClassLoader());
+
+      assertEquals(cls.getInterfaces().length, 2);
+      for(Class<?>ifc: cls.getInterfaces()) {
+        assertEquals(cls.getClassLoader(), ifc.getClassLoader());
+      }
+
+      // create a url from jar
+      urls[0] = new URL(jarUrl);
+      cl =  new URLClassLoader(urls);
+      cls = cl.loadClass(cname);
+
+      assertEquals(cls.getClassLoader(), cl);
+      assertFalse(cls.getClassLoader() == ClassLoader.getSystemClassLoader());
+      assertEquals(cls.getInterfaces().length, 2);
+      for(Class<?>ifc: cls.getInterfaces()) {
+        assertEquals(cls.getClassLoader(), ifc.getClassLoader());
+      }
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testFindResource() throws MalformedURLException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader cl =  new URLClassLoader(urls);
+
+      String resClass1 = pkg + "/Class1.class";
+      URL url = cl.findResource(resClass1);
+      String expectedUrl = dirUrl + "/" + resClass1;
+      assertEquals(url.toString(), expectedUrl);
+
+      String resInterface1 = pkg + "/Interface1.class";
+      url = cl.findResource(resInterface1);
+      expectedUrl = dirUrl + "/" + resInterface1;
+      assertEquals(url.toString(), expectedUrl);
+
+      url = cl.findResource("non_existence_resource");
+      assertNull(url);
+
+      url = cl.findResource("java/lang/Class.class");
+      assertNull(url);
+
+      // create a url from jar
+      urls[0] = new URL(jarUrl);
+      cl =  new URLClassLoader(urls);
+      url = cl.findResource(resClass1);
+      expectedUrl = jarUrl + resClass1;
+      assertEquals(url.toString(), expectedUrl);
+
+      url = cl.findResource(resInterface1);
+      expectedUrl = jarUrl + resInterface1;
+      assertEquals(url.toString(), expectedUrl);
+
+      url = cl.findResource("non_existence_resource");
+      assertNull(url);
+
+      url = cl.findResource("java/lang/Class.class");
+      assertNull(url);
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testFindResources() throws IOException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl), new URL(jarUrl), new URL(jarUrl) };
+      URLClassLoader cl =  new URLClassLoader(urls);
+      String resource = pkg + "/Class1.class";
+      Enumeration<URL> e = cl.findResources(resource);
+
+      List<String> urlList = new ArrayList<String>();
+      while(e.hasMoreElements()) {
+        urlList.add(e.nextElement().toString());
+      }
+
+      assertTrue(urlList.contains(jarUrl + resource));
+      assertTrue(urlList.contains(dirUrl + "/" + resource));
+
+      // we added the same url path twice, but findResource return value should only 
+      // include one entry for the same resource
+      assertEquals(urlList.size(), 2);
+
+      e = cl.findResources(null);
+      assertNotNull(e);
+      assertFalse(e.hasMoreElements());
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testGetURLs() throws MalformedURLException {
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[5];
+      urls[0] = new URL("file://" + "/x/y/z/" );
+      urls[1] = new URL("file://" + "/a/b/c/" );
+      urls[2] = new URL("file://" + "/a/b/c/" );
+      urls[3] = new URL(dirUrl);;
+      urls[4] = new URL(jarUrl);
+
+      URLClassLoader cl =  new URLClassLoader(urls);
+      URL[] clUrls = cl.getURLs();
+
+      assertEquals(clUrls.length, urls.length);
+      for (int i=0; i<urls.length; i++) {
+        assertEquals(clUrls[i], urls[i]);
+      }
+    }
+  }
+
+  @Test
+  public void testNewInstance1() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[1];
+      urls[0] = new URL(dirUrl);
+      URLClassLoader cl =  URLClassLoader.newInstance(urls);
+      Class<?> c = cl.loadClass(pkg + ".Class1");
+      assertNotNull(c);
+      assertSame(c.getClassLoader(), cl);
+      URL resource = cl.getResource(pkg + "/Interface1.class");
+      assertNotNull(resource);
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testNewInstance2() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = new URL[1];
+      urls[0] = new URL(dirUrl);
+      URLClassLoader parent =  URLClassLoader.newInstance(urls);
+      URLClassLoader cl =  URLClassLoader.newInstance(urls, parent);
+      assertSame(parent, cl.getParent());
+
+      Class<?> c = cl.loadClass(pkg + ".Class1");
+      assertNotNull(c);
+      assertSame(c.getClassLoader(), parent);
+
+      String resName = pkg + "/Interface1.class";
+      URL resource = cl.getResource(resName);
+      assertNotNull(resource);
+
+      resource = cl.getParent().getResource(resName);
+      assertNotNull(resource);
+    }
+    movePkgBack();
+  }
+
+  public class Standard extends URLClassLoader {
+    public Standard (URL[] urls) {
+      super(urls);
+    }
+
+    public Standard(URL[] urls, ClassLoader parent) {
+      super(urls, parent);
+    }
+  }
+
+  public class Custom extends URLClassLoader {
+    public Custom (URL[] urls) {
+      super(urls);
+    }
+    
+    public Custom(URL[] urls, ClassLoader parent) {
+      super(urls, parent);
+    }
+    
+    @Override
+       protected Class<?> findClass(String name) throws ClassNotFoundException {
+      return super.findClass(name);
+    }
+    
+    @Override
+       public Class<?> loadClass(String name) throws ClassNotFoundException {
+      return super.loadClass(name);
+    }
+  }
+    
+  @Test
+  public void testClassResolution() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      // create a url from a dir
+      URL[] urls = { new URL(dirUrl) };
+      String cname = pkg + ".Class1";
+      String objClass = "java.lang.Object";
+
+      Standard cl1 = new Standard(new URL[0]);
+      Standard cl2 = new Standard(urls, cl1);
+      Standard cl3 =  new Standard(urls, cl2);
+
+      Class<?> c = cl3.loadClass(cname);
+      assertEquals(c.getClassLoader(), cl2);
+
+      c = cl3.loadClass(objClass);
+      assertEquals(c.getClassLoader(), ClassLoader.getSystemClassLoader());
+
+      Custom cl4 = new Custom(urls, null);
+      Standard cl5 = new Standard(urls, cl4);
+
+      c = cl5.loadClass(cname);  // delegates to cl4 (Custom)
+      assertEquals(c.getClassLoader(), cl4);
+      
+      Class<?> c4 = cl4.loadClass(cname);
+      assertSame(c, c4);
+
+      c = cl5.loadClass(objClass);
+      assertEquals(c.getClassLoader(), ClassLoader.getSystemClassLoader());
+      assertSame(c, cl4.loadClass(objClass));
+
+      cl4 = new Custom(urls, cl3);
+      cl5 = new Standard(urls, cl4);
+
+      c = cl5.loadClass(cname);
+      assertEquals(c.getClassLoader(), cl2);
+      assertSame(c, cl4.loadClass(cname));
+
+      c = cl5.loadClass(objClass);
+      assertEquals(c.getClassLoader(), ClassLoader.getSystemClassLoader());
+      assertSame(c, cl4.loadClass(objClass));
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testFindSystemClass() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      TestClassLoader loader = new TestClassLoader(urls);
+      assertNotNull(loader.delegateTofindSystemClass("java.lang.Class"));
+
+      String cname = pkg + ".Class1";
+      assertNotNull(loader.loadClass(cname));
+
+      try {
+        loader.delegateTofindSystemClass(cname);
+      } catch(ClassNotFoundException e) {
+        
+      }
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testFindSystemClass_ClassNotFoundException() throws MalformedURLException, ClassNotFoundException {
+    movePkgOut();
+    if (verifyUnhandledException("java.lang.ClassNotFoundException")) {
+      URL[] urls = { new URL(dirUrl) };
+      TestClassLoader cl = new TestClassLoader(urls);
+      String cname = pkg + ".Class1";
+
+      // this should fail, cause our SystemClassLoader cannot find a non-standard 
+      // class that is not on the classpath
+      cl.delegateTofindSystemClass(cname);
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testGetPackages() throws ClassNotFoundException, MalformedURLException {
+    movePkgOut();
+    if(verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      TestClassLoader cl = new TestClassLoader(urls);
+      Package[] pkgs = cl.getPackages();
+
+      boolean java_lang = false;
+      boolean classloader_specific_tests = false;
+      for(int i=0; i<pkgs.length; i++) {
+        if(pkgs[i].getName().equals("java.lang")) {
+          java_lang = true;
+        } else if(pkgs[i].getName().equals("classloader_specific_tests")) {
+          classloader_specific_tests = true;
+        }
+      }
+      assertTrue(java_lang && !classloader_specific_tests);
+
+      String cname = pkg + ".Class1";
+      cl.loadClass(cname);
+      pkgs = cl.getPackages();
+      for(int i=0; i<pkgs.length; i++) {
+        if(pkgs[i].getName().equals("java.lang")) {
+          java_lang = true;
+        } else if(pkgs[i].getName().equals("classloader_specific_tests")) {
+          classloader_specific_tests = true;
+        }
+      }
+      assertTrue(java_lang && classloader_specific_tests);
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testGetPackage() throws ClassNotFoundException, MalformedURLException {
+    movePkgOut();
+    if(verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      TestClassLoader cl = new TestClassLoader(urls);
+      assertNotNull(cl.getPackage("java.lang"));
+      assertNull(cl.getPackage("non_existing_package"));
+      assertNull(cl.getPackage("classloader_specific_tests"));
+
+      String cname = pkg + ".Class1";
+      cl.loadClass(cname);
+      assertNotNull(cl.getPackage("classloader_specific_tests"));
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testThrownException() throws ClassNotFoundException, MalformedURLException, SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    movePkgOut();
+    if(verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      TestClassLoader loader = new TestClassLoader(urls);
+      String cname = pkg + ".Class1";
+
+      Class<?> c = loader.loadClass(cname);
+      Method m = c.getMethod("causeArithmeticException", new Class<?>[0]);
+
+      try {
+        m.invoke(null, new Object[0]);
+        fail("Should have thrown java.lang.ArithmeticException: division by zero");
+        
+      } catch (InvocationTargetException ite) {
+        Throwable cause = ite.getCause();
+        assertTrue( cause instanceof ArithmeticException && cause.getMessage().equals("division by zero"));
+      }
+    }
+    movePkgBack();
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/net/URLEncoderTest.java b/src/tests/gov/nasa/jpf/test/java/net/URLEncoderTest.java
new file mode 100644 (file)
index 0000000..880b8f0
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.net;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import org.junit.Test;
+
+
+/**
+ * regression test for URLEncoder/Decoder
+ */
+public class URLEncoderTest extends TestJPF {
+
+  /************************** test methods ************************/
+  @Test
+  public void testEncodeCycle (){
+    if (verifyNoPropertyViolation()){
+      String s = "< what a mess >";
+      String enc = "UTF-8";
+
+      try {
+        System.out.println("original: " + s);
+        String e = URLEncoder.encode(s, enc);
+        System.out.println("encoded:  " + e);
+        String d = URLDecoder.decode(e,enc);
+        System.out.println("decoded:  " + d);
+
+        assert s.equals(d) : "encode/decode roundtrip failed";
+
+      } catch (Throwable t){
+        fail("unexpected exception: " + t);
+      }
+    }
+  }
+
+  @Test
+  public void testEncodingException (){
+    if (verifyNoPropertyViolation()){
+      String s = "< what a mess >";
+      String enc = "wrgsGrff";
+
+      try {
+        System.out.println("original: " + s);
+        String e = URLEncoder.encode(s, enc);
+        System.out.println("encoded:  " + e);
+
+        fail("this is really not a known encoding: " + enc);
+
+      } catch (java.io.UnsupportedEncodingException x){
+        System.out.println("rightfully throws " + x);
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/text/DateFormatTest.java b/src/tests/gov/nasa/jpf/test/java/text/DateFormatTest.java
new file mode 100644 (file)
index 0000000..ec41b47
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.text;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+public class DateFormatTest extends TestJPF {
+
+  @Test
+  public void testConversionCycle() {
+    if (verifyNoPropertyViolation()) {
+      DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+      df.setLenient(true);
+
+      Date d1 = new Date();
+      System.out.print("current date is: ");
+      System.out.println(d1);
+
+      String s = df.format(d1);
+      System.out.print("formatted date is: ");
+      System.out.println(s);
+
+      try {
+        Date d2 = df.parse(s);
+
+        System.out.print("re-parsed date is: ");
+        System.out.println(d2);
+
+        long t1 = d1.getTime(); // in ms
+        long t2 = d2.getTime(); // in ms
+        long delta = Math.abs(t2 - t1);
+
+        // since we loose the ms in String conversion, d2.after(d1) does not necessarily hold
+        // some locales don't format the seconds, to we might loose them in the re-conversion
+        assert delta <= 60000 : "difference > 1min";
+
+      } catch (ParseException x) {
+        assert false : "output did not parse: " + x;
+      }
+    }
+  }
+
+  @Test
+  public void testSetAndGetTimeZone() {
+    if (verifyNoPropertyViolation()) {
+      DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+      TimeZone timeZone = TimeZone.getTimeZone("GMT");
+      df.setTimeZone(timeZone);
+      assertEquals(timeZone, df.getTimeZone());
+      timeZone = TimeZone.getTimeZone("PDT");
+      df.setTimeZone(timeZone);
+      assertEquals(timeZone, df.getTimeZone());
+    }
+  }
+
+  @Test
+  public void testFormatWithTimeZone() {
+    if (verifyNoPropertyViolation()) {
+      DateFormat df = DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+      TimeZone timeZone = TimeZone.getTimeZone("GMT");
+      df.setTimeZone(timeZone);
+      Calendar calendar = new GregorianCalendar(timeZone);
+      calendar.set(2010, 10, 10, 10, 10, 10);
+      String time = "10:10"; // some Locales don't include the seconds
+      String dft = df.format(calendar.getTime()); 
+      assertTrue(dft.contains(time));
+      df.setTimeZone(TimeZone.getTimeZone("EST"));
+      time = "5:10";  // some Locales don't include the seconds
+      dft = df.format(calendar.getTime());
+      assertTrue(dft.contains(time));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/java/text/DecimalFormatTest.java b/src/tests/gov/nasa/jpf/test/java/text/DecimalFormatTest.java
new file mode 100644 (file)
index 0000000..4812659
--- /dev/null
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.text;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+
+import org.junit.Test;
+
+/**
+ * simple regression test for java.text.DecimalFormat
+ */
+public class DecimalFormatTest extends TestJPF {
+
+  @Test
+  public void testDoubleConversion() {
+
+    if (verifyNoPropertyViolation()) {
+      StringBuffer sb = new StringBuffer();
+      DecimalFormat dFormat = new DecimalFormat();
+      sb = dFormat.format(new Double(42), sb, new FieldPosition(0));
+      String output = sb.toString();
+      try {
+        double d = Double.parseDouble(output);
+        assert (d == 42.0) : "parsed value differs: " + output;
+      } catch (NumberFormatException e) {
+        assert false : "output did not parse " + e;
+      }
+    }
+  }
+
+  @Test
+  public void testIsParseIntegerOnly () {
+    if (verifyNoPropertyViolation()) {
+      DecimalFormat dFormat = new DecimalFormat();
+      assertFalse(dFormat.isParseIntegerOnly());
+      dFormat.setParseIntegerOnly(true);
+      assertTrue(dFormat.isParseIntegerOnly());
+      dFormat.setParseIntegerOnly(false);
+      assertFalse(dFormat.isParseIntegerOnly());
+      NumberFormat format = NumberFormat.getIntegerInstance();
+      assertTrue(format.isParseIntegerOnly());
+      format = NumberFormat.getNumberInstance();
+      assertFalse(format.isParseIntegerOnly());
+    }
+  }
+
+  @Test
+  public void testIsGroupingUsed() {
+    if (verifyNoPropertyViolation()) {
+      DecimalFormat dFormat = new DecimalFormat();
+      assertTrue(dFormat.isGroupingUsed());
+      dFormat.setGroupingUsed(false);
+      assertFalse(dFormat.isGroupingUsed());
+      dFormat.setGroupingUsed(true);
+      assertTrue(dFormat.isGroupingUsed());
+    }
+  }
+
+  @Test
+  public void testSetGroupingUsed() {
+
+    if (verifyNoPropertyViolation()) {
+      DecimalFormat dFormat = new DecimalFormat();
+      String s = dFormat.format(4200000L);
+      assertTrue(s.length() == 9);
+      dFormat.setGroupingUsed(false);
+      s = dFormat.format(4200000L);
+      assertTrue(s.equals("4200000"));
+      dFormat.setGroupingUsed(true);
+      s = dFormat.format(4200000L);
+      assertTrue(s.length() == 9);
+    }
+  }
+
+  @Test
+  public void testParseDouble() {
+
+    if (verifyNoPropertyViolation()) {
+      DecimalFormatSymbols dfs = new DecimalFormatSymbols();
+      DecimalFormat dFormat = new DecimalFormat();
+      ParsePosition ps = new ParsePosition(0);
+      Number nb = dFormat.parse("10" + dfs.getDecimalSeparator() + "10",ps);
+      assertTrue(nb instanceof Double);
+      assertTrue(nb.doubleValue() == 10.10d);
+      assertTrue(ps.getErrorIndex() == -1);
+      assertTrue(ps.getIndex() == 5);
+    }
+  }
+
+  @Test
+  public void testParseInt() {
+
+    if (verifyNoPropertyViolation()) {
+      DecimalFormat dFormat = new DecimalFormat();
+      ParsePosition ps = new ParsePosition(0);
+      Number nb = dFormat.parse("10",ps);
+      assertTrue(nb instanceof Long);
+      assertTrue(nb.doubleValue() == 10l);
+      assertTrue(ps.getErrorIndex() == -1);
+      assertTrue(ps.getIndex() == 2);
+    }
+  }
+
+  @Test
+  public void testParseError() {
+
+    if (verifyNoPropertyViolation()) {
+      DecimalFormat dFormat = new DecimalFormat();
+      int parsePos = 1;
+      ParsePosition ps = new ParsePosition(parsePos);
+      Number nb = dFormat.parse("^^10",ps);
+      assertEquals(nb,null);
+      assertEquals(ps.getIndex(), parsePos);
+      assertEquals(ps.getErrorIndex(), parsePos);
+    }
+  }
+}
+
diff --git a/src/tests/gov/nasa/jpf/test/java/text/SimpleDateFormatTest.java b/src/tests/gov/nasa/jpf/test/java/text/SimpleDateFormatTest.java
new file mode 100644 (file)
index 0000000..04204b9
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.java.text;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.TimeZone;
+
+import org.junit.Test;
+
+public class SimpleDateFormatTest extends TestJPF {
+
+  @Test
+  public void testFormatWithTimeZone() {
+    if (verifyNoPropertyViolation()) {
+      SimpleDateFormat df = (SimpleDateFormat) DateFormat.getDateTimeInstance(DateFormat.FULL, DateFormat.FULL);
+      TimeZone timeZone = TimeZone.getTimeZone("GMT");
+      df.setTimeZone(timeZone);
+      Calendar calendar = new GregorianCalendar(timeZone);
+      calendar.set(2010, 10, 10, 10, 10, 10);
+      String time = "10:10"; // some locales don't print the secs
+      assertTrue(df.format(calendar.getTime(), new StringBuffer(), new FieldPosition(0)).toString().contains(time));
+      df.setTimeZone(TimeZone.getTimeZone("EST"));
+      time = "5:10"; // see above
+      assertTrue(df.format(calendar.getTime(), new StringBuffer(), new FieldPosition(0)).toString().contains(time));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/AttrsTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/AttrsTest.java
new file mode 100644 (file)
index 0000000..ecdece1
--- /dev/null
@@ -0,0 +1,544 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.DSTORE;
+import gov.nasa.jpf.jvm.bytecode.INVOKEVIRTUAL;
+import gov.nasa.jpf.jvm.bytecode.ISTORE;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.LRETURN;
+import gov.nasa.jpf.util.ObjectList;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * raw test for field/operand/local attribute handling
+ */
+public class AttrsTest extends TestJPF {
+
+//------------ this part we only need outside of JPF execution
+  static class AttrType {
+    @Override
+       public String toString() {
+      return "<an AttrType>";
+    }
+  }
+  static final AttrType ATTR = new AttrType();
+  static final Class<?> ATTR_CLASS = ATTR.getClass();
+
+  public static class IntListener extends ListenerAdapter {
+
+    public IntListener () {}
+
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      MethodInfo mi = executedInsn.getMethodInfo();
+
+      // not very efficient, but who cares - it's a small test
+      if (executedInsn instanceof ISTORE){
+        if (mi.getName().equals("testIntPropagation")){
+          ISTORE istore = (ISTORE)executedInsn;
+          String localName = istore.getLocalVariableName();
+          int localIndex = istore.getLocalVariableIndex();
+
+          if (localName.equals("i")){
+            StackFrame frame = ti.getModifiableTopFrame();
+            frame.setLocalAttr(localIndex, ATTR);
+            
+            Object a = frame.getLocalAttr(localIndex, ATTR_CLASS);
+            System.out.println("'i' attribute set to: " + a);
+
+          } else if (localName.equals("j")){
+            StackFrame frame = ti.getTopFrame();
+            
+            Object a = frame.getLocalAttr(localIndex, ATTR_CLASS);
+            System.out.println("'j' AttrType attribute: " + a);
+          }
+        }
+      }
+    }
+  }
+  
+  static int sInt;
+  int iInt;
+
+  static double sDouble;
+  double iDouble;
+
+  int echoInt (int a){
+    return a;
+  }
+
+  @Test public void testIntPropagation () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.AttrsTest$IntListener")) {
+      int i = 42; // this gets attributed
+      Verify.setLocalAttribute("i", 42); // this overwrites whatever the ISTORE listener did set on 'i'
+      int attr = Verify.getLocalAttribute("i");
+      Verify.println("'i' attribute after Verify.setLocalAttribute(\"i\",42): " + attr);
+      assertTrue( attr == 42);
+
+      iInt = echoInt(i); // return val -> instance field
+      sInt = iInt; // instance field -> static field
+      int j = sInt; // static field -> local - now j should have the initial i attribute, and value 42
+      
+      attr = Verify.getLocalAttribute("j");
+      Verify.println("'j' attribute after assignment: " + attr);
+      assertTrue( attr == 42);
+    }
+  }
+  
+  //----------------------------------------------------------------------------------------------
+  
+  public static class DoubleListener extends ListenerAdapter {
+
+    public DoubleListener () {}
+
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      MethodInfo mi = executedInsn.getMethodInfo();
+
+      if (executedInsn instanceof DSTORE){
+        if (mi.getName().equals("testDoublePropagation")){
+          DSTORE dstore = (DSTORE)executedInsn;
+          String localName = dstore.getLocalVariableName();
+          int localIndex = dstore.getLocalVariableIndex();
+
+          if (localName.equals("d")){
+            StackFrame frame = ti.getModifiableTopFrame();
+
+            System.out.print("listener setting 'd' attr = ");
+            frame.setLocalAttr(localIndex, ATTR);
+            Object a = frame.getLocalAttr(localIndex);
+            System.out.println( a);
+
+          } else if (localName.equals("r")){
+            StackFrame frame = ti.getTopFrame();
+            Object a = frame.getLocalAttr(localIndex, ATTR_CLASS);
+            System.out.println("'r' attribute: " + a);
+            
+            /** get's overwritten in the model class
+            if (a != ATTR){
+              throw new JPFException("attribute propagation failed");
+            }
+            **/
+          }
+        }
+
+      }
+    }
+  }
+
+  @Test public void testDoublePropagation () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.AttrsTest$DoubleListener")) {
+      double d = 42.0; // this gets attributed
+      Verify.setLocalAttribute("d", 42);  // this overwrites whatever the DSTORE listener did set on 'd'
+      int attr = Verify.getLocalAttribute("d");
+      assert attr == 42;
+
+      // some noise on the stack
+      iDouble = echoDouble(d);
+      sDouble = iDouble;
+
+      //double r = sDouble; // now r should have the same attribute
+      double r = echoDouble(d);
+
+      attr = Verify.getLocalAttribute("r");
+      Verify.print("@ 'r' attribute after assignment: " + attr);
+      Verify.println();
+
+      assert attr == 42;
+    }
+  }
+
+  
+  //-----------------------------------------------------------------------------------------------
+
+  public static class InvokeListener extends ListenerAdapter {
+
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      if (executedInsn instanceof JVMInvokeInstruction) {
+        JVMInvokeInstruction call = (JVMInvokeInstruction)executedInsn;
+        MethodInfo mi = call.getInvokedMethod();
+        String mName = mi.getName();
+        if (mName.equals("goModel") || mName.equals("goNative")) {
+          Object[] a = call.getArgumentAttrs(ti);
+          assert a != null & a.length == 3;
+
+          System.out.println("listener notified of: " + mName + "(), attributes= "
+                             + a[0] + ',' + a[1] + ',' + a[2]);
+
+          // note - this is only acceptable if we know exactly there are just
+          // single attrs
+          
+          assert a[0] instanceof Integer && a[1] instanceof Integer;
+          assert (((Integer)a[0]).intValue() == 1) &&
+                 (((Integer)a[1]).intValue() == 2) &&
+                 (((Integer)a[2]).intValue() == 3);
+        }
+      }
+    }
+  }
+  
+  @Test public void testInvokeListener () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.AttrsTest$InvokeListener")) {
+      Verify.setLocalAttribute("this", 1);
+
+      double d = 42.0;
+      Verify.setLocalAttribute("d", 2);
+
+      int i = 42;
+      Verify.setLocalAttribute("i", 3);
+
+      double result = goNative(d, i); // that's going to be listened on
+      int attr = Verify.getLocalAttribute("result");
+
+      Verify.print("@ 'result' attribute: " + attr);
+      Verify.println();
+
+      assert attr == 6;
+
+      int r = goModel(d, i);  // that's listened for, too
+      assert r == 6;
+    }
+  }
+
+
+  native double goNative (double d, int i);
+
+  @Test public void testNativeMethod () {
+    if (verifyNoPropertyViolation()) {
+      Verify.setLocalAttribute("this", 1);
+
+      double d = 42.0;
+      Verify.setLocalAttribute("d", 2);
+
+      int i = 42;
+      Verify.setLocalAttribute("i", 3);
+
+      double result = goNative(d, i);
+      int attr = Verify.getLocalAttribute("result");
+
+      Verify.print("@ 'result' attribute: " + attr);
+      Verify.println();
+
+      assert attr == 6;
+    }
+  }
+
+
+  int goModel (double d, int i) {
+    int a1 = Verify.getLocalAttribute("d");
+    int a2 = Verify.getLocalAttribute("i");
+
+    return a1*a2;
+  }
+
+  double echoDouble (double d){
+    return d;
+  }
+
+
+  @Test public void testExplicitRef () {
+    if (verifyNoPropertyViolation()) {
+      int attr = Verify.getFieldAttribute(this, "iDouble");
+      Verify.print("@ 'iDouble' attribute before set: ", Integer.toString(attr));
+      Verify.println();
+
+      Verify.setFieldAttribute(this, "iDouble", 42);
+
+      attr = Verify.getFieldAttribute(this, "iDouble");
+      Verify.print("@ 'iDouble' attribute after set: ", Integer.toString(attr));
+      Verify.println();
+
+      assert attr == 42;
+    }
+  }
+
+  @Test public void testExplicitArrayRef () {
+    if (verifyNoPropertyViolation()) {
+      int attr;
+      double[] myArray = new double[10];
+
+      attr = Verify.getElementAttribute(myArray, 5);
+      Verify.print("@ 'myArray[5]' attribute before set: ", Integer.toString(attr));
+      Verify.println();
+
+      Verify.setElementAttribute(myArray, 5, 42);
+
+      attr = Verify.getElementAttribute(myArray, 5);
+      Verify.print("@ 'myArray[5]' attribute after set: ", Integer.toString(attr));
+      Verify.println();
+
+      assert attr == 42;
+    }
+  }
+
+  @Test public void testArraycopy () {
+    if (verifyNoPropertyViolation()) {
+      int attr;
+      double[] a1 = new double[10];
+      double[] a2 = new double[10];
+
+      Verify.setElementAttribute(a1, 3, 42);
+      System.arraycopy(a1, 1, a2, 0, 3);
+
+      attr = Verify.getElementAttribute(a2, 2);
+      assert attr == 42;
+    }
+  }
+
+  double ddd;
+
+  @Test public void testArrayPropagation() {
+    if (verifyNoPropertyViolation()) {
+
+      int attr;
+      double[] a1 = new double[10];
+      double[] a2 = new double[10];
+
+      Verify.setElementAttribute(a1, 3, 42);
+
+      //attr = Verify.getElementAttribute(a1,3);
+      //System.out.println(attr);
+
+      ddd = a1[3];
+      //Verify.setFieldAttribute(this,"ddd",42);
+      //attr = Verify.getFieldAttribute(this,"ddd");
+      //System.out.println("@ ddd : " + attr);
+
+      double d = ddd;
+      //ccc = d;
+      //attr = Verify.getFieldAttribute(this,"ccc");
+      //System.out.println("ccc ; " + attr);
+
+      //double d = a1[3]; // now d should have the attr
+      a2[0] = d;
+      attr = Verify.getElementAttribute(a2, 0);
+      System.out.println("@ a2[0] : " + attr);
+
+      assert attr == 42;
+    }
+  }
+
+  @Test public void testBacktrack() {
+    if (verifyNoPropertyViolation()) {
+      int v = 42; // need to init or the compiler does not add it to the name table
+      Verify.setLocalAttribute("v", 42);
+
+      boolean b = Verify.getBoolean(); // restore point
+      System.out.println(b);
+
+      int attr = Verify.getLocalAttribute("v");
+      System.out.println(attr);
+
+      Verify.setLocalAttribute("v", -1);
+      attr = Verify.getLocalAttribute("v");
+      System.out.println(attr);
+    }
+  }
+  
+  @Test public void testInteger() {
+    if (verifyNoPropertyViolation()) {
+      int v = 42;
+      Verify.setLocalAttribute("v", 4200);
+
+      // explicit
+      Integer o = new Integer(v);
+      int j = o.intValue();
+      int attr = Verify.getLocalAttribute("j");
+      assert attr == 4200;
+
+      // semi autoboxed
+      j = o;
+      boolean b = Verify.getBoolean(); // just cause some backtracking damage
+      attr = Verify.getLocalAttribute("j");
+      assert attr == 4200;
+
+    /** this does not work because of cached, preallocated Integer objects)
+    // fully autoboxed
+    Integer a = v;
+    j = a;
+    attr = Verify.getLocalAttribute("j");
+    assert attr == 4200;
+     **/
+    }
+  }
+  
+  @Test public void testObjectAttr(){
+
+    if (verifyNoPropertyViolation()){
+      Integer o = new Integer(41);
+      Verify.setObjectAttribute(o, 42);
+
+      boolean b = Verify.getBoolean();
+
+      int attr = Verify.getObjectAttribute(o);
+      System.out.println("object attr = " + attr);
+      assert attr == 42;
+    }
+  }
+  
+  //--- the multiple attributes tests
+  
+  @Test
+  public void testIntAttrList(){
+    if (verifyNoPropertyViolation()){
+      int var = 42;
+      Verify.addLocalAttribute("var", Integer.valueOf(var));
+      Verify.addLocalAttribute("var", Integer.valueOf(-var));
+      
+      int x = var;
+      int[] attrs = Verify.getLocalAttributes("x");
+      for (int i=0; i<attrs.length; i++){
+        System.out.printf("[%d] = %d\n", i, attrs[i]);
+      }
+      
+      assertTrue( attrs.length == 2);
+      assertTrue( attrs[0] == -var); // lifo
+      assertTrue( attrs[1] == var);
+    }
+  }
+  
+  
+  public static class MixedAttrTypeListener extends ListenerAdapter {
+    
+    public MixedAttrTypeListener() {}
+    
+    @Override
+    public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+      
+      if (insnToExecute instanceof INVOKEVIRTUAL){
+        MethodInfo callee = ((INVOKEVIRTUAL)insnToExecute).getInvokedMethod();
+        if (callee.getUniqueName().equals("foo(J)J")){
+          System.out.println("--- pre-exec foo() invoke interception, setting arg attrs");
+          
+          StackFrame frame = ti.getModifiableTopFrame();
+          
+          // we are still in the caller stackframe
+          frame.addLongOperandAttr("foo-arg");
+          
+          Long v = Long.valueOf( frame.peekLong());
+          frame.addLongOperandAttr( v);
+          
+          System.out.println("   operand attrs:");
+          for (Object a: frame.longOperandAttrIterator()){
+            System.out.println(a);
+          }
+        }
+        
+      } else if (insnToExecute instanceof LRETURN){
+        MethodInfo mi = insnToExecute.getMethodInfo();
+        if (mi.getUniqueName().equals("foo(J)J")){
+          System.out.println("--- pre-exec foo() return interception");
+          StackFrame frame = ti.getModifiableTopFrame();
+          int varIdx = frame.getLocalVariableSlotIndex("x");
+          Object attr = frame.getLocalAttr(varIdx);
+
+          System.out.println("  got 'x' attributes");
+          for (Object a: frame.localAttrIterator(varIdx)){
+            System.out.println(a);
+          }                  
+          
+          assertTrue(attr.equals("foo-arg"));
+          
+          System.out.println("  setting lreturn operand attrs");
+          frame.addLongOperandAttr("returned");
+          
+          for (Object a: frame.longOperandAttrIterator()){
+            System.out.println(a);
+          }          
+        }
+      }
+    }
+    
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+
+      if (executedInsn instanceof INVOKEVIRTUAL){
+        MethodInfo callee = ((INVOKEVIRTUAL)executedInsn).getInvokedMethod();
+        if (callee.getUniqueName().equals("foo(J)J")){
+          System.out.println("--- post-exec foo() invoke interception");
+          StackFrame frame = ti.getModifiableTopFrame(); // we are now in the callee
+          int varIdx = frame.getLocalVariableSlotIndex("x");
+
+          for (Object a: frame.localAttrIterator(varIdx)){
+            System.out.println(a);
+          }
+          
+          Object attrs = frame.getLocalAttr(varIdx);
+          assertTrue( ObjectList.size(attrs) == 2);
+          
+          Object sAttr = frame.getLocalAttr(varIdx, String.class);
+          assertTrue( sAttr != null && sAttr.equals("foo-arg"));
+          assertTrue( frame.getNextLocalAttr(varIdx, String.class, sAttr) == null);
+          
+          Object lAttr = frame.getLocalAttr(varIdx, Long.class);
+          assertTrue( lAttr != null && lAttr.equals( frame.getLongLocalVariable(varIdx)));
+          assertTrue( frame.getNextLocalAttr(varIdx, Long.class, lAttr) == null);
+          
+          frame.removeLocalAttr(varIdx, lAttr);
+          System.out.println("  removing " + lAttr);
+          for (Object a: frame.localAttrIterator(varIdx)){
+            System.out.println(a);
+          }
+        }
+        
+      } else if (executedInsn instanceof LRETURN){
+        MethodInfo mi = executedInsn.getMethodInfo();
+        if (mi.getUniqueName().equals("foo(J)J")){
+          StackFrame frame = ti.getTopFrame();
+          
+          System.out.println("--- post-exec foo() return interception");
+          for (Object a: frame.longOperandAttrIterator()){
+            System.out.println(a);
+          }
+          
+          String a = frame.getLongOperandAttr(String.class);
+          assertTrue( a.equals("returned"));
+          
+          a = frame.getNextLongOperandAttr(String.class, a);
+          assertTrue( a.equals("foo-arg"));
+          
+          a = frame.getNextLongOperandAttr(String.class, a);
+          assertTrue(a == null);
+        }        
+      }
+    }
+  }
+    
+  long foo (long x){
+    return x;
+  }
+  
+  @Test
+  public void testListenerMixedLongAttrLists(){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.AttrsTest$MixedAttrTypeListener")){
+      foo(42);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/BreakTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/BreakTest.java
new file mode 100644 (file)
index 0000000..944eed7
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.PUTFIELD;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+/**
+ * simple test application to break transitions from listeners
+ */
+public class BreakTest extends TestJPF {
+
+  static final String LISTENER = "+listener=.test.mc.basic.BreakTestListener";
+
+  static class BreakListener extends ListenerAdapter {
+    public static int nCG; // braindead, just to check from outside
+
+    public BreakListener() {
+      nCG = 0;
+    }
+
+    @Override
+    public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG) {
+      System.out.println("CG set: " + newCG);
+      nCG++;
+    }
+
+    @Override
+    public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+      System.out.println("CG advanced: " + currentCG);
+    }
+  }
+
+
+  int data;
+  
+  //--- test setIgnored
+
+  public static class FieldIgnorer extends BreakListener {
+    @Override
+       public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+
+      if (executedInsn instanceof PUTFIELD) {  // break on field access
+        FieldInfo fi = ((PUTFIELD) executedInsn).getFieldInfo();
+        if (fi.getClassInfo().getName().endsWith(".BreakTest")) {
+          System.out.println("# ignoring after: " + executedInsn);
+          ss.setIgnored(true);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void testSimpleIgnore () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.BreakTest$FieldIgnorer",
+                                  "+vm.max_transition_length=1000000")) { 
+      int i = 42;
+      data = i; // we ignore here
+      fail("should never get here");
+
+    } else {
+      if (BreakListener.nCG != 1) { // that's really simplistic
+        fail("wrong number of CGs: " + BreakListener.nCG);
+      }
+    }
+  }
+
+
+  //--- testSimpleBreak
+
+  public static class FieldBreaker extends BreakListener {
+    @Override
+       public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+
+      if (executedInsn instanceof PUTFIELD) {  // break on field access
+        FieldInfo fi = ((PUTFIELD) executedInsn).getFieldInfo();
+        if (fi.getClassInfo().getName().endsWith(".BreakTest")) {
+          System.out.println("# breaking after: " + executedInsn);
+          ti.breakTransition("breakTest");
+        }
+      }
+    }
+  }
+
+  @Test 
+  public void testSimpleBreak () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.BreakTest$FieldBreaker",
+                                  "+vm.max_transition_length=1000000")) { 
+      int i = 42;
+      data = i; // we break after that
+      i = 0;
+
+    } else {
+      if (BreakListener.nCG != 2) { // that's really simplistic
+        fail("wrong number of CGs: " + BreakListener.nCG);
+      }
+    }
+  }
+
+
+  //--- test CG chain break
+
+  public static class FooCallBreaker extends BreakListener {
+    @Override
+       public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+
+      if (executedInsn instanceof JVMInvokeInstruction) { // break on method call
+        JVMInvokeInstruction call = (JVMInvokeInstruction) executedInsn;
+
+        if ("foo()V".equals(call.getInvokedMethodName())) {
+          System.out.println("# breaking & pruning after: " + executedInsn);
+          System.out.println("# registered (ignored) CG: " + ss.getNextChoiceGenerator());
+          ti.breakTransition("breakTest"); // not required since we ignore
+          ss.setIgnored(true);
+        }
+      }
+    }
+  }
+
+  void foo () {
+    System.out.println("foo");
+  }
+
+  void bar () {
+    System.out.println("bar");
+  }
+
+  @Test 
+  public void testDeepCGBreak () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.BreakTest$FooCallBreaker")) {
+      if (Verify.getBoolean(false)) {
+        System.out.println("foo,bar branch");
+        foo(); // listener sets it ignored -> break
+        bar();
+        fail("should not get here");
+
+      } else {
+        Verify.incrementCounter(0);
+
+        System.out.println("bar,foo branch");
+        bar();
+        foo(); // listener sets it ignored -> break
+        fail("should not get here");
+      }
+    }
+
+    if (!isJPFRun()){
+      assert Verify.getCounter(0) == 1;
+    }
+  }
+
+
+  //--- test ignore after setting nextCG
+
+  public static class VerifyNextIntBreaker extends BreakListener {
+    @Override
+       public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo ti, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+      
+      ChoiceGenerator<?> cg = ss.getNextChoiceGenerator();
+      if (cg.getId().equals("verifyGetInt(II)")) {
+        System.out.println("# breaking & pruning after: " + ti.getPC());
+        System.out.println("# registered (ignored) CG: " + cg);
+
+        ss.setIgnored(true); // should reset the IntIntervalCG registered by the native getInt()
+        ti.breakTransition("breakTest"); // should have no effect
+      }
+    }
+  }
+
+  @Test
+  public void testIgnoreAfterCG () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.BreakTest$VerifyNextIntBreaker")) {
+      if (Verify.getBoolean(false)){
+        System.out.println("true branch (should be first)");
+
+        int i = Verify.getInt(1, 2); // listener breaks & ignores post exec
+        fail("should never get here");
+
+      } else {
+        Verify.incrementCounter(0);
+
+        System.out.println("false branch");
+      }
+    }
+
+    if (!isJPFRun()){
+      assert Verify.getCounter(0) == 1;
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/CGNotificationTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/CGNotificationTest.java
new file mode 100644 (file)
index 0000000..c929497
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.SyncPolicy;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Verify;
+import gov.nasa.jpf.vm.choice.IntChoiceFromList;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+/**
+ * regression test for CG notifications
+ */
+public class CGNotificationTest extends TestJPF {
+
+  public static class Sequencer extends ListenerAdapter {
+
+    static ArrayList<String> sequence;
+
+    @Override
+    public void choiceGeneratorRegistered(VM vm, ChoiceGenerator<?> nextCG, ThreadInfo ti, Instruction executedInsn) {
+      System.out.println("# CG registered: " + nextCG);
+      sequence.add("registered " + nextCG.getId());
+
+      assert nextCG.hasMoreChoices();
+    }
+
+    @Override
+    public void choiceGeneratorSet(VM vm, ChoiceGenerator<?> newCG) {
+      System.out.println("# CG set:        " + newCG);
+      sequence.add("set " + newCG.getId());
+
+      assert newCG.hasMoreChoices();
+    }
+
+    @Override
+    public void choiceGeneratorAdvanced(VM vm, ChoiceGenerator<?> currentCG) {
+      System.out.println("#   CG advanced: " + currentCG);
+      sequence.add("advance " + currentCG.getId() + ' ' + currentCG.getNextChoice());
+    }
+
+    @Override
+    public void choiceGeneratorProcessed(VM vm, ChoiceGenerator<?> processedCG) {
+      System.out.println("# CG processed:  " + processedCG);
+      sequence.add("processed " + processedCG.getId());
+
+      assert !processedCG.hasMoreChoices();
+    }
+
+    @Override
+    public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction lastInsn){
+      SystemState ss = vm.getSystemState();
+
+      if (lastInsn instanceof EXECUTENATIVE) { // break on native method exec
+        EXECUTENATIVE exec = (EXECUTENATIVE) lastInsn;
+
+        if (exec.getExecutedMethodName().equals("getInt")){// this insn did create a CG
+          if (!ti.isFirstStepInsn()){
+
+            ChoiceGenerator<Integer> cg = new IntChoiceFromList("listenerCG", 3,4);
+            ss.setNextChoiceGenerator(cg);
+          }
+        }
+      }
+
+    }
+  }
+
+  @Test
+  public void testCGNotificationSequence () {
+    if (!isJPFRun()){
+      Sequencer.sequence = new ArrayList<String>();
+    }
+
+    // make sure max insn preemption does not interfere 
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.CGNotificationTest$Sequencer",
+                                  "+vm.max_transition_length=MAX")){
+      boolean b = Verify.getBoolean(); // first CG
+      int i = Verify.getInt(1,2); // this one gets a CG on top registered by the listener
+/*
+      System.out.print("b=");
+      System.out.print(b);
+      System.out.print(",i=");
+      System.out.println(i);
+*/
+    }
+
+    if (!isJPFRun()){
+      String[] expected = {
+        "registered " + SyncPolicy.ROOT,
+        "set " + SyncPolicy.ROOT,
+        "advance " + SyncPolicy.ROOT + " ThreadInfo [name=main,id=0,state=RUNNING]",
+        "registered verifyGetBoolean",
+        "set verifyGetBoolean",
+        "advance verifyGetBoolean false",
+        "registered verifyGetInt(II)",
+        "registered listenerCG",
+        "set verifyGetInt(II)",
+        "set listenerCG",
+        "advance verifyGetInt(II) 1",
+        "advance listenerCG 3",
+        "advance listenerCG 4",
+        "processed listenerCG",
+        "advance verifyGetInt(II) 2",
+        "advance listenerCG 3",
+        "advance listenerCG 4",
+        "processed listenerCG",
+        "processed verifyGetInt(II)",
+        "advance verifyGetBoolean true",
+        "registered verifyGetInt(II)",
+        "registered listenerCG",
+        "set verifyGetInt(II)",
+        "set listenerCG",
+        "advance verifyGetInt(II) 1",
+        "advance listenerCG 3",
+        "advance listenerCG 4",
+        "processed listenerCG",
+        "advance verifyGetInt(II) 2",
+        "advance listenerCG 3",
+        "advance listenerCG 4",
+        "processed listenerCG",
+        "processed verifyGetInt(II)",
+        "processed verifyGetBoolean",
+        "processed " + SyncPolicy.ROOT
+      };
+
+      assert Sequencer.sequence.size() == expected.length;
+
+      int i=0;
+      for (String s : Sequencer.sequence){
+        assert expected[i].equals(s) : "\"" + expected[i] + "\" != \"" + s + "\"";
+        //System.out.println("\"" + s + "\",");
+        i++;
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/CGRemoverTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/CGRemoverTest.java
new file mode 100644 (file)
index 0000000..fa99ccf
--- /dev/null
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import org.junit.Test;
+
+/**
+ * regression test for the CGRemover listener
+ */
+public class CGRemoverTest extends TestJPF {
+
+  static class R1 implements Runnable {
+    int data = 42;
+
+    public synchronized int getData() {
+      return data;
+    }
+
+    @Override
+       public void run() {
+      int r = getData();  // <<<< should not cause CG, line 45
+    }
+  }
+
+  public static class R1Listener extends ListenerAdapter {
+
+    @Override
+    public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG){
+      Instruction insn = newCG.getInsn();
+
+      if (insn instanceof JVMInvokeInstruction){
+        MethodInfo mi = ((JVMInvokeInstruction)insn).getInvokedMethod();
+        if (mi.getName().equals("getData")){
+          fail("CG should have been removed by CGRemover");
+        }
+      }
+    }
+  }
+
+  // WATCH IT - THIS IS FRAGILE WRT SOURCE LINES
+  @Test
+  public void testSyncLocation() {
+    if (verifyNoPropertyViolation("+listener=.listener.CGRemover,.test.mc.basic.CGRemoverTest$R1Listener",
+            "+log.info=gov.nasa.jpf.CGRemover",
+            "+cgrm.sync.cg_class=gov.nasa.jpf.vm.ThreadChoiceGenerator",
+            "+cgrm.sync.locations=CGRemoverTest.java:45,CGRemoverTest.java:75")){
+      R1 o = new R1();
+      Thread t = new Thread(o);
+      t.start();   // from now on 'o' is shared
+
+      int r = o.getData(); // <<< should not cause CG  , line 75
+    }
+  }
+
+
+  @Test
+  public void testSyncCall() {
+    if (verifyNoPropertyViolation("+listener=.listener.CGRemover,.test.mc.basic.CGRemoverTest$R1Listener",
+            "+log.info=gov.nasa.jpf.CGRemover",
+            "+cgrm.sync.cg_class=gov.nasa.jpf.vm.ThreadChoiceGenerator",
+            "+cgrm.sync.method_calls=gov.nasa.jpf.test.mc.basic.CGRemoverTest$R1.getData()")){
+      R1 o = new R1();
+      Thread t = new Thread(o);
+      t.start();   // from now on 'o' is shared
+
+      int r = o.getData(); // should not cause CG
+    }
+  }
+
+  @Test
+  public void testSyncBody() {
+    if (verifyNoPropertyViolation("+listener=.listener.CGRemover,.test.mc.basic.CGRemoverTest$R1Listener",
+            "+log.info=gov.nasa.jpf.CGRemover",
+            "+cgrm.sync.cg_class=gov.nasa.jpf.vm.ThreadChoiceGenerator",
+            "+cgrm.sync.method_bodies=gov.nasa.jpf.test.mc.basic.CGRemoverTest$R1.run(),gov.nasa.jpf.test.mc.basic.CGRemoverTest.testSyncBody()")){
+      R1 o = new R1();
+      Thread t = new Thread(o);
+      t.start();   // from now on 'o' is shared
+
+      int r = o.getData(); // should not cause CG
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/CGReorderTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/CGReorderTest.java
new file mode 100644 (file)
index 0000000..abff582
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.DoubleChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.IntChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.Verify;
+import gov.nasa.jpf.vm.choice.DoubleChoiceFromList;
+import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
+
+import java.util.Comparator;
+
+import org.junit.Test;
+
+/**
+ * regression test for choice reordering APIs 
+ */
+public class CGReorderTest extends TestJPF {
+
+  public static class ReverseListener extends ListenerAdapter {  
+    @Override
+    public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG){
+      if (newCG instanceof IntIntervalGenerator){
+        System.out.println("reverse choice enumeration order");
+        ((IntIntervalGenerator)newCG).reverse();
+      }
+    }
+
+    int lastVal = Integer.MAX_VALUE;
+    @Override
+    public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG){
+      if (currentCG instanceof IntIntervalGenerator){
+        int v = ((IntChoiceGenerator)currentCG).getNextChoice();
+        if (v >= lastVal){
+          fail("values not decreasing");
+        }
+        lastVal = v;
+      }
+    }
+  }
+  
+  @Test
+  public void testReverse(){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.CGReorderTest$ReverseListener")){
+      int x = Verify.getInt(0,4);
+      System.out.println(x);
+    }
+  }
+
+  
+  public static class ReorderListener extends ListenerAdapter {
+    ChoiceGenerator<?> reorderedCG;
+    
+    @Override
+    public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo ti, Instruction executedInsn){
+      // make sure we are not getting recursive (could also use setId())
+      if (nextCG instanceof DoubleChoiceFromList && nextCG != reorderedCG){ 
+        System.out.println("reorder choices");
+        reorderedCG = ((DoubleChoiceFromList)nextCG).reorder( new Comparator<Double>(){
+          @Override
+               public int compare (Double d1, Double d2){
+            return (int) (d2 - d1);
+          }
+        });
+        
+        System.out.println("replacing: " + nextCG);
+        System.out.println("with: " + reorderedCG);
+        SystemState ss = vm.getSystemState();
+        ss.removeNextChoiceGenerator();
+        ss.setNextChoiceGenerator(reorderedCG);
+      }
+    }
+
+    double lastVal = Double.MAX_VALUE;
+    @Override
+    public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG){
+      if (currentCG instanceof DoubleChoiceGenerator){
+        double v = ((DoubleChoiceGenerator)currentCG).getNextChoice();
+        if (v >= lastVal){
+          fail("values not decreasing");
+        }
+        lastVal = v;
+      }
+    }
+  }
+  
+  @Test
+  public void testReorder(){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.CGReorderTest$ReorderListener")){
+      double x = Verify.getDoubleFromList(1.0, 2.0, 3.0, 4.0);
+      System.out.println(x);
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/CascadedCGTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/CascadedCGTest.java
new file mode 100644 (file)
index 0000000..646116d
--- /dev/null
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.EXECUTENATIVE;
+import gov.nasa.jpf.jvm.bytecode.GETFIELD;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Verify;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+import gov.nasa.jpf.vm.choice.IntIntervalGenerator;
+
+import org.junit.Test;
+
+/**
+ * regression test for cascaded ChoiceGenerators
+ */
+public class CascadedCGTest extends TestJPF {
+
+  public static class IntChoiceCascader extends ListenerAdapter {
+    static int result;
+
+    @Override
+    public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+
+      if (executedInsn instanceof EXECUTENATIVE) { // break on native method exec
+        EXECUTENATIVE exec = (EXECUTENATIVE) executedInsn;
+
+        if (exec.getExecutedMethodName().equals("getInt")){// this insn did create a CG
+          if (!ti.isFirstStepInsn()){
+            result = 0;
+
+            IntIntervalGenerator cg = new IntIntervalGenerator("listenerCG", 3,4);
+            ss.setNextChoiceGenerator(cg);
+            System.out.println("# listener registered " + cg);
+
+          } else { // reexecution
+
+            ChoiceGenerator<?>[] curCGs = ss.getCurrentChoiceGenerators();
+            assert curCGs.length == 2;
+
+            IntIntervalGenerator cg = ss.getCurrentChoiceGenerator("listenerCG", IntIntervalGenerator.class);
+            assert cg != null : "no 'listenerCG' IntIntervalGenerator found";
+            int i = cg.getNextChoice();
+            System.out.println("# current listener CG choice: " + i);
+
+            cg = ss.getCurrentChoiceGenerator("verifyGetInt(II)", IntIntervalGenerator.class);
+            assert cg != null : "no 'verifyGetInt(II)' IntIntervalGenerator found";
+            int j = cg.getNextChoice();
+            System.out.println("# current insn CG choice: " + j);
+
+            result += i * j;
+          }
+        }
+      }
+    }
+  }
+
+  @Test
+  public void testCascadedIntIntervals () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.CascadedCGTest$IntChoiceCascader")){
+      int i = Verify.getInt( 1, 2);
+      System.out.print("i=");
+      System.out.println(i);
+    } else {
+      assert IntChoiceCascader.result == 21;
+    }
+  }
+
+
+  //--- mixed data and thread CG
+
+  // this listener replaces all GETFIELD "mySharedField" results with configured
+  // choice values (i.e. it is a simplified field Perturbator).
+  // The demo point is that it is not aware of that such GETFIELDs might also be
+  // scheduling points because of shared object field access, and it should work
+  // the same no matter if there also was a ThreadChoice/context switch or not
+
+  // NOTE: while the cascaded CG interface is easy to use (almost the same as the
+  // single CG interface), the context can be quite tricky because the cascaded
+  // CG (the scheduling point in this case) means the corresponding instruction
+  // is already rescheduled and might have been cut short in insn specific ways
+  // (in this case before pushing the field value on the operand stack). For this
+  // reason a simple ti.isFirstStepInsn() check is not sufficient. There might not
+  // have been a reschedule if there was only one thread, or even if this is the
+  // first step insn, the corresponding CG might have been not related to the
+  // getfield but some action in the preceeding thread (e.g. a terminate).
+  // In this case, the simple solution is based on that we want the data CG
+  // unconditionally, so we check if there is a corresponding current CG
+  // (which means this is not the first step insn)
+
+  public static class FieldAccessCascader extends ListenerAdapter {
+
+    @Override
+    public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      SystemState ss = vm.getSystemState();
+
+      if (executedInsn instanceof GETFIELD){
+        GETFIELD getInsn = (GETFIELD) executedInsn;
+        FieldInfo fi = getInsn.getFieldInfo();
+        if (fi.getName().equals("mySharedField")){
+
+          IntChoiceFromSet cg = ss.getCurrentChoiceGenerator("fieldReplace", IntChoiceFromSet.class);
+          if (cg == null){
+
+            // we might get here after a preceding rescheduling exec, i.e.
+            // partial execution (with successive re-execution), or after
+            // non-rescheduling exec has been completed (only one runnable thread).
+            // In the first case we have to restore the operand stack so that
+            // we can reexecute
+            if (!ti.willReExecuteInstruction()){
+              // restore old operand stack contents
+              StackFrame frame = ti.getModifiableTopFrame();
+
+              frame.pop();
+              frame.pushRef( getInsn.getLastThis());
+            }
+
+            cg = new IntChoiceFromSet("fieldReplace", 42, 43);
+            ss.setNextChoiceGenerator(cg);
+            ti.reExecuteInstruction();
+
+            System.out.println("# listener registered CG: " + cg);
+
+          } else {
+            StackFrame frame = ti.getModifiableTopFrame();
+
+            int v = cg.getNextChoice();
+            int n = frame.pop();
+            frame.push(v);
+
+            System.out.println("# listener replacing " + n + " with " + v);
+          }
+        }
+      }
+    }
+
+    //--- those are just for debugging purposes
+    @Override
+    public void stateBacktracked(Search search) {
+      System.out.println("#------ [" + search.getDepth() + "] backtrack: " + search.getStateId());
+    }
+    
+    @Override
+    public void stateAdvanced(Search search){
+      System.out.println("#------ " + search.getStateId() + " isNew: " + search.isNewState() + ", isEnd: " + search.isEndState());
+    }
+    
+    @Override
+    public void threadScheduled(VM vm, ThreadInfo ti){
+      System.out.println("# running thread: " + ti);
+    }
+    
+    @Override
+    public void threadTerminated(VM vm, ThreadInfo ti){
+      System.out.println("# terminated thread: " + ti);
+    }
+    
+    @Override
+    public void threadStarted(VM vm, ThreadInfo ti){
+      System.out.println("# started thread: " + ti);
+    }
+    
+    @Override
+    public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+      System.out.println("# choice: " + currentCG);
+    }
+  }
+
+  int mySharedField = -1;
+
+  @Test
+  public void testMixedThreadDataCGs () {
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.CascadedCGTest$FieldAccessCascader")){
+      Thread t = new Thread(){
+        @Override
+               public void run() {
+          int n = mySharedField;
+          System.out.print("<thread> mySharedField read: ");
+          System.out.println( n);
+          assert n == 42 || n == 43; // regardless of main thread exec state
+        }
+      };
+      t.start();
+
+      mySharedField = 7;
+      System.out.println("<main> mySharedField write: 7");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/ExceptionInjectorTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/ExceptionInjectorTest.java
new file mode 100644 (file)
index 0000000..9dbf822
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.Test;
+
+/**
+ * regression test for ExceptionInjector listener
+ */
+public class ExceptionInjectorTest extends TestJPF {
+  
+  @Test
+  public void testAbsLine () {
+    if (verifyNoPropertyViolation("+listener=.listener.ExceptionInjector",
+                                  "+ei.exception=java.lang.ArithmeticException@gov.nasa.jpf.test.mc.basic.ExceptionInjectorTest:41")){
+      boolean handled = false;
+      try {
+        int x = 10;
+        int y = 2;
+        int z = x / y;                    // <<<< perfectly fine, but we want it to blow up , line 41
+      } catch (ArithmeticException ax){   // ..so that we can check the handler
+        handled = true;
+        System.out.println("got it handled");
+        ax.printStackTrace();
+      }
+
+      assert handled : "failed to throw exception";
+    }
+  }
+
+  static class Zapp extends RuntimeException {
+    Zapp (String details){
+      super(details);
+    }
+  }
+
+  // NOTE - offsets count from the first statement line in the method body
+  @Test
+  public void testMethodOffset () {
+    if (verifyNoPropertyViolation("+listener=.listener.ExceptionInjector",
+                                  "+ei.exception=gov.nasa.jpf.test.mc.basic.ExceptionInjectorTest$Zapp(\"gotcha\")@gov.nasa.jpf.test.mc.basic.ExceptionInjectorTest.testMethodOffset():6")){
+      boolean handled = false;
+      try {
+        int x = 10;
+        int y = 2;
+        int z = x / y;    // <<<< method offset +6: perfectly fine, but we want it to blow up
+      } catch (Zapp x){   // ..so that we can check the handler
+        handled = true;
+        System.out.println(x);
+      }
+
+      assert handled : "failed to throw exception";
+    }
+  }
+
+  @Test
+  public void testCallee () {
+    if (verifyNoPropertyViolation("+listener=.listener.ExceptionInjector",
+                                  "+ei.exception=java.io.IOException@java.io.File.createTempFile(java.lang.String,java.lang.String)")){
+      boolean handled = false;
+      try {
+        File f = File.createTempFile("foo", "bar");
+      } catch (IOException x){  // if the temp file could not be created (how do you force this?)
+        handled = true;
+        x.printStackTrace();
+      }
+
+      assert handled : "failed to throw exception";
+    }
+
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/ExtendTransitionTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/ExtendTransitionTest.java
new file mode 100644 (file)
index 0000000..972ae39
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Verify;
+import org.junit.Test;
+
+/**
+ * regression test for extended transitions
+ */
+public class ExtendTransitionTest extends TestJPF {
+  
+  public static class CGListener extends ListenerAdapter {
+    @Override
+    public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {
+      System.out.println("CG registered: " + nextCG);
+    }
+
+    @Override
+    public void choiceGeneratorSet (VM vm, ChoiceGenerator<?> newCG) {
+      System.out.println("\nCG set: " + newCG + " by: " + newCG.getInsn());
+    }
+
+    @Override
+    public void choiceGeneratorAdvanced (VM vm, ChoiceGenerator<?> currentCG) {
+      System.out.println("CG advanced: " + currentCG);
+    }
+
+    @Override
+    public void choiceGeneratorProcessed (VM vm, ChoiceGenerator<?> processedCG) {
+      System.out.println("CG processed: " + processedCG);
+    }  
+    
+    @Override
+    public void stateAdvanced (Search search){
+      System.out.println("!!! state advanced - this should not happen");
+    }
+  }
+  
+  @Test
+  public void testExtendedStateTransitions(){
+    if (verifyNoPropertyViolation("+vm.extend_transitions=*", "+cg.break_single_choice=false", 
+            "+listener=" + getClass().getName() + "$CGListener")){
+      Verify.print("-- start\n");
+      for (int i=0; i<5; i++){
+        int n = Verify.breakTransition( "loop cycle", i, i);
+        Verify.print("i=", i);
+        Verify.print(", n=", n);
+        Verify.println();
+      }
+    }
+    
+    if (!isJPFRun()){
+      int nStates = VM.getVM().getStateCount();
+      System.out.println("nStates=" + nStates);
+      assertTrue(nStates == 0);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/FinalBreakTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/FinalBreakTest.java
new file mode 100644 (file)
index 0000000..200d4c5
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import java.lang.reflect.Field;
+import org.junit.Test;
+
+/**
+ * test transition breaks on finals
+ */
+public class FinalBreakTest extends TestJPF {
+
+
+  //--- breaks on instance finals
+  
+  static class InstanceFinal {
+    static InstanceFinal global;
+    
+    final int a;
+    final int b;
+    
+    InstanceFinal (){
+      Verify.println("T enter InstanceFinal ctor");
+      global = this; // leak this reference before construction
+      
+      a = 1;
+      b = 1;
+      Verify.println("T exit InstanceFinal ctor");
+    }
+  }
+  
+  void startInstanceFinal(){
+    Thread t = new Thread(new Runnable() {
+      @Override
+       public void run () {
+        Verify.println("T running");
+        InstanceFinal o = new InstanceFinal();
+        Thread.yield();
+        assertTrue( "constructed object corrupted", o.a == 1 && o.b == 1);
+        Verify.println("T terminating");
+      }
+    });
+    t.start();
+  }
+  
+  @Test
+  public void testNoFinalBreak(){
+   if (verifyNoPropertyViolation("+vm.shared.skip_finals=true")){
+     startInstanceFinal();
+     InstanceFinal o = InstanceFinal.global;
+     if (o != null){
+       assertTrue( "break between field inits", o.a == o.b);
+     }
+   }
+  }
+  
+  @Test
+  public void testFinalBreak(){
+   if (verifyAssertionError("+vm.shared.skip_finals=false")){
+     startInstanceFinal();
+     InstanceFinal o = InstanceFinal.global;
+     if (o != null){
+       assertTrue( "break between field inits", o.a == o.b);
+     }
+   }
+  }
+
+  @Test 
+  public void testNoConstructedFinalBreak(){
+    if (verifyNoPropertyViolation("+vm.shared.skip_constructed_finals=true")){
+      startInstanceFinal();
+      InstanceFinal o = InstanceFinal.global;
+      if (o != null){
+        try {
+          Field f = InstanceFinal.class.getField("a");
+          f.setAccessible(true);
+          
+          // those should not break
+          Verify.println("main now corrupting object");
+          f.setInt(o, 42);
+          Verify.println("main now fixing object");
+          f.setInt(o, 1);          
+          
+        } catch (Throwable x){
+          fail("unexpected exception: " + x);
+        }
+      }
+    }
+  }
+
+  @Test 
+  public void testConstructedFinalBreak(){
+    if (verifyAssertionError("+vm.shared.skip_finals=false", "+vm.shared.skip_constructed_finals=false")){
+      startInstanceFinal();
+      InstanceFinal o = InstanceFinal.global;
+      if (o != null){
+        try {
+          Field f = InstanceFinal.class.getField("a");
+          f.setAccessible(true);
+          
+          // those should not break
+          Verify.println("main now corrupting object");
+          f.setInt(o, 42);
+          //Thread.yield();  // not required if Field.set() properly breaks
+          Verify.println("main now fixing object");
+          f.setInt(o, 1);          
+          
+        } catch (Throwable x){
+          throw new RuntimeException("caught " + x);
+        }
+      }
+    }
+  }
+
+  
+  //--- breaks on static finals
+  
+  final static Object o1 = new Object();
+  final static Object o2 = new Object();
+  
+  static class StaticFinal {
+    final static Object a = o1;
+    final static Object b = o1;
+  }
+
+  void startStaticFinal(){
+    Thread t = new Thread(new Runnable() {
+      @Override
+       public void run () {
+        Verify.println("T running");
+        Thread.yield();
+        assertTrue( "static finals corrupted", StaticFinal.a == StaticFinal.b);
+        Verify.println("T terminating");
+      }
+    });
+    t.start();
+  }
+
+  @Test
+  public void testNoStaticFinalBreak(){
+    if (verifyNoPropertyViolation("+vm.shared.skip_static_finals=true")){
+      startStaticFinal();
+      try {
+        Field f = StaticFinal.class.getField("a");
+        f.setAccessible(true);
+        
+        // those should not break
+        Verify.println("main now corrupting static fields");
+        f.set(null, o2);
+        Verify.println("main now fixing static fields");
+        f.set(null, o1);
+        
+      } catch (Throwable x){
+          fail("unexpected exception: " + x);
+      }
+    }
+  }
+
+  
+  @Test
+  public void testStaticFinalBreak(){
+    if (verifyAssertionError("+vm.shared.skip_static_finals=false")){
+      startStaticFinal();
+      try {
+        Field f = StaticFinal.class.getField("a");
+        f.setAccessible(true);
+        
+        // those should  break
+        Verify.println("main now corrupting static fields");
+        f.set(null, o2);
+        Verify.println("main now fixing static fields");
+        f.set(null, o1);          
+        
+      } catch (Throwable x){
+          x.printStackTrace();
+          throw new RuntimeException("caught " + x);
+      }
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/FinalFieldChoiceTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/FinalFieldChoiceTest.java
new file mode 100644 (file)
index 0000000..7d8301f
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import org.junit.Test;
+
+/**
+ * test non-deterministic init of final fields
+ */
+public class FinalFieldChoiceTest extends TestJPF {
+
+  
+  //--- instance fields
+  
+  static class X {
+    final boolean a;
+    final boolean b;
+    
+    X(){
+      a = Verify.getBoolean();
+      b = Verify.getBoolean();
+    }
+  }
+  
+  @Test
+  public void testFinalInstanceFields(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+    
+    if (verifyNoPropertyViolation()){
+      X x = new X();
+      System.out.print("a=");
+      System.out.print(x.a);
+      System.out.print(", b=");
+      System.out.println(x.b);
+      
+      Verify.incrementCounter(0);
+      
+      int n = Verify.getCounter(1);
+      if (x.a && x.b) Verify.setCounter(1, n+3);
+      else if (x.a) Verify.setCounter(1, n+2);
+      else if (x.b) Verify.setCounter(1, n+1);
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( "wrong number of choices", Verify.getCounter(0) == 4);
+      assertTrue( "wrong choice values", Verify.getCounter(1) == 6);
+    }    
+  }
+  
+  //--- static fields
+  static class Y {
+    static final boolean a = Verify.getBoolean();
+    static final boolean b = Verify.getBoolean();
+  }
+  
+  @Test 
+  public void testFinalStaticFields(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+    
+    if (verifyNoPropertyViolation()){
+      boolean a = Y.a;
+      boolean b = Y.b;
+      
+      System.out.print("Y.a=");
+      System.out.print(a);
+      System.out.print(", Y.b=");
+      System.out.println(b);
+      
+      Verify.incrementCounter(0);
+      
+      int n = Verify.getCounter(1);
+      if (Y.a && Y.b) Verify.setCounter(1, n+3);
+      else if (Y.a) Verify.setCounter(1, n+2);
+      else if (Y.b) Verify.setCounter(1, n+1);
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( "wrong number of choices", Verify.getCounter(0) == 4);
+      assertTrue( "wrong choice values", Verify.getCounter(1) == 6);
+    }  
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/IdleLoopTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/IdleLoopTest.java
new file mode 100644 (file)
index 0000000..0d6ae09
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * JPF test driver for the IdleFilter listener
+ */
+public class IdleLoopTest extends TestJPF {
+
+  static final String LISTENER = "+listener=.listener.IdleFilter";
+
+  @Test public void testBreak () {
+    if (verifyNoPropertyViolation(LISTENER, "+idle.action=break", 
+                                  "+log.warning=gov.nasa.jpf.listener.IdleFilter",
+                                  "+vm.max_transition_length=MAX")) {
+      int y = 4;
+      int x = 0;
+
+      while (x != y) { // JPF should state match on the backjump
+        x = x + 1;
+        if (x > 3) {
+          x = 0;
+        }
+      }
+
+      assert false : "we should never get here";
+    }
+  }
+
+  @Test public void testPrune () {
+    if (verifyNoPropertyViolation(LISTENER, "+idle.action=prune",
+                                  "+log.warning=gov.nasa.jpf.listener.IdleFilter",
+                                  "+vm.max_transition_length=MAX")) {
+      int y = 4;
+      int x = 0;
+
+      int loopCount = 0;
+
+      while (x != y) { // JPF should prune on the backjump despite of changed 'loopCount'
+        loopCount++;
+        x = x + 1;
+        if (x > 3) {
+          x = 0;
+        }
+      }
+
+      assert false : "we should never get here";
+    }
+  }
+
+  @Test public void testJump () {
+    if (verifyNoPropertyViolation(LISTENER, "+idle.action=jump",
+                                  "+idle.max_backjumps=100",
+                                  "+log.warning=gov.nasa.jpf.listener.IdleFilter",
+                                  "+vm.max_transition_length=MAX")) {
+
+      for (int i=0; i<1000; i++){
+        assert i < 500 : "JPF failed to jump past idle loop";
+      }
+
+      System.out.println("Ok, jumped past loop");
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/InvokeListenerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/InvokeListenerTest.java
new file mode 100644 (file)
index 0000000..9998169
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.INVOKESTATIC;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.jvm.bytecode.VirtualInvocation;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import org.junit.Test;
+
+
+/**
+ * testing various aspects of listeners on INVOKE instructions
+ */
+public class InvokeListenerTest extends TestJPF {
+
+  //--- this is only used outside JPF execution
+  public static class Listener extends ListenerAdapter {
+
+    void checkArgs (ThreadInfo ti, Instruction insn, boolean isPostExec){
+      if (insn instanceof JVMInvokeInstruction){
+        JVMInvokeInstruction call = (JVMInvokeInstruction)insn;
+        MethodInfo mi = call.getInvokedMethod(ti);
+        String miSignature = mi.getUniqueName();
+        String mname = mi.getName();
+
+        if (miSignature.equals("testInstanceMethod(DI)D")){
+          Object[] args = call.getArgumentValues(ti);
+          ElementInfo ei = getTarget(ti,call);
+          log(mname, ei, args, isPostExec);
+          assert ((Double)args[0]) == 42.0;
+          assert ((Integer)args[1]) == 1;
+
+        } else if (miSignature.equals("testStaticMethod(I)I")){
+          Object[] args = call.getArgumentValues(ti);
+          ElementInfo ei = getTarget(ti,call);
+          log(mname, ei, args, isPostExec);
+          assert ((Integer)args[0]) == 42;
+
+        } else if (miSignature.equals("testNativeInstanceMethod(DI)D")){
+          Object[] args = call.getArgumentValues(ti);
+          ElementInfo ei = getTarget(ti,call);
+          log(mname, ei, args, isPostExec);
+          assert ((Double)args[0]) == 42.0;
+          assert ((Integer)args[1]) == 1;
+
+        }
+      }
+    }
+
+    ElementInfo getTarget (ThreadInfo ti, JVMInvokeInstruction call){
+      if (call instanceof VirtualInvocation){
+        int objRef = ((VirtualInvocation)call).getCalleeThis(ti);
+        return ti.getElementInfo(objRef);
+      } else if (call instanceof INVOKESTATIC){
+        return ((INVOKESTATIC)call).getInvokedMethod().getClassInfo().getStaticElementInfo();
+      } else {
+        return null;
+      }
+    }
+
+    void log (String mname, ElementInfo ei, Object[] args, boolean isPostExec){
+      System.out.print(isPostExec ? "# instructionExecuted: " : "# executeInstruction: ");
+
+      System.out.print(ei);
+      System.out.print('.');
+      System.out.print(mname);
+
+      System.out.print(" (");
+      for (int i=0; i<args.length; i++) {
+        if (i >0) System.out.print(',');
+        System.out.print( args[i]);
+      }
+      System.out.println(")");
+    }
+
+    @Override
+    public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+      checkArgs(ti, insnToExecute, false);
+    }
+
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      checkArgs(ti, executedInsn, true);
+    }
+
+  }
+
+  double testInstanceMethod (double d, int c){
+    return d + c;
+  }
+  @Test public void testInstanceMethod (){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.InvokeListenerTest$Listener")){
+      testInstanceMethod(42.0, 1);
+    }
+  }
+  
+  int testStaticMethod (int a){
+    return a + 1;
+  }
+  @Test public void testStaticMethod (){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.InvokeListenerTest$Listener")){
+      testStaticMethod(42);
+    }
+  }
+
+  native double testNativeInstanceMethod (double d, int c);
+  @Test public void testNativeInstanceMethod (){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.InvokeListenerTest$Listener")){
+      testNativeInstanceMethod(42.0, 1);
+    }
+  }
+
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_AttrsTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_AttrsTest.java
new file mode 100644 (file)
index 0000000..49aef02
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+public class JPF_gov_nasa_jpf_test_mc_basic_AttrsTest extends NativePeer {
+
+  @MJI
+  public double goNative__DI__D (MJIEnv env, int objRef, double d, int i) {
+
+    Object[] attrs = env.getArgAttributes();
+
+    if ((attrs[0] != null) && (attrs[0] instanceof Integer) &&
+        (attrs[1] != null) && (attrs[1] instanceof Integer) &&
+        (attrs[2] != null) && (attrs[2] instanceof Integer)) {
+      Integer ra = (Integer)attrs[0] + (Integer)attrs[1] + (Integer)attrs[2];
+      env.setReturnAttribute(ra);
+    }
+
+    return d * i;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_InvokeListenerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_InvokeListenerTest.java
new file mode 100644 (file)
index 0000000..85286a3
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for TestInvokeListener tests
+ */
+public class JPF_gov_nasa_jpf_test_mc_basic_InvokeListenerTest extends NativePeer {
+
+  @MJI
+  public double testNativeInstanceMethod__DI__D (MJIEnv env, int objref, double d, int c){
+    return d+c;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_NoJPFExecTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_NoJPFExecTest.java
new file mode 100644 (file)
index 0000000..d4f946d
--- /dev/null
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * native peer for NoJPFExecTest
+ */
+public class JPF_gov_nasa_jpf_test_mc_basic_NoJPFExecTest extends NativePeer {
+
+  @MJI
+  public void bar____V (MJIEnv env, int objRef){
+    System.out.println("this is the bar____V() method intercepted by the native peer");
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_RestorerTest$X.java b/src/tests/gov/nasa/jpf/test/mc/basic/JPF_gov_nasa_jpf_test_mc_basic_RestorerTest$X.java
new file mode 100644 (file)
index 0000000..66ae190
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClosedMemento;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * peer for the regression test for on-demand state restoration by means of
+ * ClosedMementos
+ */
+public class JPF_gov_nasa_jpf_test_mc_basic_RestorerTest$X extends NativePeer {
+
+  static class InsnExecCount {
+    int count;    
+  }
+  
+  static class InsnCountRestorer implements ClosedMemento {
+    InsnExecCount insnAttr;
+    int count; // the value to restore
+    
+    InsnCountRestorer (InsnExecCount insnAttr){
+      this.insnAttr = insnAttr;
+      this.count = insnAttr.count;
+      
+      System.out.println("## storing: " + count);
+    }
+    
+    @Override
+       public void restore(){
+      System.out.println("## restoring: " + count);
+      insnAttr.count = count;
+    }
+  }
+
+  @MJI
+  public void $init (MJIEnv env, int objref){
+    ThreadInfo ti = env.getThreadInfo();
+    StackFrame caller = ti.getCallerStackFrame();
+    Instruction insn = caller.getPC();
+        
+    InsnExecCount a = insn.getAttr(InsnExecCount.class);
+    if (a == null){
+      a = new InsnExecCount();
+      insn.addAttr( a);
+    }
+
+    SystemState ss = env.getSystemState();
+    if (!ss.hasRestorer(a)){
+      env.getSystemState().putRestorer( a, new InsnCountRestorer(a));      
+    }
+    
+    a.count++;
+    env.setIntField(objref, "id", a.count);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/LocalVarInfoTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/LocalVarInfoTest.java
new file mode 100644 (file)
index 0000000..8c1b145
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.JVMLocalVariableInstruction;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+
+/**
+ * regression test for LocalVarInfo lookup
+ */
+public class LocalVarInfoTest extends TestJPF {
+  
+  
+  public static class TestLookupListener extends ListenerAdapter{
+    
+    MethodInfo logMethod = null;
+    static ArrayList<String> log;
+
+    public TestLookupListener(){
+      log = new ArrayList<String>();
+    }
+
+    @Override
+    public void methodEntered (VM vm, ThreadInfo ti, MethodInfo mi){
+      if (mi.getUniqueName().equals("testLookup()V")){
+        logMethod = mi;
+        System.out.println("---- " + mi.getUniqueName() + " entered");
+        System.out.println(" LocalVarInfos (should have {'this', 'x', 'y'} : ");
+        LocalVarInfo[] lvs = mi.getLocalVars(); 
+        for (LocalVarInfo lv : lvs){
+          System.out.println("    " + lv);
+        }
+        System.out.println();
+        
+        assertTrue( lvs.length == 3);
+      }
+    }
+
+    @Override
+    public void methodExited (VM vm, ThreadInfo ti, MethodInfo mi){
+      if (mi == logMethod){
+        logMethod = null;
+      }      
+    }
+
+    @Override
+    public void instructionExecuted(VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn){
+      if (executedInsn.getMethodInfo() == logMethod){
+        System.out.printf(" %2d: %s", executedInsn.getPosition(), executedInsn);
+        if (executedInsn instanceof JVMLocalVariableInstruction){
+          JVMLocalVariableInstruction lvinsn = (JVMLocalVariableInstruction)executedInsn;
+          LocalVarInfo lv = lvinsn.getLocalVarInfo(); 
+          System.out.print(" : " + lv);
+
+          log.add( executedInsn.getClass().getSimpleName() + " " + lv.getName());
+        }
+        System.out.println();
+      }
+    }
+  }
+  
+  static String[] expected = {
+    "ALOAD this",
+    "ISTORE x",
+    "ILOAD x",
+    "ISTORE y",
+    "ILOAD y"
+  };
+  
+  @Test
+  public void testLookup (){
+    if (verifyNoPropertyViolation("+listener=.test.mc.basic.LocalVarInfoTest$TestLookupListener")){
+      // DON'T CHANGE THIS CODE!
+      // this should be a sequence of 
+      
+      //  aload this
+      //  ..
+      //  istore x
+      //  iload x 
+      //  istore y
+      //  ..
+      //  iload y
+      //  ..
+      
+      int x = 42;
+      int y = x;
+      System.out.println(y);
+    }
+    
+    if (!isJPFRun()){
+      checkLog();
+    }
+  }
+  
+  private void checkLog(){
+    System.out.println("--- local var access log: ");
+    int i = 0;
+    assertTrue(TestLookupListener.log.size() == expected.length);
+    for (String s : TestLookupListener.log) {
+      System.out.println(s);
+      assertTrue(s.equals(expected[i++]));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/MethodListenerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/MethodListenerTest.java
new file mode 100644 (file)
index 0000000..dfd457d
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+/**
+ * test of the VMListener method notifications
+ */
+public class MethodListenerTest extends TestJPF {
+
+  // avoid loading JPF classes when running under JPF (specify classnames explicitly)
+  static String CLSNAME = "gov.nasa.jpf.test.mc.basic.MethodListenerTest";
+  static String LISTENER = "+listener=gov.nasa.jpf.test.mc.basic.MethodListenerTest$Listener";
+
+  public static class Listener extends ListenerAdapter {
+    String startMthName;
+
+    boolean traceActive = false;
+    int level;
+
+    public Listener (Config config){
+      startMthName = config.getString("_start");
+    }
+
+    String levelPrefix (int lvl){
+      String prefix = "";
+      for (int i=0; i<lvl; i++){
+        prefix += "  ";
+      }
+      return prefix;
+    }
+
+    @Override
+    public void searchStarted(Search search) {
+      trace.clear();
+    }
+
+    @Override
+    public void methodEntered (VM vm, ThreadInfo ti, MethodInfo mi){
+      assertSame(mi, ThreadInfo.getCurrentThread().getTopFrameMethodInfo());
+
+      if (CLSNAME.equals(mi.getClassName())){
+        String mthName = mi.getName();
+        if (mthName.equals(startMthName)) {
+          traceActive = true;
+          level=0;
+        }
+
+        if (traceActive){
+          String prefix = levelPrefix(level);
+          trace.add(prefix + "> " + mthName);
+
+          System.out.println(prefix + "> " + mthName);
+
+          level++;
+        }
+      }
+    }
+
+    @Override
+    public void methodExited (VM vm, ThreadInfo ti, MethodInfo mi){
+      if (traceActive){
+        assertSame(mi, ThreadInfo.getCurrentThread().getTopFrameMethodInfo());
+        
+        if (CLSNAME.equals(mi.getClassName())){
+          level--;
+
+          String prefix = levelPrefix(level);
+          trace.add(prefix + "< " + mi.getName());
+
+          System.out.println(prefix + "< " + mi.getName());
+
+          if (level == 0){
+            traceActive = false;
+          }
+        }
+
+      }
+    }
+
+    @Override
+    public void exceptionThrown (VM vm, ThreadInfo ti, ElementInfo ei){
+      if (traceActive){
+        String xCls = ei.getClassInfo().getName();
+        trace.add("X " + xCls);
+        System.out.println("X " + xCls);
+      }
+    }
+  }
+
+  static ArrayList<String> trace = new ArrayList<String>();
+
+  static boolean traceEquals(String... expected){
+    if (expected.length != trace.size()){
+      System.err.println("wrong trace size, found: " + trace.size() + ", expected: " + expected.length);
+      return false;
+    }
+
+    int i = 0;
+    for (String s : trace){
+      if (!s.equals(expected[i])){
+        System.err.println("wrong trace entry, found: " + s + ", expected: " + expected[i]);
+        return false;
+      }
+      i++;
+    }
+    return true;
+  }
+
+  //--- internal test stuff
+  void foo(){
+    bar();
+  }
+
+  int bar(){
+    return 24;
+  }
+
+  void baz (){
+    blowUp();
+  }
+
+  void blowUp() {
+    throw new RuntimeException("I blow up");
+  }
+  
+  void time() {
+    System.currentTimeMillis();
+  }
+
+  //--- test methods
+  @Test public void testBasicInvocation() {
+    if (verifyNoPropertyViolation(LISTENER, "+_start=testBasicInvocation")){
+      foo();
+      
+    } else {
+      assertTrue(traceEquals(
+              "> testBasicInvocation",
+              "  > foo",
+              "    > bar",
+              "    < bar",
+              "  < foo",
+              "< testBasicInvocation"));
+    }
+  }
+
+  @Test public void testException() {
+    if (verifyNoPropertyViolation(LISTENER, "+_start=testException")){
+      try {
+        baz();
+
+      } catch (RuntimeException x){
+      }
+
+    } else {
+      assertTrue(traceEquals(
+              "> testException",
+              "  > baz",
+              "    > blowUp",
+              "X java.lang.RuntimeException",
+              "    < blowUp",
+              "  < baz",
+              "< testException"));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/NoJPFExecTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/NoJPFExecTest.java
new file mode 100644 (file)
index 0000000..133d85d
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.annotation.NoJPFExecution;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.util.TypeRef;
+import org.junit.Test;
+
+/**
+ * test for NoJPFExecution annotations
+ */
+public class NoJPFExecTest extends TestJPF {
+
+  @NoJPFExecution
+  protected void foo(){
+    System.out.println("!! foo() should not be executed under JPF");
+  }
+
+  @Test
+  public void testNoJPFExec(){
+    if (verifyJPFException( new TypeRef("gov.nasa.jpf.JPFException"))){
+      foo();
+    }
+  }
+
+
+  @NoJPFExecution
+  protected void bar(){
+    System.out.println("!! bar() bytecode should not be executed under JPF");
+  }
+
+  @Test
+  public void testInterceptedNoJPFExec(){
+    if (verifyNoPropertyViolation()){
+      bar();
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/NullTrackerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/NullTrackerTest.java
new file mode 100644 (file)
index 0000000..22432fe
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.util.HashMap;
+import org.junit.Test;
+
+/**
+ * regression test for NullTracker.
+ * 
+ * Well, not really a regression test since NullTracker only prints out reports, but at least
+ * we can see if it has errors while running
+ */
+public class NullTrackerTest extends TestJPF {
+  
+  static class TestObject {
+    String d;
+  
+    TestObject(){
+      // nothing, we forget to init d;
+    }
+    
+    TestObject (String d){
+      this.d = d;
+    }
+    
+    int getDLength(){
+      return d.length();
+    }
+    
+    void foo(){
+      // nothing
+    }
+  }
+
+  TestObject o;
+  
+  TestObject getTestObject (){
+    return null;
+  }
+  
+  void accessReturnedObject (){
+    TestObject o = getTestObject();
+    System.out.println("now accessing testObject");
+    String d = o.d; // that will NPE
+  }
+  
+  void accessObject (TestObject o){
+    System.out.println("now accessing testObject");
+    String d = o.d; // that will NPE    
+  }
+  
+  void createAndAccessObject(){
+    TestObject o = getTestObject();
+    accessObject(o);
+  }
+  
+  
+  @Test
+  public void testGetAfterIntraMethodReturn (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      accessReturnedObject();
+    }
+  }
+  
+  @Test
+  public void testGetAfterInterMethodReturn (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      createAndAccessObject();
+    }
+  }
+
+  @Test
+  public void testGetAfterIntraPut (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      o = null; // the null source
+      
+      String d = o.d; // causes the NPE
+    }    
+  }
+  
+  @Test
+  public void testCallAfterIntraPut (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      o = null; // the null source
+      
+      o.foo(); // causes the NPE
+    }    
+  }
+
+  @Test
+  public void testGetAfterASTORE (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      TestObject myObj = null; // the null source
+      
+      myObj.foo(); // causes the NPE
+    }    
+  }
+
+  
+  HashMap<String,TestObject> map = new HashMap<String,TestObject>();
+  
+  TestObject lookupTestObject (String name){
+    return map.get(name);
+  }
+  
+  @Test
+  public void testHashMapGet (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      TestObject o = lookupTestObject("FooBar");
+      o.foo();
+    }
+  }
+  
+  //------------------------------------------------------------------
+    
+  TestObject createTestObject (){
+    return new TestObject();
+  }
+  
+  
+  TestObject createTestObject (String d){
+    return new TestObject(d);
+  }
+  
+  @Test
+  public void testMissingCtorInit (){
+    if (verifyUnhandledException("java.lang.NullPointerException", "+listener=.listener.NullTracker")){
+      TestObject o = createTestObject("blah");
+      int len = o.getDLength(); // that should be fine
+      
+      o = createTestObject();
+      len = o.getDLength(); // that should NPE and report the default ctor as culprit
+    }    
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/OOMEInjectorTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/OOMEInjectorTest.java
new file mode 100644 (file)
index 0000000..3d4aac7
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+public class OOMEInjectorTest extends TestJPF {
+
+  @Test
+  public void testDirectLoc () {
+    if (verifyUnhandledException("java.lang.OutOfMemoryError", "+listener=.listener.OOMEInjector",
+                                  "+oome.locations=OOMEInjectorTest.java:32")){
+      Object o = new Integer(42);
+    }
+  }
+  
+  
+  static int bar(int y){
+    Integer res = new Integer(y);  // this should fail
+    return res;
+  }
+  
+  static int foo (int x){
+    int res = x + bar(42);
+    return res;
+  }
+  
+  @Test
+  public void testScope(){
+    if (verifyUnhandledException("java.lang.OutOfMemoryError", "+listener=.listener.OOMEInjector",
+        "+oome.locations=OOMEInjectorTest.java:52-53")){
+      bar(4200); // this should be Ok
+      int res = foo(42);  // this should cause an OOME
+      System.out.println("should never get here!");
+    }    
+  }
+  
+  static class DontAllocateMe {}
+  static class X extends DontAllocateMe {}
+  
+  @Test
+  public void testType(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation("+listener=.listener.OOMEInjector",
+        "+oome.types=*DontAllocateMe+")){
+      try {
+        X x = new X(); // that should trip an OOME
+      } catch (OutOfMemoryError oome){
+        oome.printStackTrace();
+        Verify.incrementCounter(0);
+      }
+    }
+    
+    if (!isJPFRun()){
+      assertEquals(1, Verify.getCounter(0));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/OVHeapTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/OVHeapTest.java
new file mode 100644 (file)
index 0000000..55fc025
--- /dev/null
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+/**
+ * unit test for OVHeap
+ */
+public class OVHeapTest extends TestJPF {
+  
+  static int getReferenceValue (Object o) {
+    int h = System.identityHashCode(o);
+    return h ^ 0xABCD;  // revert the ABCD hash component
+  }
+
+  static void checkRef (String msg, String key, int ref) {
+    System.out.printf("%s ,object: %s, ref: %d", msg, key, ref);
+    
+    int v = Verify.getValue(key);
+    if (v == Verify.NO_VALUE) {
+      Verify.putValue(key, ref);
+      System.out.println(" new");
+    } else {
+      if (v == ref) {
+        System.out.println(" seen");
+      } else {
+        fail("different reference values, had:" + v + ", new:" + ref);
+      }
+    }
+  }
+  
+  static class X {
+    String id;
+    
+    X (String id){
+      this.id = id;
+    }
+  }
+  
+  static class Y extends X {
+    Y (String id){
+      super(id);
+    }
+  }
+  
+  X allocX (String id) {
+    return new X(id);
+  }
+    
+  @Test
+  public void testSGOIDs() {
+    if (verifyNoPropertyViolation("+vm.heap.class=.vm.OVHeap")) {
+      Thread t = new Thread() {
+        @Override
+               public void run() {
+          Class<?> cls = X.class;
+          checkRef("from T ", "X.class", getReferenceValue(cls));
+          
+          X x1 = new X("t-x1");
+          checkRef("from T ", x1.id, getReferenceValue(x1));
+          
+          Thread.yield(); // CG #3
+          
+          Y y1 = new Y("t-y1");
+          checkRef("from T ", y1.id, getReferenceValue(y1));
+        }
+      };
+      
+      t.start();  // CG #1
+      
+      Class<?> clsY = Y.class;
+      checkRef("from M ", "Y.class", getReferenceValue(clsY));
+      
+      Class<?> clsX = X.class;
+      checkRef("from M ", "X.class", getReferenceValue(clsX));
+      
+      int n = Verify.getInt(1, 3); // CG #2
+      System.out.println("-- M next X[] arraysize = " + n);
+      X[] xs = new X[n];
+      for (int i=0; i<xs.length; i++) {
+        xs[i] = new X("xs-" + i);
+        checkRef("from M ", xs[i].id, getReferenceValue(xs[i]));
+      }
+      
+      Y y1 = new Y("m-y1");
+      checkRef("from M ", y1.id, getReferenceValue(y1));
+      
+      X x1 = new Y("m-x1");
+      checkRef("from M ", x1.id, getReferenceValue(x1));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/RecursiveLockTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/RecursiveLockTest.java
new file mode 100644 (file)
index 0000000..56fe787
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * Ensures that a recursive lock/unlock doesn't leave the lock in an acquired state.
+ */
+public class RecursiveLockTest extends TestJPF
+{
+   @Test
+   public void recursiveUnlock() throws InterruptedException
+   {
+      final Object lock;
+      Thread thread;
+      Runnable task;
+      
+      if (verifyNoPropertyViolation())
+      {
+         lock = new Object();
+         
+         synchronized (lock)
+         {
+            synchronized (lock)
+            {
+               // nothing to do
+            }
+         }
+         
+         task = new Runnable()
+         {
+            @Override
+                       public void run()
+            {
+               synchronized (lock)
+               {
+                  // nothing to do
+               }
+            }  
+         };
+         
+         thread = new Thread(task);
+         
+         thread.start();
+         thread.join();
+      }
+   }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/RestorerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/RestorerTest.java
new file mode 100644 (file)
index 0000000..a235226
--- /dev/null
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+/**
+ * regression test for on-demand state restoration by means of
+ * ClosedMementos
+ */
+public class RestorerTest extends TestJPF {
+
+  static class X {
+    int id;
+    public void whoami() {
+      System.out.print("I am X #");
+      System.out.println(id);
+    }
+  }
+  
+  @Test
+  public void testRestoredInsnCount (){
+    if (verifyNoPropertyViolation()){
+      
+      boolean b = Verify.getBoolean();
+      System.out.println( "--- 1. CG: " + b);
+      
+      for (int i=1; i<=5; i++){
+        X x = new X();
+        x.whoami();
+        assert x.id == i;
+      }
+
+      b = Verify.getBoolean();
+      System.out.println( "--- 2. CG: " + b);
+      
+      X x = new X();
+      x.whoami();
+      assert x.id == 1; // different location, so we restart with 1
+    }
+  }
+  
+  @Test
+  public void testRestoredInsnCountBFS (){
+    if (verifyNoPropertyViolation("+search.class=.search.heuristic.BFSHeuristic")){
+      
+      boolean b = Verify.getBoolean();
+      System.out.println( "--- 1. CG: " + b);
+      
+      for (int i=1; i<=5; i++){
+        X x = new X();
+        x.whoami();
+        assert x.id == i;
+      }
+
+      b = Verify.getBoolean();
+      System.out.println( "--- 2. CG: " + b);
+      
+      X x = new X();
+      x.whoami();
+      assert x.id == 1; // different location, so we restart with 1
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/SearchMultipleTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/SearchMultipleTest.java
new file mode 100644 (file)
index 0000000..deca2e4
--- /dev/null
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for search.multiple_errors test
+ */
+public class SearchMultipleTest extends TestJPF {
+
+  @Test
+  public void testSimple() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyAssertionError("+search.multiple_errors")){
+      boolean b = Verify.getBoolean();
+      System.out.println("## b = " + b);
+
+      Verify.incrementCounter(0);
+      
+      assert false : "blow up here";
+
+      fail("should never get here");
+    }
+
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+    }
+  }
+
+  @Test
+  public void testSimpleBFS() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyAssertionError("+search.multiple_errors", "+search.class=.search.heuristic.BFSHeuristic")){
+      boolean b = Verify.getBoolean();
+      System.out.println("## b = " + b);
+
+      Verify.incrementCounter(0);
+
+      assert false : "blow up here";
+    }
+
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+    }
+  }
+
+  @Test
+  public void testDeadlock(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyDeadlock("+search.multiple_errors", "+cg.boolean.false_first")){
+      Object lock = new Object();
+      boolean b = Verify.getBoolean();
+      boolean c = Verify.getBoolean();
+      System.out.println("b=" + b + ", c=" + c);
+
+      if (!b){
+        synchronized(lock){
+          try {
+            System.out.println("now deadlocking");
+            lock.wait(); // this should always deadlock
+          } catch (InterruptedException ix){
+            System.out.println("got interrupted");
+          }
+        }
+      }
+
+      System.out.println("should get here for b=true");
+      Verify.incrementCounter(0);
+    }
+
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/SharedPropagationTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/SharedPropagationTest.java
new file mode 100644 (file)
index 0000000..cbd3cac
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * various shared object propagations that could lead to missed paths
+ */
+public class SharedPropagationTest extends TestJPF {
+
+  static class Gotcha extends RuntimeException {
+    // nothing in here
+  }
+  
+  //--- simple local ref 
+  
+  static class T1 extends Thread {
+
+    static class X {
+
+      boolean pass;
+    }
+    X myX; // initially not set
+
+    public static void main(String[] args) {
+      T1 t = new T1();
+      t.start();
+
+      X x = new X();
+      t.myX = x;        // (0) x not shared until this GOT executed
+
+      //Thread.yield();  // this would expose the error
+      x.pass = true;     // (1) need to break BEFORE assignment or no error
+    }
+
+    @Override
+       public void run() {
+      if (myX != null) {
+        if (!myX.pass) {  // (2) won't fail unless main is between (0) and (1)
+          throw new Gotcha();
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testLocalRef(){
+    if (verifyUnhandledException( Gotcha.class.getName(), "+vm.scheduler.sharedness.class=.vm.GlobalSharednessPolicy")){
+      T1.main(new String[0]);
+    }
+  }
+  
+  
+  //--- one reference level down
+  
+  static class T2 extends Thread {
+
+    static class X {
+      boolean pass;
+    }
+
+    static class Y {
+      X x;
+    }
+    
+    Y y;
+
+    public static void main(String[] args) {
+      T2 t = new T2();
+      Y y = new Y();
+      X x = new X();
+
+      y.x = x;
+      // neither x nor y  shared at this point
+
+      t.start();
+      t.y = y; // y becomes shared, and with it x
+
+      x.pass = true;
+    }
+
+    @Override
+       public void run() {
+      if (y != null) {
+        if (!y.x.pass) {
+          throw new Gotcha();
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testLevel1Ref(){
+    if (verifyUnhandledException(Gotcha.class.getName())){
+      T2.main(new String[0]);
+    }
+  }
+
+  //--- propagation via static field
+  
+  static class T3 extends Thread {
+
+    static class X {
+      boolean pass;
+    }
+
+    static class Y {
+      X x;
+    }
+    static Y globalY; // initially not set
+
+    
+    public static void main(String[] args) {
+      T3 t = new T3();
+      t.start();
+
+      X x = new X();
+      Y y = new Y();
+      y.x = x;
+
+      globalY = y;           // (0) x not shared until this GOT executed
+
+      //Thread.yield();  // this would expose the error
+      x.pass = true;     // (1) need to break BEFORE assignment or no error
+    }
+
+    @Override
+       public void run() {
+      if (globalY != null) {
+        if (!globalY.x.pass) {  // (2) won't fail unless main is between (0) and (1)
+          throw new Gotcha();
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testStaticFieldPropagation(){
+    if (verifyUnhandledException(Gotcha.class.getName(), "+vm.scheduler.sharedness.class=.vm.GlobalSharednessPolicy")){
+      T3.main(new String[0]);
+    }
+  }
+  
+  
+  //--- the infamous Hyber example
+  
+  static class Hyber {
+    private static Timeout thread = new Timeout();
+
+    public static void main(String[] args) {
+      thread.start();
+      Timeout.Entry timer = thread.setTimeout(); // (0)
+      //Thread.yield();    // this forces the error
+      timer.hyber = true;  // (1) we need to break here to catch the error
+    }
+  }
+
+  static class Timeout extends Thread {
+
+    static class Entry {
+      boolean hyber = false;
+      Entry next = null;
+      Entry prev = null;
+    }
+    Entry e = new Entry();
+
+    Timeout() {
+      e.next = e.prev = e;
+    }
+
+    public Entry setTimeout() {
+      Entry entry = new Entry();
+      synchronized (e) {
+        entry.next = e;
+        entry.prev = e.prev;
+        entry.prev.next = entry;
+        entry.next.prev = entry;
+      }
+
+      return entry;
+    }
+
+    @Override
+       public void run() {
+      synchronized (e) {
+        for (Entry entry = e.next; entry != e; entry = entry.next) {
+          if (!entry.hyber) { // (2) only fails if main thread between (0) and (1)
+            throw new Gotcha();
+          }
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testHyber() {
+    if (verifyUnhandledException(Gotcha.class.getName(), "+vm.scheduler.sharedness.class=.vm.GlobalSharednessPolicy")){
+      Hyber.main(new String[0]);
+    }    
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/SharedRefTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/SharedRefTest.java
new file mode 100644 (file)
index 0000000..8448f29
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * test case for the shared object attribute detection, which is required by POR
+ * NOTE: these test cases only make sense when executed under JPF, since they
+ * depend on race conditions that are most likely not experienced when running on
+ * a normal VM
+ *
+ */
+public class SharedRefTest extends TestJPF implements Runnable {
+  
+  static class SharedOrNot {
+    boolean changed;
+  }
+  
+  SharedOrNot o;
+
+  public SharedRefTest () {
+    // only for JUnit
+  }
+
+  SharedRefTest (SharedOrNot o) {
+    // don't make this public or JUnit will choke
+    this.o = o;
+  }
+  
+  @Override
+  public void run () {
+    boolean b = o.changed;
+    o.changed = !b;
+    assert o.changed != b : "Argh, data race for o";
+  }
+  
+  /**
+   * this on should produce an AssertionError under JPF
+   */
+  @Test public void testShared () {
+    if (verifyAssertionError()) {
+      SharedOrNot s = new SharedOrNot();
+
+      Thread t1 = new Thread(new SharedRefTest(s));
+      Thread t2 = new Thread(new SharedRefTest(s));
+
+      t1.start();
+      t2.start();
+    }
+  }
+  
+  /**
+   * and this one shouldn't
+   */
+  @Test public void testNonShared () {
+    if (verifyNoPropertyViolation()) {
+      SharedOrNot s = new SharedOrNot();
+      Thread t1 = new Thread(new SharedRefTest(s));
+
+      s = new SharedOrNot();
+      Thread t2 = new Thread(new SharedRefTest(s));
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  static SharedRefTest rStatic = new SharedRefTest( new SharedOrNot());
+  
+  @Test
+  public void testSharedStaticRoot () {
+    if (verifyAssertionError()) {
+      Thread t = new Thread(rStatic);
+
+      t.start();
+
+      rStatic.o.changed = false; // why wouldn't 'true' trigger an assertion :)
+    }
+  }
+  
+  //--- test explicit sharedness management
+  
+  static class Global {
+    public static Global x = new Global();
+   
+    int d;
+  }
+  
+  @Test
+  public void testShareControl () {
+    if (verifyNoPropertyViolation()) {
+      Verify.setShared( Global.class, false);
+      Verify.freezeSharedness( Global.class, true);
+
+      Verify.setShared( Global.x, false);
+      Verify.freezeSharedness( Global.x, true);
+      
+      // now references to Global.x should not break anymore
+     
+      Thread t = new Thread() {
+        @Override
+               public void run() {
+          Verify.println("T inc");
+          Global.x.d++;
+          Verify.println("T dec");
+          Global.x.d--;
+          assertTrue( Global.x.d == 0);
+        }
+      };
+      
+      t.start();
+      
+      Verify.println("M inc");
+      Global.x.d++;
+      Verify.println("M dec");
+      Global.x.d--;
+      assertTrue( Global.x.d == 0);
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/SkipInstructionTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/SkipInstructionTest.java
new file mode 100644 (file)
index 0000000..7ac1dc2
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.GETFIELD;
+import gov.nasa.jpf.jvm.bytecode.IRETURN;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.StackFrame;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.choice.IntChoiceFromList;
+
+import org.junit.Test;
+
+public class SkipInstructionTest extends TestJPF {
+
+  //--- replacing field access
+
+  int answer = 0;
+
+  public static class GetFieldListener extends ListenerAdapter {
+    
+    @Override
+    public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute) {
+      Instruction pc = ti.getPC();
+
+      if (pc instanceof GETFIELD) {
+        GETFIELD gf = (GETFIELD) pc;
+        if (gf.getVariableId().equals(SkipInstructionTest.class.getName() + ".answer")) {
+          System.out.println("now intercepting: " + pc);
+
+          // simulate the operand stack behavior of the skipped insn
+          StackFrame frame = ti.getModifiableTopFrame();
+
+          frame.pop();
+          frame.push(42);
+
+          ti.skipInstruction(pc.getNext());
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testGETFIELD () {
+
+    if (verifyNoPropertyViolation("+listener=gov.nasa.jpf.test.mc.basic.SkipInstructionTest$GetFieldListener")){
+      int i = answer; // to be intercepted by listener
+    
+      System.out.println(i);
+      assert (i == 42) : "get_field not intercepted: " + i;
+    }
+  }
+  
+  //--- replacing method execution
+  // this is not using skipInstruction but is setting the next one to enter, which
+  // means it can skip over a whole bunch of things. The use that comes to mind is to
+  // skip over method bodies, but still preserve the invoke processing so that we
+  // preserve the locking/sync (which we otherwise would have to do explicitly from
+  // a pre-exec listener
+  
+  // the intercepted method
+  int foo (int a, int b) {
+    int result = a + b;
+    if (result > 10) {
+      return 10;
+    } else {
+      return result;
+    }
+  }
+  
+  // the listener that does the interception
+  public static class InvokeListener extends ListenerAdapter {
+    MethodInfo interceptedMethod;
+    
+    @Override
+    public void classLoaded (VM vm, ClassInfo ci) {
+      if (ci.getName().equals("gov.nasa.jpf.test.mc.basic.SkipInstructionTest")) {
+        interceptedMethod = ci.getMethod("foo(II)I", false);
+        assert interceptedMethod != null : "foo(II)I not found";
+        System.out.println("method to intercept: " + interceptedMethod);
+      }
+    }
+        
+    @Override
+    public void instructionExecuted (VM vm, ThreadInfo ti, Instruction nextInsn, Instruction executedInsn) {
+      MethodInfo mi = ti.getTopFrameMethodInfo();
+      if (mi == interceptedMethod) {
+        System.out.println("in " + mi);
+
+        if (!ti.isFirstStepInsn()) {
+          System.out.println("in top half: " + executedInsn);
+          if (executedInsn instanceof JVMInvokeInstruction) { // we just entered the interceptedMethod
+            IntChoiceFromList cg = new IntChoiceFromList("Test", 42, 43);
+            if (vm.setNextChoiceGenerator(cg)) {
+              return; // we are done here, next insn in this method will skip to return
+            }
+          }
+        } else {
+          // note - this is the first insn WITHIN the interceptedMethod
+          System.out.println("in bottom half: " + executedInsn);
+          IntChoiceFromList cg = vm.getCurrentChoiceGenerator("Test", IntChoiceFromList.class);
+          if (cg != null) {
+            int choice = cg.getNextChoice();
+            Instruction lastInsn = mi.getLastInsn();
+            assert lastInsn instanceof IRETURN : "last instruction not an IRETURN ";
+            StackFrame frame = ti.getModifiableTopFrame(); // we are modifying it
+            System.out.println("listener is skipping method body of " + mi + " returning " + choice);
+            frame.push(choice);
+            ti.setNextPC(lastInsn);
+          } else {
+            System.out.println("unexpected CG: " + cg);
+          }
+        }
+      }      
+    }    
+  }
+  
+  @Test
+  public void testSkipMethodBody() {
+    if (verifyNoPropertyViolation("+listener=gov.nasa.jpf.test.mc.basic.SkipInstructionTest$InvokeListener")){
+      int ret = foo( 3, 4);
+      System.out.println(ret);
+      //assertTrue( ret == 42);
+    }    
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/StackDepthCheckerTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/StackDepthCheckerTest.java
new file mode 100644 (file)
index 0000000..bbd1702
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * regression test for StackDepthChecker listener
+ */
+public class StackDepthCheckerTest extends TestJPF {
+  
+  int n;
+  
+  void foo(){
+    n++;
+    System.out.print("entered foo() at level ");
+    System.out.println(n);
+    
+    foo();
+    
+    n--; // not that we ever get here
+    System.out.print("exited foo() at level ");
+    System.out.println(n);
+  }
+  
+  @Test 
+  @SuppressWarnings("deprecation")
+  public void testInfiniteRecursion (){
+    if (verifyUnhandledException("java.lang.StackOverflowError", 
+        "+listener=.listener.StackDepthChecker", "+sdc.max_stack_depth=42")){
+      Thread t = Thread.currentThread();
+      n = t.countStackFrames(); // it's deprecated, but we just want to make the printout more readable
+      foo();
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/StatelessTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/StatelessTest.java
new file mode 100644 (file)
index 0000000..f7db2d3
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+/**
+ * regression test for stateless (non-matching) execution mode
+ */
+public class StatelessTest extends TestJPF {
+
+  @Test
+  public void testNumberOfPaths(){
+    
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation("+vm.storage.class=null")){
+      int d = Verify.getInt(0, 5);
+      d = 0;
+      Verify.breakTransition("testNumberOfPaths"); // just to give the serializer something to chew on (if there is any)
+      System.out.println("got here");
+      Verify.incrementCounter(0);
+    }
+    
+    if (!isJPFRun()){
+      assert Verify.getCounter(0) == 6;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/TraceTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/TraceTest.java
new file mode 100644 (file)
index 0000000..57cda5c
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.io.File;
+
+import org.junit.Test;
+
+public class TraceTest extends TestJPF {
+
+  static final String TEST_CLASS = TraceTest.class.getName();
+  static final String TRACE = "trace";
+
+  // the method run by JPF
+  public void foo () {
+    int a = Verify.getInt(0, 42);
+    int b = Verify.getInt(100, 142);
+
+    System.out.println("pre-trace choice: " + a + ',' + b);
+    Verify.incrementCounter(0);
+
+    Verify.storeTraceAndTerminateIf(!Verify.isTraceReplay(),
+            TRACE, "cut off here..");
+
+    int c = Verify.getInt(0, 3);
+    Verify.incrementCounter(0);
+    System.out.println("post-trace choice: " + a + ',' + b + ',' + c);
+  }
+
+  // the method that runs JPF
+  @Test
+  public void testPartialTrace () {
+    File tf = new File(TRACE);
+
+    try {
+      if (tf.exists()) {
+        tf.delete();
+      }
+      Verify.resetCounter(0);
+
+      // first JPF run
+      noPropertyViolation(setTestMethod(TEST_CLASS, "foo"));
+
+      if (Verify.getCounter(0) != 1) {
+        fail("wrong number of backtracks on non-replay run: " + Verify.getCounter(0));
+      }
+
+      // second JPF run
+      noPropertyViolation( setTestMethod(TEST_CLASS, "foo"), "+listener=.listener.ChoiceSelector",
+              "+choice.use_trace=" + TRACE);
+
+      if (Verify.getCounter(0) != 5) {
+        fail("wrong number of backtracks on replay run: " + Verify.getCounter(0));
+      }
+
+    } finally {
+      tf.delete();
+    }
+  }
+
+
+  // the method run by JPF
+  public void bar () {
+    int i1 = Verify.getInt(0, 5);
+    int i2 = Verify.getInt(0, 5);
+    int i3 = Verify.getInt(0, 5);
+    boolean b1 = Verify.getBoolean();
+    int i4 = Verify.getInt(0,3);
+
+    System.out.printf("%d,%d,%d,%b,%d\n", i1, i2, i3, b1, i4);
+    
+    
+    assert !(i1 == 0 && i2 == 1 && i3 == 2 && b1 && i4 == 3);
+  }
+
+  // the method that runs JPF
+  @Test public void testErrorTrace () {
+    File tf = new File(TRACE);
+
+    try {
+      if (tf.exists()) {
+        tf.delete();
+      }
+
+      // first JPF run
+      System.out.println("--- creating trace");
+      assertionError(setTestMethod("bar"), "+listener=.listener.TraceStorer", "+trace.file=" + TRACE);
+
+      // second JPF run
+      System.out.println("--- replaying trace");
+      assertionError(setTestMethod("bar"), "+listener=.listener.ChoiceSelector","+choice.use_trace=" + TRACE);
+    } finally {
+      tf.delete();
+    }
+  }
+
+  public void baz() {
+    // note there always is an automatic thread-CG after static initialization
+    boolean a = Verify.getBoolean();  // depth 2
+    System.out.print("a=");
+    System.out.println(a);
+    boolean b = Verify.getBoolean();  // depth 3
+    System.out.print("b=");
+    System.out.println(a);
+    boolean c = Verify.getBoolean();  // depth 4
+    System.out.print("c=");
+    System.out.println(a);
+    assert false : "should not search up to this";
+  }
+
+  @Test public void testDepth () {
+    File tf = new File(TRACE);
+
+    try {
+      if (tf.exists()) {
+        tf.delete();
+      }
+
+      noPropertyViolation("+listener=.listener.TraceStorer",
+              "+trace.file=" + TRACE,
+              "+trace.depth=3",
+              "+search.depth_limit=3",
+              TEST_CLASS, "baz");
+
+      noPropertyViolation("+listener=.listener.ChoiceSelector",
+              "+choice.use_trace=" + TRACE,
+              "+search.depth_limit=3",
+              TEST_CLASS, "baz");
+    } finally {
+      tf.delete();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/TransitionLengthTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/TransitionLengthTest.java
new file mode 100644 (file)
index 0000000..83c8e36
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.choice.BreakGenerator;
+import org.junit.Test;
+
+/**
+ * JPF regression test for breaking transitions when exceeding
+ * vm.max_transition_length. While the program would not terminate outside
+ * JPF, it will terminate when run under JPF because of state matching
+ * 
+ * the listener is purely informative - if the test fails it doesn't terminate.
+ * It should report two registrations within the loop, the second one matching
+ * the state that was stored after the first one. However, the number of 
+ * transition breaks might change with more sophisticated loop detection
+ */
+public class TransitionLengthTest extends TestJPF {
+    
+  public static class Listener extends ListenerAdapter {
+    @Override
+    public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> nextCG, ThreadInfo currentThread, Instruction executedInstruction) {
+      if (nextCG instanceof BreakGenerator){
+        System.out.println();
+        System.out.println("registered: " + nextCG);
+      }
+    }
+  }
+  
+  @Test
+  public void testTermination(){
+    if (verifyNoPropertyViolation("+vm.max_transition_length=500", 
+                                  "+listener=" + TransitionLengthTest.class.getName() + "$Listener")){
+      System.out.println("starting loop");
+      while (true){
+        // no program state change withing body - this should eventually run into state matching
+        System.out.print(".");
+      }
+      // we can never get here outside of JPF
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/UnlockNonSharedTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/UnlockNonSharedTest.java
new file mode 100644 (file)
index 0000000..d1f44fe
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * Ensures that a recursive lock/unlock doesn't leave the lock in an acquired state.
+ */
+public class UnlockNonSharedTest extends TestJPF
+{
+   @Test
+   public void test() throws InterruptedException
+   {
+      Runnable task, nop;
+      Thread thread;
+      
+      if (verifyNoPropertyViolation())
+      {
+         Verify.setProperties("vm.por.skip_local_sync=true");
+
+         nop = new Runnable()
+         {
+            @Override
+                       public void run()
+            {
+               // nothing to do
+            }
+         };
+         
+         task = new Runnable()
+         {
+            private final Object m_lock = new Object();
+            private       int    m_count;
+            
+            @Override
+                       public void run()
+            {
+               synchronized (m_lock)
+               {
+                  m_count++;
+               }
+            }
+         };
+         
+         task.run();                    // Acquire m_lock in a single-threaded state so that MONITOREXIT.isShared() will return false.  (The bug would cause ei.unlock() to not be called).
+         
+         thread = new Thread(task);     // Create a thread to acquire m_lock.
+         
+         thread.setDaemon(false);
+         thread.start();                // To reproduce the bug, this thread shouldn't start executing until after the main thread exits.  This thread should then execute to the point just before acquiring m_lock.
+         
+         thread = new Thread(nop);      // Create another thread.  This thread must not be able to reach m_lock.
+   
+         thread.setDaemon(false);
+         thread.start();                // This thread should start and exit before the above thread acquires m_lock.  Due to the bug, the above thread won't be able to acquire the lock and hence a "deadlock" will ensue.
+      }
+   }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/basic/VerifyTest.java b/src/tests/gov/nasa/jpf/test/mc/basic/VerifyTest.java
new file mode 100644 (file)
index 0000000..cf86610
--- /dev/null
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.basic;
+
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+/**
+ * test various Verify APIs
+ */
+public class VerifyTest extends TestJPF {
+
+  @Test public void testBreak () {
+
+    if (verifyNoPropertyViolation()) {
+      int y = 4;
+      int x = 0;
+
+      while (x != y) { // JPF should state match on the backjump
+        x = x + 1;
+        if (x > 3) {
+          x = 0;
+        }
+
+        Verify.breakTransition("testBreakTransition"); // this should eventually state match
+      }
+
+      assert false : "we should never get here";
+    }
+  }
+
+  @Test public void testProperties () {
+
+    if (verifyNoPropertyViolation("+hum=didum")) {
+      String target = Verify.getProperty("hum");
+      System.out.println("got hum=" + target);
+      assert target.equals("didum");
+
+      Verify.setProperties("foo=bar");
+      String p = Verify.getProperty("foo");
+      System.out.println("got foo=" + p);
+      assert "bar".equals(p);
+    }
+  }
+  
+  @Test public void testChangeListener () {
+    
+    if (verifyNoPropertyViolation()) {
+      Verify.setProperties("listener=gov.nasa.jpf.listener.StateSpaceAnalyzer");  // This used to cause a NullPointerException
+    }
+  }
+  
+  @Test public void testGetBoolean () {
+
+    Verify.resetCounter(0);
+    Verify.resetCounter(1);
+
+    if (verifyNoPropertyViolation()) {
+      Verify.incrementCounter(Verify.getBoolean() ? 1 : 0);
+    } else {
+      assert Verify.getCounter(0) == 1;
+      assert Verify.getCounter(1) == 1;
+    }
+  }
+
+  @Test public void testGetBooleanFalseFirst () {
+    boolean falseFirst, value;
+
+    Verify.resetCounter(0);
+
+    if (verifyNoPropertyViolation()) {
+    
+      falseFirst = Verify.getBoolean();
+    
+      Verify.resetCounter(0);
+    
+      value = Verify.getBoolean(falseFirst);
+      
+      Verify.ignoreIf(Verify.getCounter(0) != 0);
+      
+      Verify.incrementCounter(0);
+      
+      assert value == !falseFirst;
+    }
+  }
+  
+  /**
+   * This test ensures that stateBacktracked() is called even if the transistion 
+   * is ignored.  This is important for listeners that keep a state that must 
+   * match the VM's state exactly and the state is updated in the middle of 
+   * transitions.  This is not possible if a backtrack happens on an ignored 
+   * transition and the stateBacktracked is not called.
+   */
+  @Test
+  public void backtrackNotificationAfterIgnore() {
+    if (verifyNoPropertyViolation("+listener+=,gov.nasa.jpf.test.mc.basic.VerifyTest$CountBacktrack",
+            "+vm.max_transition_length=MAX")) {
+      if (Verify.getBoolean(false)) {
+        Verify.ignoreIf(true);
+      }
+    } else {
+      // 2 for the Verify.getBoolean, 1 for <root>
+      assertEquals(3, CountBacktrack.getBacktrackedCount());
+    }
+  }
+  
+  public static class CountBacktrack extends ListenerAdapter {
+
+    private static int m_backtrackedCount;
+
+    @Override
+    public void stateBacktracked(Search search) {
+      m_backtrackedCount++;
+    }
+
+    public static int getBacktrackedCount() {
+      return (m_backtrackedCount);
+    }
+  }
+   
+  // <2do>... and many more to come
+
+
+  @Test
+  public void testBitSet() {
+    int id = 2;
+
+    if (verifyNoPropertyViolation()) {      
+      // JPF execution only
+      Verify.setBitInBitSet(id, 3, true);
+      Verify.setBitInBitSet(id, 1, true);
+      
+    } else {
+      // host VM execution only
+      assert Verify.getBitInBitSet(id, 1) == true;
+      assert Verify.getBitInBitSet(id, 2) == false;
+      assert Verify.getBitInBitSet(id, 3) == true;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/CGCreatorFactoryTest.java b/src/tests/gov/nasa/jpf/test/mc/data/CGCreatorFactoryTest.java
new file mode 100644 (file)
index 0000000..b422dc8
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.json.CGCreator;
+import gov.nasa.jpf.util.json.Value;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.BooleanChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+public class CGCreatorFactoryTest extends TestJPF {
+static class TestBoolCGCreator implements CGCreator {
+
+    @Override
+       public ChoiceGenerator createCG(String id, Value[] values) {
+      return new BooleanChoiceGenerator(id);
+    }
+
+  }
+
+  class B {
+
+    boolean b;
+
+    public B(boolean b) {
+      this.b = b;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+      B other = (B) o;
+
+      return this.b == other.b;
+    }
+
+  }
+
+  @Test
+  public void testAddUserDefinedCGCreator() {
+    if (verifyNoPropertyViolation("+jpf-core.native_classpath+=;${jpf-core}/build/tests",
+            "+jpf-core.test_classpath+=;${jpf-core.native_classpath}",
+            "+cg-creators=TF:" + TestBoolCGCreator.class.getName())) {
+
+      String json = "{"
+              + "'b' : TF()"
+              + "}";
+
+      Object[] expected = {
+        new B(true), new B(false)
+      };
+
+      B b = Verify.createFromJSON(B.class, json);
+      JSONTest.checkValue(expected, b);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/CrossingTest.java b/src/tests/gov/nasa/jpf/test/mc/data/CrossingTest.java
new file mode 100644 (file)
index 0000000..423edfc
--- /dev/null
@@ -0,0 +1,282 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * This is the famous Bridge-crossing puzzle. The aim is to see what the minimum
+ * amount of time is for 4 people to cross with flash-light (torch): 10.5.2.1.
+ * The answer is 17 for the given configuration.
+ *
+ * One can find this solution with JPF in BFS search mode - DFS will not
+ * work since this is an infinite-state program (time keeps increasing).
+ *
+ * If one uses the ignoreIf(cond) call the branches that will lead to
+ * solutions worst than the current will be cut-off and this will allow
+ * DFS also to complete - and BFS to terminate faster.
+ *
+ * When setting the native flag in main one can also save information
+ * from one run to the next using JPF's native peer methods -
+ * see JPF_Crossing.java for the code of getTotal() and setTotal(int).
+ */
+package gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class CrossingTest extends TestJPF {
+
+  static class Constants {
+
+    public static final boolean east = true;
+    public static final boolean west = false;
+  }
+
+  static class Torch {
+
+    public static boolean side = Constants.east;
+
+    @Override
+       public String toString() {
+      if (side == Constants.east) {
+        return "east";
+      } else {
+        return "west";
+      }
+    }
+  }
+
+  static class Bridge {
+
+    static Person[] onBridge = new Person[2];
+    static int numOnBridge = 0;
+
+    public static boolean isFull() {
+      return numOnBridge != 0;
+    }
+
+    public static int Cross() {
+      int time = 0;
+      Torch.side = !Torch.side;
+
+      if (numOnBridge == 1) {
+        onBridge[0].side = Torch.side;
+        time = onBridge[0].time;
+
+      //System.out.println("Person " + onBridge[0] +
+      //                     " moved to " + Torch.side +
+      //               " in time " + time);
+      } else {
+        assert onBridge[0] != null : "Argh, null " + numOnBridge;
+        assert onBridge[1] != null;
+
+        onBridge[0].side = Torch.side;
+        onBridge[1].side = Torch.side;
+
+        if (onBridge[0].time > onBridge[1].time) {
+          time = onBridge[0].time;
+        } else {
+          time = onBridge[1].time;
+        }
+
+      //System.out.println("Person " + onBridge[0] +
+      //                 " and Person " + onBridge[1] +
+      //                 " moved to " + Torch.side +
+      //                 " in time " + time);
+      }
+
+      return time;
+    }
+
+    public static void clearBridge() {
+      if (numOnBridge == 0) {
+        return;
+      } else if (numOnBridge == 1) {
+        onBridge[0] = null;
+        numOnBridge = 0;
+      } else {
+        onBridge[0] = null;
+        onBridge[1] = null;
+        numOnBridge = 0;
+      }
+    }
+
+    public static void initBridge() {
+      onBridge[0] = null;
+      onBridge[1] = null;
+      numOnBridge = 0;
+    }
+
+    public static boolean tryToCross(Person th) {
+      if ((numOnBridge < 2) && (onBridge[0] != th) && (onBridge[1] != th)) {
+        onBridge[numOnBridge++] = th;
+
+        return true;
+      } else {
+        return false;
+      }
+    }
+  }
+
+  static class Person {
+    // person to cross the bridge
+
+    int id;
+    public int time;
+    public boolean side;
+
+    public Person(int i, int t) {
+      time = t;
+      side = Constants.east;
+      id = i;
+    }
+
+    public void move() {
+      if (side == Torch.side) {
+        if (!Verify.randomBool()) {
+          Bridge.tryToCross(this);
+        }
+      }
+    }
+
+    @Override
+       public String toString() {
+      return "" + id;
+    }
+  }
+
+  public static native void setTotal(int time);
+
+  // due to these natives the code only runs in JPF
+  public static native int getTotal();
+
+  public void run() {
+    //Verify.beginAtomic();
+
+    // when natives are used one can record the minimal value found so
+    // far to get a better one on the next execution - this
+    // requires the -mulitple-errors option and then the last error
+    // found must be replayed
+    boolean isNative = false;
+
+    if (isNative) {
+      setTotal(30); // set to value larger than solution (17)
+    }
+
+    int total = 0;
+    boolean finished = false;
+    Bridge.initBridge();
+
+    Person p1 = new Person(1, 1);
+    Person p2 = new Person(2, 2);
+    Person p3 = new Person(3, 5);
+    Person p4 = new Person(4, 10);
+    //Verify.endAtomic();
+
+    while (!finished) {
+      //Verify.beginAtomic();
+      p1.move();
+      p2.move();
+      p3.move();
+      p4.move();
+
+      if (Bridge.isFull()) {
+        total += Bridge.Cross();
+
+        if (isNative) {
+          Verify.ignoreIf(total > getTotal());
+        } else {
+          Verify.ignoreIf(total > 17); //with this DFS will also find error
+        }
+
+        Bridge.clearBridge();
+
+        //printConfig(p1,p2,p3,p4,total);
+        finished = !(p1.side || p2.side || p3.side || p4.side);
+      }
+
+    //Verify.endAtomic();
+    }
+
+    //Verify.beginAtomic();
+
+    if (isNative) {
+      if (total < getTotal()) {
+        System.out.println("new total " + total);
+        setTotal(total);
+        assert (total > getTotal());
+      }
+    } else {
+      System.out.println("total time = " + total);
+      assert (total > 17) : "total > 17  |  total = " + total;
+    }
+
+  //Verify.endAtomic();
+  }
+
+  static void printConfig(Person p1, Person p2, Person p3, Person p4,
+          int total) {
+    if (p1.side == Constants.east) {
+      System.out.print("p1(" + p1.time + ")");
+    }
+
+    if (p2.side == Constants.east) {
+      System.out.print("p2(" + p2.time + ")");
+    }
+
+    if (p3.side == Constants.east) {
+      System.out.print("p3(" + p3.time + ")");
+    }
+
+    if (p4.side == Constants.east) {
+      System.out.print("p4(" + p4.time + ")");
+    }
+
+    System.out.print(" - " + total + " -> ");
+
+    if (p1.side == Constants.west) {
+      System.out.print("p1(" + p1.time + ")");
+    }
+
+    if (p2.side == Constants.west) {
+      System.out.print("p2(" + p2.time + ")");
+    }
+
+    if (p3.side == Constants.west) {
+      System.out.print("p3(" + p3.time + ")");
+    }
+
+    if (p4.side == Constants.west) {
+      System.out.print("p4(" + p4.time + ")");
+    }
+
+    System.out.println();
+  }
+
+
+  @Test public void testNoHeuristic () {
+    if (verifyAssertionError()){
+      run();
+    }
+  }
+
+  @Test public void testBFSHeuristic() {
+    if (verifyAssertionError("+search.class=gov.nasa.jpf.search.heuristic.BFSHeuristic")){
+      run();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/DataChoiceTest.java b/src/tests/gov/nasa/jpf/test/mc/data/DataChoiceTest.java
new file mode 100644 (file)
index 0000000..f16b56f
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+public class DataChoiceTest extends TestJPF {
+
+  class MyType {
+
+    String id;
+
+    MyType(String id) {
+      this.id = id;
+    }
+
+    @Override
+       public String toString() {
+      return ("MyType " + id);
+    }
+  }
+
+
+  int intField = 42;
+  double doubleField = -42.2;
+
+  @Test
+  public void testIntFromSet() {
+
+    if (!isJPFRun()) {
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+my_int_from_set.class=gov.nasa.jpf.vm.choice.IntChoiceFromSet",
+            "+my_int_from_set.values=1,2,3,intField,localVar")) {
+      int localVar = 43;  // read by choice generator
+
+      int i = Verify.getInt("my_int_from_set");
+      Verify.incrementCounter(0);
+      System.out.println(i);
+
+    }
+
+    if (!isJPFRun()) { // this is only executed outside JPF
+      if (Verify.getCounter(0) != 5) {
+        fail("wrong number of backtracks");
+      }
+    }
+  }
+
+  @Test
+  public void testIntFromArray () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation()){
+      int i = Verify.getIntFromList(1,2,3,4,5); // ..and change the combination on my luggage
+      System.out.println(i);
+      if (i>0 && i < 6){
+        Verify.incrementCounter(0);
+      } else {
+        assert false : "wrong int choice value: " + i;
+      }
+    }
+
+    if (!isJPFRun()) { // this is only executed outside JPF
+      if (Verify.getCounter(0) != 5) {
+        fail("wrong number of backtracks");
+      }
+    }
+  }
+
+
+  @Test
+  public void testDoubleFromSet() {
+
+    if (!isJPFRun()) {
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+my_double_from_set.class=gov.nasa.jpf.vm.choice.DoubleChoiceFromSet",
+            "+my_double_from_set.values=42.0,43.5,doubleField,localVar")) {
+
+      double localVar = 4200.0; // read by choice generator
+
+      double d = Verify.getDouble("my_double_from_set");
+      Verify.incrementCounter(0);
+      System.out.println(d);
+    }
+
+    if (!isJPFRun()) { // this is only executed outside JPF
+      if (Verify.getCounter(0) != 4) {
+        fail("wrong number of backtracks");
+      }
+    }
+  }
+
+  @Test
+  public void testDoubleFromArray () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation()){
+      double d = Verify.getDoubleFromList(-42.0,0,42.0);
+      System.out.println(d);
+
+      if (d == -42.0 || d == 0.0 || d == 42.0){
+        Verify.incrementCounter(0);
+      } else {
+        assert false : "wrong double choice value: " + d;
+      }
+    }
+
+    if (!isJPFRun()) { // this is only executed outside JPF
+      if (Verify.getCounter(0) != 3) {
+        fail("wrong number of backtracks");
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/DynamicAbstractionTest.java b/src/tests/gov/nasa/jpf/test/mc/data/DynamicAbstractionTest.java
new file mode 100644 (file)
index 0000000..ad94717
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import gov.nasa.jpf.vm.serialize.AbstractionAdapter;
+
+import org.junit.Test;
+
+/**
+ * regression test for field value abstractions
+ */
+public class DynamicAbstractionTest extends TestJPF {
+  
+  static final String SERIALIZER_ARG = "+vm.serializer.class=.vm.serialize.DynamicAbstractionSerializer";
+  
+  static class MyClass {
+    int data;
+    double notAbstracted;
+  }
+  
+  public static class MyClassDataAbstraction extends AbstractionAdapter {
+    
+    @Override
+    public int getAbstractValue (int data){
+      int cat = 1;
+      if (data > 5) cat = 2;
+      if (data > 10) cat = 3;
+      
+      System.out.println("abstracted value for " + data + " = " + cat);
+      return cat;
+    }
+  }
+  
+  //---------------------------------------------------------------------------
+  
+  @Test
+  public void testMyClass() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation(SERIALIZER_ARG,
+                                  "+das.classes.include=*$MyClass",
+                                  "+das.fields=data", 
+                                  "+das.data.field=*$MyClass.data",
+                                  "+das.data.abstraction=gov.nasa.jpf.test.mc.data.DynamicAbstractionTest$MyClassDataAbstraction")){
+      MyClass matchedObject = new MyClass();
+      matchedObject.data = Verify.getInt(0, 20);
+      
+      Verify.breakTransition("testDataAbstraction");
+      System.out.println("new state for myClass.data = " + matchedObject.data);
+      Verify.incrementCounter(0);
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 3);
+    }    
+  }
+
+  
+  //---------------------------------------------------------------------------
+  
+  @Test
+  public void testMixedFields(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation(SERIALIZER_ARG,
+                                  "+das.classes.include=*$MyClass",
+                                  "+das.fields=data", 
+                                  "+das.data.field=*$MyClass.data",
+                                  "+das.data.abstraction=gov.nasa.jpf.test.mc.data.DynamicAbstractionTest$MyClassDataAbstraction")){
+      MyClass matchedObject = new MyClass();
+      matchedObject.data = Verify.getInt(0, 20);
+      
+      if (matchedObject.data % 4 == 0){
+        System.out.println("  notAbstracted=1");
+        matchedObject.notAbstracted = 1;
+      }
+      
+      Verify.breakTransition("testDataAbstraction"); // matching point
+      System.out.println("new state for myClass.data = " + matchedObject.data + ", " + matchedObject.notAbstracted);
+      Verify.incrementCounter(0);
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 6);
+    }        
+  }
+
+  
+  //---------------------------------------------------------------------------
+  
+  static class SomeIgnoredClass {
+    int data;  // note that it is not @FilterField annotated
+  }
+  
+  @Test
+  public void testClassFilter() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation(SERIALIZER_ARG,
+                                  "+das.classes.include=*$MyClass", // only consider MyClass instance and static data
+                                  "+das.methods.exclude=*",  // make sure we don't match this stackframe ('i' changes)
+                                  "+vm.max_transition_length=MAX")){
+      MyClass matchedObject = new MyClass();
+      SomeIgnoredClass ignoredObject = new SomeIgnoredClass();
+      
+      matchedObject.data = Verify.getInt(0, 2); // (1) 1st CG
+      System.out.print(" matchedObject.data=");
+      System.out.println( matchedObject.data);
+      
+      for (int i=0; i<2; i++){
+        ignoredObject.data = i;
+        System.out.print("    ignoredObject.data=");
+        System.out.println( ignoredObject.data);
+
+        Verify.breakTransition("testDataAbstraction"); // (2) matching point for someObject
+        
+        // if we get here we had a new state (i.e. wasn't matched)
+        // NOTE we don't get here for matchedObject.data=0 because that would match with the state before (1)
+        System.out.printf("         new state for matched=%d, ignored=%d\n", matchedObject.data, ignoredObject.data);
+        Verify.incrementCounter(0); // should be only reached once for matchedObject.data={1,2}
+      }
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+    }
+  }
+  
+
+  //---------------------------------------------------------------------------
+
+  void matchThis() {
+    for (int i=0; i<2; i++){
+      System.out.printf("  matchThis() i=%d\n", i);
+    
+      Verify.breakTransition("testDataAbstraction"); // 'i' has changed
+      System.out.println("    new state");
+      Verify.incrementCounter(0);
+    }
+  }
+  
+  @Test
+  public void testStackFrameFilter() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation(SERIALIZER_ARG,
+                                  "+das.methods.include=*DynamicAbstractionTest.matchThis(*)V")){
+      for (int i=0; i<10; i++){ // 'i' changes this frame..
+        System.out.printf("loop cycle %d\n", i);
+        matchThis(); // ..but not this one
+      }
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/EventGeneratorTest.java b/src/tests/gov/nasa/jpf/test/mc/data/EventGeneratorTest.java
new file mode 100644 (file)
index 0000000..e09f700
--- /dev/null
@@ -0,0 +1,188 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.EventProducer;
+import gov.nasa.jpf.util.event.EventContext;
+import gov.nasa.jpf.util.event.Event;
+import gov.nasa.jpf.util.event.NoEvent;
+import gov.nasa.jpf.util.event.TestEventTree;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.junit.Test;
+
+/**
+ * regression test for EventGenerator based test drivers
+ */
+public class EventGeneratorTest extends TestJPF {
+
+   
+  //---------------------------------------------------------------------------------------
+  public static class SimpleTree extends TestEventTree {
+    public SimpleTree (){
+      expected = new String[] {
+        "a1b",
+        "axxb"
+      };
+    }
+    
+    @Override
+    public Event createRoot() {
+      return 
+        sequence(
+          event("a"),
+          alternatives(
+            event("1"),
+            iteration(2,
+              event("x")
+            )
+          ),
+          event("b")
+        );
+    }
+  }
+  
+  @Test
+  public void testSimpleTree(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyNoPropertyViolation("+event.tree.class=.test.mc.data.EventGeneratorTest$SimpleTree", "+log.info=event")){
+      EventProducer producer = new EventProducer();
+      StringBuilder sb = new StringBuilder();
+      
+      while (producer.processNextEvent()){
+        sb.append(producer.getEventName());
+      }
+      
+      String trace = sb.toString();
+      System.out.print("got trace: ");
+      System.out.println(trace);
+      
+      if (!producer.checkPath()){
+        fail("unexpected trace failure");        
+      }
+      
+      if (producer.isCompletelyCovered()){
+        Verify.setCounter(0, 1);
+      }
+    }
+    
+    if (!isJPFRun()){
+      if (Verify.getCounter(0) != 1){
+        fail("unaccounted trace failure");
+      }
+    }
+  }
+    
+  //-------------------------------------------------------------------------------------
+  public static class CombinationTree extends TestEventTree {
+    public CombinationTree (){
+      printTree();
+      printPaths();
+    }
+    
+    @Override
+    public Event createRoot() {
+       Event[] options = { event("A"), event("B"), event("C") };
+
+       return anyCombination(options);
+     }
+  }
+  
+  @Test
+  public void testAnyCombination (){
+    if (verifyNoPropertyViolation("+event.tree.class=.test.mc.data.EventGeneratorTest$CombinationTree", "+log.info=event")){
+      EventProducer producer = new EventProducer();
+      StringBuilder sb = new StringBuilder();
+      
+      while (producer.processNextEvent()){
+        sb.append(producer.getEventName());
+      }
+    }
+  }
+  
+  
+  //------------------------------------------------------------------------------------
+  public static class ExpandTree extends TestEventTree {
+    public ExpandTree (){
+      printTree();
+    }
+    
+    @Override
+    public Event createRoot(){
+      return
+              sequence(
+                event("a"),
+                event("*"),
+                event("<opt>"),
+                event("b"));
+    }    
+  }
+
+  public static class MyEventContext implements EventContext {
+    @Override
+    public Event map (Event e){
+        String eventName = e.getName();
+      
+        if (eventName.equals("*")){
+          System.out.println("  expanding " + eventName + " to [X,Y]");
+          List<Event> list = new ArrayList<Event>();
+          list.add( new Event("X"));
+          list.add( new Event("Y"));
+          return e.replaceWithAlternativesFrom(list);
+          
+        } else if (eventName.equals("<opt>")){
+          System.out.println("  skipping " + eventName);
+          // that's effectively event removal without changing the structure of the tree
+          return e.replaceWith(new NoEvent()); 
+        }
+
+        return e;
+    }
+  }
+
+  @Test
+  public void testEventExpansion (){
+    if (verifyNoPropertyViolation("+event.tree.class=.test.mc.data.EventGeneratorTest$ExpandTree",
+                                  "+event.context.class=.test.mc.data.EventGeneratorTest$MyEventContext",
+                                  "+log.info=event")){
+      EventProducer producer = new EventProducer();
+      StringBuilder sb = new StringBuilder();
+      
+      while (producer.processNextEvent()){
+        String eventName = producer.getEventName();
+        if (eventName != null){
+          sb.append(eventName);
+        }
+      }
+      
+      String trace = sb.toString();
+      System.out.print("--- got trace: ");
+      System.out.println(trace);
+    }
+  }
+  
+
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/JSONTest.java b/src/tests/gov/nasa/jpf/test/mc/data/JSONTest.java
new file mode 100644 (file)
index 0000000..be8df7e
--- /dev/null
@@ -0,0 +1,588 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+/**
+ * JPF regression test for JSON test object creation
+ * @author Ivan Mushketik
+ */
+public class JSONTest extends TestJPF {
+  
+  class MySup {
+    int j;
+  }
+
+
+  @Test
+  public void testFillFromJSONSingleClass() {
+    if (verifyNoPropertyViolation()) {
+      MySup sup = Verify.createFromJSON(MySup.class, "{'j' : 123 }");
+
+      assert sup.j == 123;
+    }
+  }
+
+  class MyClass extends MySup {
+    int i;
+  }
+
+  @Test
+  public void testFillFromJSONInheritance() {
+    if (verifyNoPropertyViolation()) {
+      MyClass sup = Verify.createFromJSON(MyClass.class, "{'j':123, 'i':321 }");
+
+      assert sup.j == 123;
+      assert sup.i == 321;
+    }
+  }
+
+  class Primitives {
+    boolean z;
+    byte b;
+    short s;
+    int i;
+    long l;
+    float f;
+    double d;
+  }
+
+  @Test
+  public void testFillPrivimitivesFromJSON() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{'z': true,'b': 10,'s': 1000,'i': 321, 'l': 123456,'f': 12.34,'d': 23.45}";
+      Primitives p = Verify.createFromJSON( Primitives.class, json);
+
+      assert p.z == true;
+      assert p.b == 10;
+      assert p.s == 1000;
+      assert p.i == 321;
+      assert p.l == 123456;
+      assertEquals(12.34, p.f, 0.001);
+      assertEquals(23.45, p.d, 0.001);
+    }
+  }
+
+  class IntArr {
+    int ints[];
+  }
+
+  @Test
+  public void testFillIntArrayFromJSON() {
+    if (verifyNoPropertyViolation()) {
+      IntArr ia = Verify.createFromJSON( IntArr.class, "{'ints': [1, 2, 3]}");
+
+      assert ia.ints[0] == 1;
+      assert ia.ints[1] == 2;
+      assert ia.ints[2] == 3;
+    }
+  }
+
+  class Boxed {
+    Boolean t;
+    Boolean f;
+    Byte b;
+    Short s;
+    Integer i;
+    Long l;
+    Float fl;
+    Double d;
+  }
+
+  @Test
+  public void testFillBoxedPrimitivesFromJSON() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{'t':true, 'f':false, 'b':10, 's':1000,'i':321, 'l':123456, 'fl':12.34, 'd':23.45 }";
+      Boxed b = Verify.createFromJSON( Boxed.class, json);
+
+      assert b.t == true;
+      assert b.f == false;
+      assert b.b == 10;
+      assert b.s == 1000;
+      assert b.i == 321;
+      assert b.l == 123456;
+      assertEquals(12.34, b.fl, 0.001);
+      assertEquals(23.45, b.d, 0.001);
+    }
+  }
+
+  class PrimitiveArrays {
+    boolean bools[];
+    byte bytes[];
+    short shorts[];
+    int ints[];
+    long longs[];
+    float floats[];
+    double doubles[];
+  }
+
+  @Test
+  public void testFillPrimitiveArrays() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"bools\" : [true, false, true],"
+              + "\"bytes\" : [-40, -30, -20],"
+              + "\"shorts\" : [2, 3, 4],"
+              + "\"ints\" : [1, 2, 3],"
+              + "\"longs\" : [1000, 2000, 3000],"
+              + "\"floats\" : [12.34, 23.45, 34.56],"
+              + "\"doubles\" : [-12.34, -23.45, -34.56]"
+              + "}";
+      PrimitiveArrays pa = Verify.createFromJSON( PrimitiveArrays.class, json);
+
+      assert pa.bools[0] == true;
+      assert pa.bools[1] == false;
+      assert pa.bools[2] == true;
+
+      assert pa.bytes[0] == -40;
+      assert pa.bytes[1] == -30;
+      assert pa.bytes[2] == -20;
+
+      assert pa.shorts[0] == 2;
+      assert pa.shorts[1] == 3;
+      assert pa.shorts[2] == 4;
+
+      assert pa.ints[0] == 1;
+      assert pa.ints[1] == 2;
+      assert pa.ints[2] == 3;
+
+      assert pa.longs[0] == 1000;
+      assert pa.longs[1] == 2000;
+      assert pa.longs[2] == 3000;
+
+      assertEquals(12.34, pa.floats[0], 0.0001);
+      assertEquals(23.45, pa.floats[1], 0.0001);
+      assertEquals(34.56, pa.floats[2], 0.0001);
+
+      assertEquals(-12.34, pa.doubles[0], 0.0001);
+      assertEquals(-23.45, pa.doubles[1], 0.0001);
+      assertEquals(-34.56, pa.doubles[2], 0.0001);
+    }
+  }
+
+  class InnerClass {
+    int i;
+  }
+
+  class OuterClass {
+    long l;
+    InnerClass ic;
+  }
+
+  @Test
+  public void testInnerClassFilling() {
+    if (verifyNoPropertyViolation()) {
+      String json =
+                "{"
+              +   "'l' : 1234,"
+              +   "'ic' : {"
+              +      "'i' : 4321"
+              +   "}"
+              + "}";
+
+      OuterClass oc = Verify.createFromJSON( OuterClass.class, json);
+
+      assert oc.l == 1234;
+      assert oc.ic.i == 4321;
+    }
+  }
+
+  @Test
+  public void testFillingWhenInnerClassIsNull() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"l\" : 1234,"
+              + "\"ic\" : null"
+              + "}";
+
+      OuterClass oc = Verify.createFromJSON( OuterClass.class, json);
+
+      assert oc.l == 1234;
+      assert oc.ic == null;
+    }
+  }
+
+  class MultiArray {
+    int intsInts[][];
+  }
+
+  @Test
+  public void testMultiArrayFilling() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"intsInts\" : [[1, 2, 3], [4, 5, 6]]"
+              + "}";
+
+      MultiArray ma = Verify.createFromJSON( MultiArray.class, json);
+
+      assert ma.intsInts[0][0] == 1;
+      assert ma.intsInts[0][1] == 2;
+      assert ma.intsInts[0][2] == 3;
+
+      assert ma.intsInts[1][0] == 4;
+      assert ma.intsInts[1][1] == 5;
+      assert ma.intsInts[1][2] == 6;
+    }
+  }
+
+  class BoxIntsArr {
+    Integer ints[];
+  }
+
+  @Test
+  public void testBoxedTypesArrayFilling() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"ints\" : [1, 2, 3]"
+              + "}";
+
+      BoxIntsArr bia = Verify.createFromJSON( BoxIntsArr.class, json);
+
+      assert bia.ints[0] == 1;
+      assert bia.ints[1] == 2;
+      assert bia.ints[2] == 3;
+    }
+  }
+
+  static class IC {
+    int i;
+  }
+
+  static class ArrayOfObjects {
+    IC cls[];
+  }
+
+  @Test
+  public void testArrayOfObjectsFilling() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"cls\" : [{\"i\" : 1}, {\"i\" : 2}, {\"i\" : 3}]"
+              + "}";
+
+      ArrayOfObjects aoo = Verify.createFromJSON( ArrayOfObjects.class, json);
+
+      assert aoo.cls[0].i == 1;
+      assert aoo.cls[1].i == 2;
+      assert aoo.cls[2].i == 3;
+    }
+  }
+
+  class MultObjectsArr {
+    IC cls[][];
+  }
+
+  @Test
+  public void testFillingMultArrayOfObjects() {
+      if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"cls\" : ["
+              + "[{\"i\" : 1}, {\"i\" : 2}, {\"i\" : 3}],"
+              + "[{\"i\" : 4}, {\"i\" : 5}, {\"i\" : 6}],"
+              + "[{\"i\" : 7}, {\"i\" : 8}, {\"i\" : 9}]"
+              + "]"
+              + "}";
+
+      MultObjectsArr moa = Verify.createFromJSON( MultObjectsArr.class, json);
+
+      assert moa.cls[0][0].i == 1;
+      assert moa.cls[0][1].i == 2;
+      assert moa.cls[0][2].i == 3;
+
+      assert moa.cls[1][0].i == 4;
+      assert moa.cls[1][1].i == 5;
+      assert moa.cls[1][2].i == 6;
+
+      assert moa.cls[2][0].i == 7;
+      assert moa.cls[2][1].i == 8;
+      assert moa.cls[2][2].i == 9;
+    }
+  }
+
+  class ClassWithString {
+    String s1;
+    String s2;
+  }
+
+  @Test
+  public void testFillStringValue() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "\"s1\" : \"val\","
+              + "\"s2\" : null"
+              + "}";
+
+      ClassWithString cws = Verify.createFromJSON( ClassWithString.class, json);
+
+      assert cws.s1.equals("val") == true;
+      assert cws.s2 == null;
+    }
+  }
+
+  // --- CG Tests
+
+  class Bool {
+    Bool(boolean b) {this.b = b;}
+    boolean b;
+
+    @Override
+       public boolean equals(Object o) {
+      Bool bool = (Bool) o;
+      return this.b == bool.b;
+    }
+  }
+
+  static void checkValue(Object[] expected, Object curVal) {
+    for (int i = 0; i < expected.length; i++) {
+      if (curVal.equals(expected[i])) {
+        Verify.setBitInBitSet(0, i, true);
+        break;
+      }
+    }
+
+    Verify.incrementCounter(0);
+
+    if (Verify.getCounter(0) == expected.length) {
+      for (int i = 0; i < expected.length; i++) {
+        assert Verify.getBitInBitSet(0, i) == true;
+      }
+    }
+  }
+
+  @Test
+  public void testSetBoolFromCG() {
+    if (verifyNoPropertyViolation()) {      
+      String json = "{"
+              + "'b' : TrueFalse()"
+              + "}";
+
+      Object[] expected = {
+        new Bool(true),
+        new Bool(false)
+      };
+      Bool bb = Verify.createFromJSON(Bool.class, json);      
+      checkValue(expected, bb);
+    }
+  }
+
+  class ByteShortIntLong {
+
+    public ByteShortIntLong(int b, int s, int i, long l) {
+      this.b = (byte) b; this.s = (short) s; this.i = i; this.l = l;
+    }
+
+    byte b; short s; int i; long l;
+
+    @Override
+       public boolean equals(Object o) {
+      ByteShortIntLong bs = (ByteShortIntLong) o;
+
+      return bs.b == b && bs.s == s && bs.i == i && bs.l == l;
+    }
+  }
+
+  @Test
+  public void testSetByteShortIntFromCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'b' : IntSet(1, 2),"
+              + "'s' : 2,"
+              + "'i' : IntSet(3, 4, 5),"
+              + "'l' : IntSet(8)"
+              + "}";
+
+      Object[] expected = {
+        new ByteShortIntLong(1, 2, 3, 8), new ByteShortIntLong(2, 2, 3, 8),
+        new ByteShortIntLong(1, 2, 4, 8), new ByteShortIntLong(2, 2, 4, 8),
+        new ByteShortIntLong(1, 2, 5, 8), new ByteShortIntLong(2, 2, 5, 8),
+      };
+      ByteShortIntLong bsil = Verify.createFromJSON(ByteShortIntLong.class, json);
+      checkValue(expected, bsil);
+    }
+  }
+
+  @Test
+  public void testFillWithIntevalCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'b' : 1,"
+              + "'s' : IntInterval(1, 3),"
+              + "'i' : 1,"
+              + "'l' : IntInterval(8, 10)"
+              + "}";
+
+      Object[] expected = {
+        new ByteShortIntLong(1, 1, 1, 8), new ByteShortIntLong(1, 2, 1, 8), new ByteShortIntLong(1, 3, 1, 8),
+        new ByteShortIntLong(1, 1, 1, 9), new ByteShortIntLong(1, 2, 1, 9), new ByteShortIntLong(1, 3, 1, 9),
+        new ByteShortIntLong(1, 1, 1, 10), new ByteShortIntLong(1, 2, 1, 10), new ByteShortIntLong(1, 3, 1, 10),};
+      ByteShortIntLong bsil = Verify.createFromJSON(ByteShortIntLong.class, json);
+      checkValue(expected, bsil);
+    }
+  }
+
+  class I {
+    int i;
+  }
+
+  class O {
+    I inner;
+    public O(int i) {
+      inner = new I();
+      inner.i = i;
+    }
+
+    @Override
+       public boolean equals(Object o) {
+      O outer = (O) o;
+
+      return outer.inner.i == this.inner.i;
+    }
+  }
+
+  @Test
+  public void testFillInnerClassCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'inner' : {"
+                + "'i' : IntSet(3, 4, 5)"
+                + "}"
+              + "}";
+
+      Object[] expected = {
+        new O(3), new O(4), new O(5),
+      };
+      O bsil = Verify.createFromJSON(O.class, json);
+      checkValue(expected, bsil);
+    }
+  }
+
+  class ArrI {
+    I[] arr;
+
+    ArrI(int... ints) {
+      arr = new I[ints.length];
+      for (int i = 0; i < ints.length; i++) {arr[i] = new I(); arr[i].i = ints[i];}
+    }
+
+    @Override
+       public boolean equals(Object o) {
+      ArrI other = (ArrI) o;
+
+      if (other.arr.length != this.arr.length) {
+        return false;
+      }
+      for (int i = 0; i < this.arr.length; i++) {
+        if (this.arr[i].i != other.arr[i].i) {
+          return false;
+        }
+      }
+      return true;
+    }
+  }
+
+  @Test
+  public void testFillingObjectInArrayWithCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'arr' : [ {'i' : IntSet(1, 2, 3)}, {'i' : IntSet(4, 5, 6)}]"
+              + "}";
+
+      Object[] expected = {
+        new ArrI(1, 4), new ArrI(2, 4), new ArrI(3, 4),
+        new ArrI(1, 5), new ArrI(2, 5), new ArrI(3, 5),
+        new ArrI(1, 6), new ArrI(2, 6), new ArrI(3, 6),
+      };
+      ArrI arri = Verify.createFromJSON(ArrI.class, json);
+      checkValue(expected, arri);
+    }
+  }
+
+  class BoxedInteger {
+    Integer bi;
+
+    BoxedInteger(Integer newI) {
+      bi = newI;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      BoxedInteger bic = (BoxedInteger) obj;
+      return this.bi.equals(bic.bi);
+    }
+  }
+
+  @Test
+  public void testObjectFromCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'bi' : IntSet(1, 2, 3)"
+              + "}";
+
+      Object[] expected = {
+        new BoxedInteger(1), new BoxedInteger(2), new BoxedInteger(3),
+      };
+      BoxedInteger bi = Verify.createFromJSON(BoxedInteger.class, json);
+      checkValue(expected, bi);
+    }
+  }
+
+  class BoxedDouble {
+    Double d;
+
+    public BoxedDouble(Double d) {
+      this.d = d;
+    }
+
+    @Override
+       public boolean equals(Object o) {
+      BoxedDouble bd = (BoxedDouble) o;
+
+      return doublesEqual(bd.d, this.d);
+    }
+
+    boolean doublesEqual(double d1, double d2) {
+      double diff = 0.001;
+
+      return Math.abs(d1 - d2) <= diff;
+    }
+  }
+
+  @Test
+  public void testBoxedDoubleFromCG() {
+    if (verifyNoPropertyViolation()) {
+      String json = "{"
+              + "'d' : DoubleSet(1.1, 2.2, 3.3)"
+              + "}";
+
+      Object[] expected = {
+        new BoxedDouble(1.1), new BoxedDouble(2.2), new BoxedDouble(3.3),
+      };
+      BoxedDouble bd = Verify.createFromJSON(BoxedDouble.class, json);
+      checkValue(expected, bd);
+    }
+  }
+
+  
+}
+
+
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/NativeStateHolderTest.java b/src/tests/gov/nasa/jpf/test/mc/data/NativeStateHolderTest.java
new file mode 100644 (file)
index 0000000..e60baa9
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.NativeStateHolder;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.Verify;
+import org.junit.Test;
+
+/**
+ * regression test for NativeStateHolder API
+ */
+public class NativeStateHolderTest extends TestJPF {
+
+  public static class NSHListener extends ListenerAdapter implements NativeStateHolder {
+    
+    // note that we don't restore nativeState, i.e. once it has reached the threshold
+    // the search should terminate
+    
+    int nativeState;
+    int n;
+        
+    @Override
+    public void vmInitialized (VM vm){
+      vm.getSerializer().addNativeStateHolder(this);
+      System.out.println("native state holder registered");
+    }
+    
+    @Override
+    public void choiceGeneratorRegistered (VM vm, ChoiceGenerator<?> cg, ThreadInfo ti, Instruction insn){
+      n++;
+      System.out.print("transition: " + n);
+      
+      if (nativeState < 5){
+        nativeState++;
+        System.out.println(", nativeState modified: " + nativeState);
+        
+      } else {
+        System.out.println(", no nativeState change");
+        if (n > 10){
+          fail("state leak despite constant nativesState");
+        }
+      }
+    }
+    
+    @Override
+    public void stateAdvanced (Search search){
+      System.out.println("cg advanced, isNewState: " + search.isNewState());
+    }
+
+    @Override
+    public void stateBacktracked (Search search){
+      System.out.println("state backtracked");
+    }
+
+    
+    @Override
+    public int getHash() {
+      return nativeState;
+    }
+  }
+  
+
+  @Test
+  public void testNativeStateHolder(){    
+    if (verifyNoPropertyViolation("+listener=" + getClass().getName() + "$NSHListener")){
+      while (true){
+        Verify.breakTransition("cycle");
+      }
+    }
+  }
+  
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/NumericValueCheckerTest.java b/src/tests/gov/nasa/jpf/test/mc/data/NumericValueCheckerTest.java
new file mode 100644 (file)
index 0000000..0540396
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * regression test for the NumericValueChecker listener
+ */
+public class NumericValueCheckerTest extends TestJPF {
+
+  static class C1 {
+    double d;
+    void setValue(double v){
+      d = v;
+    }
+  }
+
+  @Test
+  public void testField(){
+    if (verifyPropertyViolation(new TypeRef("gov.nasa.jpf.listener.NumericValueChecker"),
+            "+listener=.listener.NumericValueChecker",
+            "+range.fields=d",
+            "+range.d.field=*.NumericValueCheckerTest$C1.d",
+            "+range.d.min=42")){
+      C1 c1= new C1();
+      c1.setValue(0);
+    }
+  }
+
+
+  static class C2 {
+    void doSomething(int d){
+      int x = d;
+    }
+  }
+
+  @Test
+  public void testVars(){
+    if (verifyPropertyViolation(new TypeRef("gov.nasa.jpf.listener.NumericValueChecker"),
+            "+listener=.listener.NumericValueChecker",
+            "+range.vars=x",
+            "+range.x.var=*$C2.doSomething(int):x",
+            "+range.x.min=42")){
+      C2 c2= new C2();
+      c2.doSomething(-42);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/ObjectStreamTest.java b/src/tests/gov/nasa/jpf/test/mc/data/ObjectStreamTest.java
new file mode 100644 (file)
index 0000000..dd488c4
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.io.File;
+import java.io.Serializable;
+import java.util.ArrayList;
+
+import org.junit.After;
+import org.junit.Test;
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+public class ObjectStreamTest extends TestJPF {
+  static String osFileName = "file";
+
+  @After
+  public void deleteFile(){
+    File osFile = new File(osFileName);
+
+    if (osFile.exists()) {
+      osFile.delete();
+    }
+  }
+
+
+  @Test
+  public void testWriteReadInteger() {
+    if (!isJPFRun()) {
+      Verify.writeObjectToFile(new Integer(123), osFileName);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      Integer i = Verify.readObjectFromFile(Integer.class, osFileName);
+
+      assert i == 123;
+    }
+  }
+
+  @Test
+  public void testWriteReadString() {
+    if (!isJPFRun()) {
+      Verify.writeObjectToFile(new String("hello"), osFileName);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      String s = Verify.readObjectFromFile(String.class, osFileName);
+      assert s.equals("hello");
+    }
+  }
+  
+  static class Sup implements Serializable {
+    int s;
+  }
+  
+  static class Inherited extends Sup{
+    int i;
+  }
+  
+  @Test
+  public void testWriteReadInheritedClass() {
+    if (!isJPFRun()) {
+      Inherited inh = new Inherited();
+      inh.s = 1;
+      inh.i = 2;
+
+      Verify.writeObjectToFile(inh, osFileName);
+    }
+
+    if (verifyNoPropertyViolation("+jpf-core.native_classpath+=;${jpf-core}/build/tests")) {
+      Inherited inh = Verify.readObjectFromFile(Inherited.class, osFileName);
+
+      assert inh.s == 1;
+      assert inh.i == 2;
+    }
+
+  }
+
+  static class WithTransient implements Serializable {
+    int i;
+    transient int t;
+  }
+
+  @Test
+  public void testWriteReadTransientField() {
+    if (!isJPFRun()) {
+      WithTransient wt = new WithTransient();
+      wt.i = 10;
+      wt.t = 10;
+      Verify.writeObjectToFile(wt, osFileName);
+    }
+
+    if (verifyNoPropertyViolation("+jpf-core.native_classpath+=;${jpf-core}/build/tests")) {
+      WithTransient wt = Verify.readObjectFromFile(WithTransient.class, osFileName);
+
+      assert wt.i == 10;
+      // t is transient
+      assert wt.t == 0;
+    }
+  }
+
+  class SerializableArrayList<T> extends ArrayList<T> implements Serializable {}
+
+  @Test
+  public void testWriteReadArrayList() {
+    if (!isJPFRun()) {
+      ArrayList<Integer> al = new ArrayList<Integer>();
+      al.add(1);
+      al.add(2);
+      al.add(3);
+      Verify.writeObjectToFile(al, osFileName);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      ArrayList al = Verify.readObjectFromFile(ArrayList.class, osFileName);
+
+      assert al.size() == 3;
+      assert al.get(0).equals(1);
+      assert al.get(1).equals(2);
+      assert al.get(2).equals(3);
+    }
+  }
+
+  static class MultiDimArr implements Serializable {
+    int arr [][];
+  }
+
+  @Test
+  public void tsetWriteReadObjectWithMultiDimArray() {
+    if (!isJPFRun()) {
+      MultiDimArr mda = new MultiDimArr();
+      mda.arr = new int[2][];
+      mda.arr[0] = new int[3];
+      mda.arr[1] = new int[3];
+
+      mda.arr[0][0] = 1;
+      mda.arr[0][1] = 2;
+      mda.arr[0][2] = 3;
+
+      mda.arr[1][0] = 4;
+      mda.arr[1][1] = 5;
+      mda.arr[1][2] = 6;
+
+      Verify.writeObjectToFile(mda, osFileName);
+    }
+
+    if (verifyNoPropertyViolation("+jpf-core.native_classpath+=;${jpf-core}/build/tests")) {
+      MultiDimArr mda = Verify.readObjectFromFile(MultiDimArr.class, osFileName);
+
+      assert mda.arr[0][0] == 1;
+      assert mda.arr[0][1] == 2;
+      assert mda.arr[0][2] == 3;
+
+      assert mda.arr[1][0] == 4;
+      assert mda.arr[1][1] == 5;
+      assert mda.arr[1][2] == 6;
+    }
+  }
+
+  static class Inner implements Serializable {
+    int i;
+  }
+
+  static class Outer implements Serializable {
+    Inner inner;
+    int o;
+  }
+
+
+  @Test
+  public void testReadWriteObjectWithReference() {
+    if (!isJPFRun()) {
+      Outer out = new Outer();
+      out.o = 1;
+      out.inner = new Inner();
+      out.inner.i = 2;
+
+      Verify.writeObjectToFile(out, osFileName);
+    }
+
+    if (verifyNoPropertyViolation("+jpf-core.native_classpath+=;${jpf-core}/build/tests")) {
+      Outer out = Verify.readObjectFromFile(Outer.class, osFileName);
+
+      assert out.o == 1;
+      assert out.inner.i == 2;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/PerturbatorTest.java b/src/tests/gov/nasa/jpf/test/mc/data/PerturbatorTest.java
new file mode 100644 (file)
index 0000000..707095d
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for Perturbator listener
+ */
+public class PerturbatorTest extends TestJPF {
+
+  int data = 42;
+
+  public static void main(String[] args) {
+    runTestsOfThisClass(args);
+  }
+
+  @Test
+  public void testIntFieldPerturbation() {
+
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.Perturbator",
+                                  "+perturb.fields=data",
+                                  "+perturb.data.class=.perturb.IntOverUnder",
+                                  "+perturb.data.field=gov.nasa.jpf.test.mc.data.PerturbatorTest.data",
+                                  "+perturb.data.delta=1")){
+      System.out.println("instance field perturbation test");
+      int d = data;
+      System.out.print("d = ");
+      System.out.println(d);
+
+      Verify.incrementCounter(0);
+      switch (Verify.getCounter(0)){
+        case 1: assert d == 43; break;
+        case 2: assert d == 42; break;
+        case 3: assert d == 41; break;
+        default:
+          assert false : "wrong counter value: " + Verify.getCounter(0);
+      }
+
+    } else {
+      assert Verify.getCounter(0) == 3;
+    }
+  }
+
+  @Test
+  public void testFieldPerturbationLocation() {
+
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.Perturbator",
+                                  "+perturb.fields=data",
+                                  "+perturb.data.class=.perturb.IntOverUnder",
+                                  "+perturb.data.field=gov.nasa.jpf.test.mc.data.PerturbatorTest.data",
+                                  "+perturb.data.location=PerturbatorTest.java:87",
+                                  "+perturb.data.delta=1")){
+      System.out.println("instance field location perturbation test");
+
+      int x = data; // this should not be perturbed
+      System.out.print("x = ");
+      System.out.println(x);
+
+      int d = data; // this should be
+      System.out.print("d = ");
+      System.out.println(d);
+
+      Verify.incrementCounter(0);
+
+    } else {
+      assert Verify.getCounter(0) == 3;
+    }
+  }
+
+  int foo (int i) {
+    return i;
+  }
+  
+  int bar (int i, boolean b) {
+       return i-1;
+  }
+
+  int bar (int i){
+    return i-1;
+  }
+
+  @Test
+  public void testIntReturnPerturbation() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.Perturbator",
+                                  "+perturb.returns=foo,bar",
+
+                                  "+perturb.foo.class=.perturb.IntOverUnder",
+                                  "+perturb.foo.method=gov.nasa.jpf.test.mc.data.PerturbatorTest.foo(int)",
+                                  "+perturb.foo.location=PerturbatorTest.java:136",
+                                  "+perturb.foo.delta=1",
+
+                                  "+perturb.bar.class=.perturb.IntOverUnder",
+                                  "+perturb.bar.method=gov.nasa.jpf.test.mc.data.PerturbatorTest.bar(int,boolean)",
+                                  "+perturb.bar.delta=5")){
+      int x, y;
+
+      System.out.println("int return perturbation test");
+
+      x = foo(-1); // this should not be perturbed ('foo' has a location spec)
+      System.out.print("foo() = ");
+      System.out.println(x);
+
+      x = foo(42); // line 136 => this should be
+      System.out.print("foo() = ");
+      System.out.println(x);
+
+      Verify.incrementCounter(0);
+      switch (Verify.getCounter(0)){
+        // foo() preturbations
+        case 1: assert x == 43; break;
+        case 2: assert x == 42; break;
+        case 3: assert x == 41; break;
+        default:
+          assert false : "wrong counter 0 (foo() perturbation) value: " + Verify.getCounter(0);
+      }
+
+      if (x == 41){
+        y = bar(40, false); // this too (no location spec for 'bar')
+        System.out.print("bar() = ");
+        System.out.println(y);
+
+        Verify.incrementCounter(1);
+        switch (Verify.getCounter(1)){
+          // bar() perturbations
+          case 1: assert y == 44; break;
+          case 2: assert y == 39; break;
+          case 3: assert y == 34; break;
+          default:
+            assert false : "wrong counter 1 (bar() perturbation) value: " + Verify.getCounter(1);
+        }
+      }
+
+    } else {
+      assert Verify.getCounter(0) == 3;
+      assert Verify.getCounter(1) == 3;
+    }
+  }
+  
+  static void printParams(int i, boolean b) {
+       System.out.println("(" + i + ", " + b + ")");
+  }
+  
+  static int zoo(int i, boolean b) {
+       printParams(i, b);
+       if (b)
+               return -1 * i;
+       else
+               return i;
+  }
+  
+  void printParam(long i, double d) {
+       System.out.println("(" + i + ", " + d + ")");
+  }
+  
+  double foobar(long i, double d) {
+       printParam(i, d);
+       long j = i;
+       return d + (j % 10);
+  }
+  
+  @Test
+  public void testParamPerturbation() {
+
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.Perturbator",
+                                  "+perturb.params=foo,zoo",
+                                  "+perturb.foo.class=.perturb.GenericDataAbstractor",
+                                  "+perturb.foo.method=gov.nasa.jpf.test.mc.data.PerturbatorTest.foobar(long,double)",
+                                  "+perturb.foo.location=PerturbatorTest.java:233",        // <<<<<<<<< update if file is changed!
+                                  "+perturb.zoo.class=.perturb.GenericDataAbstractor",
+                                  "+perturb.zoo.method=gov.nasa.jpf.test.mc.data.PerturbatorTest.zoo(int,boolean)"
+    )) {
+      System.out.println("parameters perturbation test");
+
+      int e = zoo(42, false);
+      System.out.print("zoo = ");
+      System.out.println(e);
+
+      Verify.incrementCounter(0);
+      switch (Verify.getCounter(0)){
+        case 1: assert e == 21; break;
+        case 2: assert e == -21; break;
+        case 3: assert e == 0; break;
+        case 4: assert e == 0; break;
+        case 5: assert e == -84; break;
+        case 6: assert e == 84; break;
+        default:
+          assert false : "wrong counter value: " + Verify.getCounter(0);
+      }
+    
+      if (e == 84) {
+       double d = foobar(42, 0.0); // no perturbation
+       System.out.print("foobar = ");
+       System.out.println(d);
+
+       d = foobar(42, 0.0); // <<<< yes perturbation, line 233
+       System.out.print("foobar = ");
+       System.out.println(d);
+
+       Verify.incrementCounter(1);
+       switch (Verify.getCounter(1)){
+       case 1: assert Math.abs(d - 0.586) < 0.0001; break;
+       case 2: assert d == 2; break;
+       case 3: assert d == 5.141; break;
+       case 4: assert d == -1.414; break;
+       case 5: assert d == 0; break;
+       case 6: assert d == 3.141; break;
+       case 7: assert d == -1.414; break;
+       case 8: assert d == 0; break;
+       case 9: assert d == 3.141; break;
+       default:
+               assert false : "wrong counter value: " + Verify.getCounter(1);
+       }
+      }
+    } else {
+      assert Verify.getCounter(0) == 6;
+      assert Verify.getCounter(1) == 9;
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/RandomTest.java b/src/tests/gov/nasa/jpf/test/mc/data/RandomTest.java
new file mode 100644 (file)
index 0000000..8c582d2
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * test of gov.nasa.jpf.vm.Verify nondeterministic data initailization
+ */
+public class RandomTest extends TestJPF {
+  private void run (int n){
+    int i = Verify.getInt(0,n); // we should backtrack 0..n times to this location
+    Verify.incrementCounter(0); // counter '0' should have value (n+1) after JPF is done
+    System.out.println(i);
+  }
+
+  @Test public void testRandom () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    if (verifyNoPropertyViolation()){
+      run(3);
+    }
+    if (!isJPFRun()){
+      if (Verify.getCounter(0) != 4){
+        fail("wrong number of paths");
+      }
+    }
+  }
+
+  @Test public void testRandomBFS () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    if (verifyNoPropertyViolation("+search.class=gov.nasa.jpf.search.heuristic.BFSHeuristic")){
+      run(3);
+    }
+    if (!isJPFRun()){
+      if (Verify.getCounter(0) != 4){
+        fail("wrong number of paths");
+      }
+    }
+  }
+
+
+  
+  @Test public void testJavaUtilRandom () {
+
+    if (verifyUnhandledException("java.lang.ArithmeticException", "+cg.enumerate_random=true")) {
+      Random random = new Random(42);      // (1)
+
+      int a = random.nextInt(4);           // (2)
+      System.out.print("a=");
+      System.out.println(a);
+
+      //... lots of code here
+
+      int b = random.nextInt(3);           // (3)
+      System.out.print("a=");
+      System.out.print(a);
+      System.out.print(",b=");
+      System.out.println(b);
+
+
+      int c = a / (b + a - 2);                  // (4)
+      System.out.print("a=");
+      System.out.print(a);
+      System.out.print(",b=");
+      System.out.print(b);
+      System.out.print(",c=");
+      System.out.println(c);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/StopWatchFuzzerTest.java b/src/tests/gov/nasa/jpf/test/mc/data/StopWatchFuzzerTest.java
new file mode 100644 (file)
index 0000000..7eeda85
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for StopWatchFuzzer
+ */
+public class StopWatchFuzzerTest extends TestJPF {
+  
+  @Test
+  public void testPaths() {
+    
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation("+listener=.listener.StopWatchFuzzer")){
+      long tStart = System.currentTimeMillis();
+      System.out.println("some lengthy computation..");
+      long tEnd = System.currentTimeMillis();
+      
+      if (tEnd - tStart <= 5000){
+        System.out.println("all fine, finished in time");
+        Verify.incrementCounter(0); // should get here two times, for < and ==
+      } else {
+        System.out.println("panic, we didn't make it in time");
+        Verify.incrementCounter(1);
+      }
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+      assertTrue( Verify.getCounter(1) == 1);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/TimeModelTest.java b/src/tests/gov/nasa/jpf/test/mc/data/TimeModelTest.java
new file mode 100644 (file)
index 0000000..d4e4734
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for TimeModel implementations
+ */
+public class TimeModelTest extends TestJPF {
+  
+  @Test
+  public void testSystemTime(){
+    
+    if (verifyNoPropertyViolation("+vm.time.class=.vm.SystemTime")){
+      long t1 = System.currentTimeMillis();
+      System.out.printf("t1 = %d\n", t1);
+      
+      boolean b2 = Verify.getBoolean();
+      long t2 = System.currentTimeMillis();
+      System.out.printf("  t2 = %d\n", t2);
+
+      boolean b3 = Verify.getBoolean();
+      long t3 = System.currentTimeMillis();
+      System.out.printf("    t3 = %d\n", t3);
+
+      assertTrue((t3 >= t2) && (t2 >= t1));
+    }
+  }
+  
+  @Test
+  public void testPathTime(){
+    
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+    
+    if (verifyNoPropertyViolation("+vm.time.class=.vm.ConstInsnPathTime")){
+      long t1 = System.currentTimeMillis();
+      System.out.printf("t1 = %d\n", t1);
+      
+      boolean b2 = Verify.getBoolean(true); // we do falseFirst
+      long t2 = System.currentTimeMillis();
+      System.out.printf("  t2 = %d\n", t2);
+      
+      if (b2){ // has to be second time around
+        assertTrue(t2 == Verify.getCounter(0));
+      } else {
+        Verify.setCounter(0,(int)t2);
+      }
+
+
+      boolean b3 = Verify.getBoolean(true);  // store the result so that we don't state match
+      long t3 = System.currentTimeMillis();
+      System.out.printf("    t3 = %d\n", t3);
+
+      if (b3){ // has to be second time around
+        assertTrue(t3 == Verify.getCounter(1));
+      } else {
+        Verify.setCounter(1,(int)t3);
+      }
+      
+      assertTrue((t3 > t2) && (t2 > t1));
+    }
+    
+    
+  }
+  
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/data/TypedObjectChoiceTest.java b/src/tests/gov/nasa/jpf/test/mc/data/TypedObjectChoiceTest.java
new file mode 100644 (file)
index 0000000..1e8c330
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.data;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import org.junit.Test;
+
+/**
+ * regression test for TypedObjectChoice CGs
+ */
+public class TypedObjectChoiceTest extends TestJPF {
+
+  static class A {
+    int id;
+    A (int i){
+      id = i;
+    }
+    public String toString(){ return String.format("A(%d)", id); }
+  }
+
+  @Test
+  public void testNoCG (){
+    if (verifyNoPropertyViolation("+mycg.class=gov.nasa.jpf.vm.choice.TypedObjectChoice",
+            "+mycg.include_null=false",
+            "+mycg.type=" + getClass().getName() + "$A")){
+      Object o = Verify.getObject("mycg");
+      System.out.print("got object: ");
+      System.out.println(o);
+
+      assertTrue( o == null);
+    }
+  }
+
+  @Test
+  public void testNoObject (){
+    if (verifyNoPropertyViolation("+mycg.class=gov.nasa.jpf.vm.choice.TypedObjectChoice",
+                                  "+mycg.include_null=true",
+                                  "+mycg.type=" + getClass().getName() + "$A")){
+      Object o = Verify.getObject("mycg");
+      System.out.print("got object: ");
+      System.out.println(o);
+
+      assertTrue( o == null);
+    }
+  }
+
+  @Test
+  public void testObject (){
+    if (verifyNoPropertyViolation("+mycg.class=gov.nasa.jpf.vm.choice.TypedObjectChoice",
+            "+mycg.include_null=false",
+            "+mycg.type=" + getClass().getName() + "$A")) {
+      A a = new A(42);
+
+      Object o = Verify.getObject("mycg");
+      System.out.print("got object: ");
+      System.out.println(o);
+
+      assertTrue(o instanceof A);
+      a = (A) o;
+      assertTrue(a.id == 42);
+    }
+  }
+
+  @Test
+  public void testMultipleObjects(){
+    if (verifyNoPropertyViolation("+mycg.class=gov.nasa.jpf.vm.choice.TypedObjectChoice",
+            "+mycg.include_null=false",
+            "+mycg.type=" + getClass().getName() + "$A")) {
+      A a1 = new A(1);
+      A a2 = new A(2);
+
+      Object o = Verify.getObject("mycg");
+      System.out.print("got object: ");
+      System.out.println(o);
+
+      assertTrue(o instanceof A);
+      A a = (A) o;
+      Verify.setBitInBitSet(0, a.id, true);
+      Verify.incrementCounter(0);
+    }
+
+    if (!isJPFRun()){
+      assertTrue( Verify.getCounter(0) == 2);
+      assertTrue( Verify.getBitInBitSet(0, 1));
+      assertTrue( Verify.getBitInBitSet(0, 2));
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/AtomicTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/AtomicTest.java
new file mode 100644 (file)
index 0000000..1e05051
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+public class AtomicTest extends TestJPF {
+  
+  static int data = 42;
+  
+  @Test public void testNoRace () {
+    if (verifyNoPropertyViolation("+cg.enable_atomic")) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          System.out.println("  enter run in Thread-0");
+          assert data == 42;
+          data += 1;
+          assert data == 43;
+          data -= 1;
+          assert data == 42;
+          System.out.println("  exit run in Thread-0");
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      Verify.beginAtomic();
+      System.out.println("enter atomic section in main");
+      t.start();
+      assert data == 42;
+      data += 2;
+      assert data == 44;
+      data -= 2;
+      assert data == 42;
+      System.out.println("exit atomic section in main");
+      Verify.endAtomic();
+    }
+  }
+  
+  @Test 
+  public void testDataCG () {
+    if (verifyNoPropertyViolation("+cg.enable_atomic")) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          data += 10;
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      Verify.beginAtomic();
+      t.start();
+      int i = Verify.getInt(1, 2);
+      data += i;
+      assert data < 45 : "data got incremented: " + data;
+      Verify.incrementCounter(0);
+      assert i == Verify.getCounter(0);
+      Verify.endAtomic();
+    }
+  }
+
+  @Test public void testBlockedInAtomic () {
+    if (verifyDeadlock("+cg.enable_atomic")){
+      Runnable r = new Runnable() {
+
+        @Override
+               public synchronized void run() {
+          System.out.println("T notifying..");
+          this.notify();
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      synchronized (r){
+        System.out.println("main going atomic, holding r lock");
+        Verify.beginAtomic();
+        t.start();
+
+        try {
+          System.out.println("main waiting on r");
+          r.wait();
+        } catch (InterruptedException x){
+          System.out.println("main got interrupted");
+        }
+        System.out.println("main leaving atomic");
+        Verify.endAtomic();
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/ClinitTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/ClinitTest.java
new file mode 100644 (file)
index 0000000..1326749
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+/**
+ * regression test for concurrent clinit execution
+ */
+public class ClinitTest extends TestJPF {
+
+  static class X {
+    static int x;
+
+    static {
+      Verify.threadPrintln("initializing X");
+      assertTrue(x == 0);
+      x++;
+    }
+  }
+
+  @Test
+  public void testNoConcurrentClinit() {
+    if (verifyNoPropertyViolation()) {
+
+      Runnable r = new Runnable() {
+        @Override
+        public void run() {
+          int x = X.x;
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      int x = X.x;
+      assertTrue("x = " + x, x == 1);
+    }
+  }
+
+
+  static class Y {
+    static long y;
+
+    static {
+      Thread t = Thread.currentThread();
+      Verify.threadPrintln("initializing Y");
+      y = t.getId();
+    }
+  }
+
+  @Test
+  public void testClinitChoices() {
+    if (verifyAssertionErrorDetails("gotcha")) {
+      Runnable r = new Runnable() {
+        @Override
+        public void run() {
+          long y = Y.y;
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      long y = Y.y;
+      Thread tCur = Thread.currentThread();
+      Verify.threadPrintln("testing Y.y");
+      assertTrue("gotcha", y == tCur.getId());
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/DaemonTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/DaemonTest.java
new file mode 100644 (file)
index 0000000..f162f64
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+/**
+ * test verification of daemon threads, which are different because of their
+ * automatic termination upon exit of the last non-daemon
+ */
+public class DaemonTest extends TestJPF {
+
+  static class T1 extends Thread {
+
+    boolean blowUp = false;
+    //Boolean blowUp = false;
+
+    T1() {
+      setDaemon(true);
+    }
+
+    @Override
+       public void run() {
+      if (blowUp){
+        throw new RuntimeException("blow up");
+      }
+    }
+  }
+
+  
+  /**
+   * test if there is a proper CG BEFORE the main thread terminates, which
+   * would take the daemon down before it has a chance to blow up
+   */
+  @Test
+  public void testRace(){
+    if (verifyUnhandledExceptionDetails("java.lang.RuntimeException", "blow up")){
+      T1 t = new T1();
+      t.start();
+      
+      // this is a shared access CG, but since this is the last statement,
+      // main thread termination would kill the daemon. In a host VM, there
+      // could be still a context switch between the PUTFIELD and the thread
+      // termination
+      t.blowUp = true;
+      
+      int dummy = 42; // totally pointless, but a host VM could still reschedule here
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/DeadlockTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/DeadlockTest.java
new file mode 100644 (file)
index 0000000..6509f23
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * various deadlock detection test
+ */
+public class DeadlockTest extends TestJPF {
+
+  //--- tests
+  abstract class SyncRunnable implements Runnable {
+
+    SyncRunnable other;
+
+    public void setOther(SyncRunnable other) {
+      this.other = other;
+    }
+
+    public abstract void doSomething();
+
+    public abstract void doSomethingElse();
+
+    @Override
+       public abstract void run();
+  }
+
+  /**
+   * Required by the testMissedSignalDeadlock().
+   */
+  class Event {
+
+    int count = 0;
+
+    public synchronized void signal_event() {
+      count = (count + 1) % 3;
+      notifyAll();
+    }
+
+    public synchronized void wait_for_event() {
+      try {
+        wait();
+      } catch (InterruptedException e) {
+      }
+    }
+  }
+
+  /**
+   * Required by the testMissedSignalDeadlock().
+   */
+  class FirstTask extends java.lang.Thread {
+
+    Event event1;
+    Event event2;
+    int count = 0;
+
+    public FirstTask(Event e1, Event e2) {
+      event1 = e1;
+      event2 = e2;
+    }
+
+    @Override
+       public void run() {
+      count = event1.count;
+
+      while (true) {
+        if (count == event1.count) {
+          event1.wait_for_event();
+        }
+
+        count = event1.count;
+        event2.signal_event();
+      }
+    }
+  }
+
+  /**
+   * Required by the testMissedSignalDeadlock().
+   */
+  class SecondTask extends java.lang.Thread {
+
+    Event event1;
+    Event event2;
+    int count = 0;
+
+    public SecondTask(Event e1, Event e2) {
+      event1 = e1;
+      event2 = e2;
+    }
+
+    @Override
+       public void run() {
+      count = event2.count;
+
+      while (true) {
+        event1.signal_event();
+
+        if (count == event2.count) {
+          event2.wait_for_event();
+        }
+
+        count = event2.count;
+      }
+    }
+  }
+
+  class SyncBlockRunnable extends SyncRunnable {
+
+    @Override
+       public void doSomething() {
+      synchronized (this) {
+        other.doSomethingElse();
+      }
+    }
+
+    @Override
+       public void doSomethingElse() {
+      synchronized (this) {
+      }
+    }
+
+    @Override
+       public void run() {
+      //while (true) {
+        synchronized (this) {
+          other.doSomething();
+        }
+      //}
+    }
+  }
+
+  class SyncMthRunnable extends SyncRunnable {
+
+    @Override
+       public synchronized void doSomething() {
+      other.doSomethingElse();
+    }
+
+    @Override
+       public synchronized void doSomethingElse() {
+    }
+
+    @Override
+       public synchronized void run() {
+      //while (true) {
+        other.doSomething();
+      //}
+    }
+  }
+  static Object lock1 = new Object();
+  static Object lock2 = new Object();
+  static int counter;
+
+  /**
+   * classical asymmetric lock order deadlock
+   */
+  @Test
+  public void testLockOrderDeadlock() {
+    if (verifyDeadlock()) {
+      Thread t1 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (lock1) {
+            synchronized (lock2) {
+              counter++;
+            }
+          }
+          System.out.println("t1 finished");
+        }
+      });
+
+      Thread t2 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (lock2) {
+            synchronized (lock1) {
+              counter--;
+            }
+          }
+          System.out.println("t2 finished");
+        }
+      });
+
+      t1.start();
+      t2.start();
+    }
+  }
+  /**
+   * a nested monitor lockout
+   */
+  static Object lock = new Object();
+  static Object sig = new Object();
+
+  @Test
+  public void testNestedMonitorLockoutDeadlock() {
+    if (verifyDeadlock()) {
+      Thread t1 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          for (int i = 0; i < 3; i++) {
+            synchronized (lock) {
+              synchronized (sig) {
+                try {
+                  sig.wait();
+                } catch (InterruptedException ix) {
+                }
+              }
+            }
+          }
+          System.out.println("t1 finished");
+        }
+      });
+
+      Thread t2 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          for (int i = 0; i < 2; i++) {
+            synchronized (sig) {
+              synchronized (lock) {
+              }
+              sig.notify();
+            }
+          }
+          System.out.println("t2 finished");
+        }
+      });
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  @Test
+  public void testSimpleMissedSignal() {
+    if (verifyDeadlock()) {
+
+      Thread t1 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (sig) {
+            try {
+              sig.wait();
+            } catch (InterruptedException ix) {
+            }
+          }
+          System.out.println("t1 finished");
+        }
+      });
+
+      Thread t2 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (sig) {
+            sig.notify();
+          }
+          System.out.println("t2 finished");
+        }
+      });
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  /**
+   * Testing for deadlocks resulting from missed signal. This is the original
+   * oldclassic example which has been turned into a test case.
+   * as the name implies - a missed signal deadlock
+   */
+  @Test
+  public void testMissedSignalDeadlock() {
+    if (verifyDeadlock()) {
+
+      Event new_event1 = new Event();
+      Event new_event2 = new Event();
+
+      FirstTask task1 = new FirstTask(new_event1, new_event2);
+      SecondTask task2 = new SecondTask(new_event1, new_event2);
+
+      task1.start();
+      task2.start();
+    }
+  }
+
+  /**
+   * the test which mixes synchronized method attr with MONITORENTER/EXIT
+   * simple lock order deadlock
+   */
+  @Test
+  public void testMixedDeadlock() {
+    if (verifyDeadlock()) {
+
+      SyncRunnable r1 = new SyncMthRunnable();
+      SyncRunnable r2 = new SyncBlockRunnable();
+      r1.setOther(r2);
+      r2.setOther(r1);
+
+      //Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+      Thread t1 = new Thread(r1);
+
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  /**
+   * the test which checks MONITORENTER / MONITOREXIT
+   * simple lock order deadlock
+   */
+  @Test
+  public void testSyncBlockDeadlock() {
+    if (verifyDeadlock()) {
+
+      SyncRunnable r1 = new SyncBlockRunnable();
+      SyncRunnable r2 = new SyncBlockRunnable();
+      r1.setOther(r2);
+      r2.setOther(r1);
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  /**
+   * the test which checks the synchronized method attribute
+   * simple lock order deadlock
+   */
+  @Test
+  public void testSyncMthDeadlock() {
+    if (verifyDeadlock()) {
+
+      SyncRunnable r1 = new SyncMthRunnable();
+      SyncRunnable r2 = new SyncMthRunnable();
+      r1.setOther(r2);
+      r2.setOther(r1);
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  @Test
+  public void testTerminationDeadlock() {
+    if (verifyDeadlock()){
+      Thread t = new Thread(){
+        @Override
+               public void run(){
+          System.out.println("# t running");
+          synchronized(this){
+            System.out.println("# t waiting (forever)..");
+            try {
+              wait();
+            } catch (InterruptedException ix){
+              fail("t got unexpectedly interrupted");
+            }
+          }
+        }
+      };
+      t.start();
+      System.out.println("# main thread terminating");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/ExceptionalThreadChoiceTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/ExceptionalThreadChoiceTest.java
new file mode 100644 (file)
index 0000000..c45f0ce
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+import java.io.IOException;
+import java.net.SocketTimeoutException;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ExceptionalThreadChoiceTest extends TestJPF {
+
+  native void foo() throws IOException, SocketTimeoutException; // this gets rescheduled with exceptions
+  
+  @Test
+  public void testExceptions (){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+      Verify.resetCounter(2);
+    }
+    
+    if (verifyNoPropertyViolation()){
+      try {
+        foo();
+        System.out.println("main no exception");
+        Verify.incrementCounter(0);
+      }  catch (SocketTimeoutException stox){ // order matters since SocketTimeoutException is also a IOException
+        System.out.println("main got SocketTimeoutException");
+        Verify.incrementCounter(1);
+      } catch (IOException iox){
+        System.out.println("main got IOException");
+        Verify.incrementCounter(2);
+      }
+    }
+    
+    if (!isJPFRun()){
+      assertTrue( "nominal path missing", Verify.getCounter(0) > 0);
+      assertTrue( "SocketTimeoutException missing", Verify.getCounter(1) > 0);
+      assertTrue( "IOException missing", Verify.getCounter(2) > 0);
+    }    
+  }
+  
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/FinalizerThreadTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/FinalizerThreadTest.java
new file mode 100644 (file)
index 0000000..690a9fe
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Test suit for FinalizerThread & FinalizeThreadInfo
+ */
+public class FinalizerThreadTest extends TestJPF {
+  
+  static class Finalize {
+    static void createFinalize() {
+      new Finalize();
+    }
+    
+    @Override
+    protected void finalize() throws Throwable {
+      System.out.println("finalizer executing... ");
+      throw new Exception();
+    }
+  }
+  
+  @Test
+  public void testExceptionFromFinalizer (){
+    if (verifyNoPropertyViolation( "+vm.process_finalizers=true")){
+      // FinalizerThread should swallow the exception thrown in the finalize() method
+      new Finalize();
+    }
+  }
+  
+  public static class FinalizerThreadListener extends ListenerAdapter {
+
+    @Override
+    public void stateAdvanced(Search search){
+      if(search.isEndState()) {
+        ThreadInfo currTi = search.getVM().getCurrentThread();
+        ThreadInfo finalizerTi = search.getVM().getFinalizerThread();
+        
+        // make sure a finalizer thread exists
+        assertTrue(finalizerTi!=null);
+        
+        // make sure the thread leading to the end state is finalizer
+        assertEquals(currTi, currTi);
+      }
+    }
+  }
+  
+  private static String[] JPF_ARGS = { "+vm.process_finalizers=true",
+                                       "+listener=gov.nasa.jpf.test.mc.threads.FinalizerThreadTest$FinalizerThreadListener"};  
+  @Test
+  public void testFinalizerThreadRunning () {
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Finalize.createFinalize();
+    }
+  }
+  
+  // This is to make sure that an idle finalizer does not cause a deadlock in single-process apps
+  @Test
+  public void testIdleFinalizerThread () {
+    if (verifyNoPropertyViolation("+vm.process_finalizers=true")){
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/HORaceTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/HORaceTest.java
new file mode 100644 (file)
index 0000000..5bd29d6
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * high order data race
+ */
+public class HORaceTest extends TestJPF {
+
+  static class D {
+    int a;
+    int b;
+
+    D (int x, int y){
+      a = x;
+      b = y;
+    }
+
+    D (D other){
+      setA( other.getA());
+      Thread.yield(); // give the 2nd thread a chance to interfere
+      setB( other.getB());
+    }
+
+    synchronized int getA() { return a; }
+    synchronized int getB() { return b; }
+    synchronized void setA(int x){ a = x; }
+    synchronized void setB(int x){ b = x; }
+
+    synchronized void change(int delta) {
+      a += delta;
+      b += delta;
+    }
+
+    synchronized boolean isConsistent() {
+      return a == b;
+    }
+  }
+
+  static D d1;
+  static D d2;
+
+  @Test
+  public void testHighOrderRace() {
+
+    if (verifyAssertionErrorDetails("inconsistent d2")) {
+      d1 = new D(42, 42);
+
+      Thread t1 = new Thread() {
+
+        @Override
+               public void run() {
+          d2 = new D(d1);
+        }
+      };
+      Thread t2 = new Thread() {
+
+        @Override
+               public void run() {
+          d1.change(-1);
+        }
+      };
+
+      t1.start();
+      t2.start();
+
+      try {
+        t1.join();
+        t2.join();
+      } catch (InterruptedException ix) {
+        fail("unexpected interrupt during {t1,t2}.join()");
+      }
+
+      System.out.print("d2 = ");
+      System.out.print(d2.a);
+      System.out.print(',');
+      System.out.println(d2.b);
+
+      assert d2.isConsistent() : "inconsistent d2";
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/JPF_gov_nasa_jpf_test_mc_threads_ExceptionalThreadChoiceTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/JPF_gov_nasa_jpf_test_mc_threads_ExceptionalThreadChoiceTest.java
new file mode 100644 (file)
index 0000000..7a18c22
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.choice.ExceptionThreadChoiceFromSet;
+
+/**
+ *
+ */
+public class JPF_gov_nasa_jpf_test_mc_threads_ExceptionalThreadChoiceTest extends NativePeer {
+
+  @MJI
+  public void foo____V (MJIEnv env, int objRef){
+    
+    ThreadInfo ti = env.getThreadInfo();
+    if (!ti.isFirstStepInsn()){
+      String[] exceptions = { "java.net.SocketTimeoutException", "java.io.IOException" };
+      
+      System.out.println("    in top half of native foo()");
+      VM vm = ti.getVM();
+      ThreadInfo[] runnables = vm.getThreadList().getAllMatching(vm.getTimedoutRunnablePredicate());
+      ExceptionThreadChoiceFromSet cg = new ExceptionThreadChoiceFromSet("FOO_CG", runnables, ti, exceptions);
+      
+      ti.getVM().getSystemState().setNextChoiceGenerator(cg);
+      env.repeatInvocation();
+      
+    } else {
+      System.out.println("    in bottom half of native foo()");
+      
+      ExceptionThreadChoiceFromSet cg = ti.getVM().getCurrentChoiceGenerator("FOO_CG", ExceptionThreadChoiceFromSet.class);
+      if (cg == null){
+        throw new JPFException("wrong CG: " + cg);
+      }
+        
+      String exceptionName = cg.getExceptionForCurrentChoice();
+      if (exceptionName != null){
+        env.throwException(exceptionName);
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/MinimizePreemptionTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/MinimizePreemptionTest.java
new file mode 100644 (file)
index 0000000..78f036c
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadChoiceGenerator;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for MinimizePreemption heuristic search
+ */
+public class MinimizePreemptionTest extends TestJPF {
+  
+  static final String SEARCH_OPT = "+search.class=.search.heuristic.MinimizePreemption";
+  
+  public static class A implements Runnable {
+    static int d = 0;  // shared field
+    
+    private final int trip;
+    
+    public A (int dTrip){
+      trip = dTrip;
+    }
+    
+    @Override
+       public void run(){
+      int s = 0;
+      for (int i=1; i<=5; i++){ // use loop var to make sure there always is a state change
+        s = d*10 + i;  // a shared GETSTATIC scheduling point
+      }
+      
+      System.out.println(s);      
+      Verify.incrementCounter(0); // number of completions
+      
+      assert s/10 != trip : "gotcha";
+    }
+  }
+  
+  //----------------------------------------------------------------------------
+    
+  @Test
+  public void testNoPreemptions(){
+    
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+    
+    if (verifyAssertionError( SEARCH_OPT)){
+      A a = new A(0);
+      Thread t = new Thread(a);
+      t.start();
+      
+      for (int i=0; i<10; i++){ // use loop var to make sure there always is a state change
+        A.d = i; // that's a PUTSTATIC scheduling point per cycle
+      } 
+    }
+    
+    if (!isJPFRun()){
+      int nRunCompletions = Verify.getCounter(0);
+      System.out.printf("number of A(0) run() completions = %d\n", nRunCompletions);
+      assertTrue ("preemptions should be <= 2: (t* m+) or (m* t+)", 
+              (nRunCompletions <=2));
+    }
+  }
+  
+  
+  //----------------------------------------------------------------------------
+  
+  // <2do> not a test yet
+
+  static int maxPreemptions; // just outside JPF
+  
+  public static class Listener extends ListenerAdapter {
+
+    int getPreemptions (Search search){
+      VM vm = search.getVM();
+      int n=0;
+      ThreadChoiceGenerator cg = vm.getLastChoiceGeneratorOfType(ThreadChoiceGenerator.class);
+      
+      while (cg != null){
+        ThreadChoiceGenerator cgPrev = cg.getPreviousChoiceGeneratorOfType(ThreadChoiceGenerator.class);
+        if (cg.isSchedulingPoint()) {
+          System.out.println("       " + cg);
+          if (cgPrev != null) {
+            ThreadInfo ti = cg.getNextChoice();
+            ThreadInfo tiPrev = cgPrev.getNextChoice();
+
+            if (tiPrev != ti && cg.contains(tiPrev)) {
+              n++;
+            }
+          }
+        }
+        
+        cg = cgPrev;
+      }
+      
+      return n;
+    }
+    
+    // for the next queued state - preemption count should never decrease
+    @Override
+    public void stateRestored(Search search){
+      int nPreemptions = getPreemptions(search);
+      System.out.println("   explore state with " + nPreemptions + " preemptions");
+      if (nPreemptions > maxPreemptions){
+        maxPreemptions = nPreemptions;
+      } else if (nPreemptions < maxPreemptions){
+        fail("number of preemptions has to be increasing!");
+      }
+    }
+  }
+  
+  @Test
+  public void testPreemptionCount(){
+    String listener = null;
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      listener = "+listener=" + Listener.class.getName();
+    }
+    
+    if (verifyAssertionError( SEARCH_OPT, listener)){
+      A a = new A(1); // means we need one main cycle
+      Thread t = new Thread(a);
+      t.start();
+
+      for (int i=1; i<5; i++){
+        A.d = i;
+      }
+    }
+    
+    if (!isJPFRun()){
+      int nRunCompletions = Verify.getCounter(0);
+      System.out.printf("completions = %d\n", nRunCompletions);
+    }    
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/MissedPathTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/MissedPathTest.java
new file mode 100644 (file)
index 0000000..16b89cc
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+/**
+ * test for missed paths in concurrent threads with very little interaction
+ */
+public class MissedPathTest extends TestJPF {
+
+  static class X {
+    boolean pass;
+  }
+  
+  static class InstanceFieldPropagation extends Thread {
+    X myX; // initially not set
+
+    @Override
+       public void run() {
+      if (myX != null){
+        Verify.println("T: accessed global myX");
+        if (!myX.pass){  // (2) won't fail unless main is between (0) and (1)
+          throw new AssertionError("gotcha");
+        }
+      }
+    }    
+  }
+
+  @Test
+  public void testInstanceFieldPropagation () {
+    if (verifyAssertionErrorDetails("gotcha", "+vm.shared.break_on_exposure=true")) {
+      InstanceFieldPropagation mp = new InstanceFieldPropagation();
+      mp.start();
+      
+      X x = new X();
+      Verify.println("M: new " + x);
+      mp.myX = x;        // (0) exposure - x cannot become shared until this GOT executed
+     
+      //Thread.yield();  // this would expose the error
+      Verify.println("M: x.pass=true");
+      x.pass = true;     // (1) need to break BEFORE assignment or no error
+    }
+  }
+  
+  //----------------------------------------------------------------------------------
+  
+  static class Y {
+    X x;
+  }
+  
+  static Y globalY; // initially not set
+  
+  static class StaticFieldPropagation extends Thread {
+    @Override
+       public void run(){
+      if (globalY != null){
+        if (!globalY.x.pass){  // (2) won't fail unless main is between (0) and (1)
+          throw new AssertionError("gotcha");
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testStaticFieldPropagation () {
+    if (verifyAssertionErrorDetails("gotcha", "+vm.shared.break_on_exposure=true")) {
+      StaticFieldPropagation mp = new StaticFieldPropagation();
+      mp.start();
+
+      X x = new X();
+      Y y = new Y();
+      y.x = x;
+      
+      globalY = y;       // (0) x not shared until this GOT executed
+
+      //Thread.yield();  // this would expose the error
+      x.pass = true;     // (1) need to break BEFORE assignment or no error
+    }    
+  }
+  
+  //-------------------------------------------------------------------------------
+  
+  static class PutContender extends Thread {
+    X myX;
+
+    @Override
+       public void run () {
+      myX = new X();  // competing put with exposure
+
+      if (myX != null) {  // doesn't matter, we just want to GET myX
+        Verify.println("T: accessed global myX");
+      }
+    }
+  }
+  
+  // this does not really belong here since it doesn't test not missing paths, but
+  // if the exposure CGs we use to avoid missing paths are not causing infinite loops.
+  // NOTE: turning off state matching is crucial here
+  @Test
+  public void testCompetingExposures(){
+    if (verifyNoPropertyViolation("+vm.storage.class=nil")){
+      PutContender mp = new PutContender();
+      mp.start();
+
+      X x = new X();
+      Verify.println("M: new " + x);
+      mp.myX = x;    // this is one of the competing PUTs
+
+      Verify.println("M: x.pass=true");
+      x.pass = true; // irrelevant in this case
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/NestedInitTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/NestedInitTest.java
new file mode 100644 (file)
index 0000000..7d154fe
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * test deadlock detection during concurrent class init.
+ *
+ * This models hotspot behavior that can explode the state space
+ * and hence needs to be explicitly configured to perform
+ * nested locking during class init
+ */
+public class NestedInitTest extends TestJPF {
+
+  //------------------------------------------------ check normal clinit execution
+  static class Root {
+    static int data;
+
+    static {
+      System.out.print( Thread.currentThread().getName());
+      System.out.println(" in Root.<clinit>()");
+      data = 40;
+    }
+  }
+
+  static class Base extends Root {
+    static int data;
+
+    static {
+      System.out.print( Thread.currentThread().getName());
+      System.out.println(" in Base.<clinit>()");
+      data = Root.data + 1;
+    }
+  }
+
+  static class Derived extends Base {
+    static int data;
+
+    static {
+      System.out.print( Thread.currentThread().getName());
+      System.out.println(" in Derived.<clinit>()");
+      data = Base.data + 1;
+    }
+  }
+
+  @Test
+  public void testNestedInitSingleOk() {
+    if (verifyNoPropertyViolation("+jvm.nested_init")){
+      new Derived(); // force clinit
+      System.out.print("Derived.data = ");
+      System.out.println(Derived.data);
+      assertTrue(Derived.data == 42);
+    }
+  }
+
+  @Test
+  public void testNestedInitConcurrentOk() {
+    if (verifyNoPropertyViolation("+jvm.nested_init")){
+      new Thread( new Runnable(){
+        public void run(){
+          new Derived();
+          System.out.print("t: Derived.data = ");
+          System.out.println(Derived.data);
+          assertTrue(Derived.data == 42);
+        }
+      }).start();
+
+      new Derived(); // force clinit
+      System.out.print("main: Derived.data = ");
+      System.out.println(Derived.data);
+      assertTrue(Derived.data == 42);
+    }
+  }
+
+  //--- and now the nasty cases
+
+  //------------------------------------------ symmetric case
+
+  static class A {
+    static final B b = new B();
+  }
+
+  static class B {
+    static final A a = new A();
+  }
+
+  @Test
+  public void testSymmetricDeadlock() {
+    if (verifyDeadlock("+jvm.nested_init")) {
+      new Thread() {
+        public void run() {
+          new A();
+        }
+      }.start();
+      new B();
+    }
+  }
+
+  //------------------------------------------- hierarchical case
+  public static class CyclicBase {
+    static CyclicDerived sub = new CyclicDerived();
+  }
+
+  public static class CyclicDerived extends CyclicBase {
+  }
+
+
+  @Test
+  public void testCyclicHierarchyDeadlock (){
+    if (verifyDeadlock("+jvm.nested_init")) {
+      new Thread() {
+        public void run() {
+          new CyclicDerived(); // causes class inits via CyclicDerived
+        }
+      }.start();
+
+      Object o = CyclicBase.sub; // causes class inits via CyclicBase
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/OldClassicTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/OldClassicTest.java
new file mode 100644 (file)
index 0000000..9d93d97
--- /dev/null
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+public class OldClassicTest extends TestJPF {
+
+  /**************************** tests **********************************/
+  class Event {
+
+    int count = 0;
+
+    public synchronized void signal_event() {
+      count = (count + 1) % 3;
+      notifyAll();
+    }
+
+    public synchronized void wait_for_event() {
+      try {
+        wait();
+      } catch (InterruptedException e) {
+      }
+    }
+  }
+
+  class FirstTask extends java.lang.Thread {
+
+    Event event1;
+    Event event2;
+    int count = 0;
+
+    public FirstTask(Event e1, Event e2) {
+      this.event1 = e1;
+      this.event2 = e2;
+    }
+
+    @Override
+       public void run() {
+      count = event1.count;
+
+      while (true) {
+        if (count == event1.count) {
+          //assert (count == event1.count);
+          event1.wait_for_event();
+        }
+        count = event1.count;
+        event2.signal_event();
+      }
+    }
+  }
+
+  class SecondTask extends java.lang.Thread {
+
+    Event event1;
+    Event event2;
+    int count = 0;
+
+    public SecondTask(Event e1, Event e2) {
+      this.event1 = e1;
+      this.event2 = e2;
+    }
+
+    @Override
+       public void run() {
+      count = event2.count;
+
+      while (true) {
+        event1.signal_event();
+        if (count == event2.count) {
+          //assert (count == event2.count);
+          event2.wait_for_event();
+        }
+        count = event2.count;
+      }
+    }
+  }
+
+  public void run() {
+    Event new_event1 = new Event();
+    Event new_event2 = new Event();
+
+    FirstTask task1 = new FirstTask(new_event1, new_event2);
+    SecondTask task2 = new SecondTask(new_event1, new_event2);
+
+    task1.start();
+    task2.start();
+
+  }
+
+  /**
+   * Tests running the Crossing example with no heuristics, i.e., with the
+   * default DFS.
+   */
+  @Test
+  public void testDFSearch() {
+    if (verifyDeadlock()) {
+      run();
+    }
+  }
+
+  /**
+   * Tests running the Crossing example with BFS heuristic.
+   */
+  @Test
+  public void testBFSHeuristic() {
+    if (verifyDeadlock("+search.class=gov.nasa.jpf.search.heuristic.BFSHeuristic")) {
+      run();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/RaceTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/RaceTest.java
new file mode 100644 (file)
index 0000000..91186b9
--- /dev/null
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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.
+ */
+/**
+ * this is a raw test class for detection of thread-shared fields, i.e.
+ * it executes the garbage collection based reachability analysis
+ */
+
+package gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+class SharedObject {
+  int instanceField;
+  int whatEver;
+}
+
+
+public class RaceTest extends TestJPF {
+
+  static final TypeRef PROPERTY = new TypeRef("gov.nasa.jpf.listener.PreciseRaceDetector");
+  static final String LISTENER = "+listener=gov.nasa.jpf.listener.PreciseRaceDetector";
+
+  static int staticField;
+
+  @Test
+  public void testStaticRace () {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+
+      Runnable r1 = new Runnable() {
+
+        @Override
+               public void run() {
+          staticField = 1;
+          if (staticField != 1) {
+            throw new RuntimeException("r1 detected race!");
+          }
+        }
+      };
+
+      Runnable r2 = new Runnable() {
+
+        @Override
+               public void run() {
+          staticField = 0;
+          if (staticField != 0) {
+            throw new RuntimeException("r2 detected race!");
+          }
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  @Test 
+  public void testStaticRaceNoThrow () {
+    if (verifyPropertyViolation(PROPERTY, LISTENER)) {
+      Runnable r1 = new Runnable() {
+
+        @Override
+               public void run() {
+          staticField = 1;
+        }
+      };
+
+      Runnable r2 = new Runnable() {
+
+        @Override
+               public void run() {
+          staticField = 0;
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+  
+  
+  // this represents the case where the class loading thread is non-deterministic
+  
+  static class Container {
+    static int data; // that's what we race for
+  }
+  
+  static class StaticRacer extends Thread {
+    @Override
+       public void run(){
+      Container.data++;
+    }
+  }
+  
+  @Test
+  public void testSymmetricStaticRace(){
+    if (verifyUnhandledExceptionDetails("java.lang.RuntimeException", "got race",
+                                        "+vm.scheduler.sharedness.class=.vm.GlobalSharednessPolicy")) {
+      StaticRacer t1 = new StaticRacer();
+      StaticRacer t2 = new StaticRacer();
+      t1.start();
+      t2.start();
+      try {
+        t1.join();
+        t2.join();
+      } catch (InterruptedException ix){
+        fail("got interrupted");
+      }
+      
+      if (Container.data != 2){
+        System.out.print("Container.data = ");
+        System.out.print( Container.data);
+        System.out.println(" => throwing RuntimeException");
+        throw new RuntimeException("got race");
+      }
+    }
+  }
+  
+  @Test
+  public void testInstanceRace () {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+      final SharedObject o = new SharedObject();
+
+      Runnable r1 = new Runnable() {
+
+        SharedObject d = o;
+
+        @Override
+               public void run() {
+          d.instanceField = 1;
+          if (d.instanceField != 1) {
+            throw new RuntimeException("r1 detected race!");
+          }
+        }
+      };
+
+      Runnable r2 = new Runnable() {
+
+        SharedObject d = o;
+
+        @Override
+               public void run() {
+          d.instanceField = 0;
+          if (d.instanceField != 0) {
+            throw new RuntimeException("r2 detected race!");
+          }
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  @Test
+  public void testInstanceRaceNoThrow () {
+    if (verifyPropertyViolation(PROPERTY, LISTENER)) {
+      final SharedObject o = new SharedObject();
+
+      Runnable r1 = new Runnable() {
+
+        SharedObject d = o;
+
+        @Override
+               public void run() {
+          d.instanceField = 1;
+        }
+      };
+
+      Runnable r2 = new Runnable() {
+
+        SharedObject d = o;
+
+        @Override
+               public void run() {
+          d.instanceField = 0;
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  @Test
+  public void testInstanceRaceListenerExclude () {
+    if (verifyNoPropertyViolation(LISTENER, "+race.exclude="+ RaceTest.class.getName() + "*")){
+      testInstanceRaceNoThrow();
+    }
+  }
+
+  @Test
+  public void testInstanceRaceListenerInclude () {
+    if (verifyPropertyViolation(PROPERTY, LISTENER,
+                                 "+race.include=" + RaceTest.class.getName() + "*")){
+      testInstanceRaceNoThrow();
+    }
+  }
+
+  @Test
+  public void testStaticRaceListenerIncludeOther () {
+    if (verifyNoPropertyViolation(LISTENER, "+race.include=sho.bi.Doo*")){
+      testStaticRaceNoThrow();
+    }
+  }
+
+  @Test
+  public void testArrayRaceNoThrow () {
+    if (verifyPropertyViolation(PROPERTY, LISTENER, "+cg.threads.break_arrays")){
+      final int[] shared = new int[1];
+
+      Runnable r1 = new Runnable(){
+        int[] a = shared;
+        @Override
+               public void run() {
+          a[0] = 0;
+        }
+      };
+
+      Runnable r2 = new Runnable(){
+        int[] a = shared;
+        @Override
+               public void run() {
+          a[0] = 1;
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+  /*
+   * mostly the same as above except of that the race candidates are the same insn instance, i.e. use the same
+   * cached insn fields values
+   */
+  static class AT extends Thread {
+    int[] a;
+    int idx;
+    
+    AT (int[] a, int idx) {
+      this.a = a;
+      this.idx = idx;
+    }
+    
+    @Override
+       public void run (){
+      //assertTrue( a[idx] == 0);
+      a[idx] = 1;
+    }
+  }
+  
+  @Test
+  public void testNoArrayRaceSameInsn (){
+    if (verifyNoPropertyViolation(LISTENER, "+cg.threads.break_arrays")){
+      int[] a = new int[2];
+      AT t1 = new AT(a, 0);
+      t1.start();
+      AT t2 = new AT(a, 1);
+      t2.start();
+    }
+  }
+
+  // the dual
+  @Test
+  public void testArrayRaceSameInsn (){
+    if (verifyPropertyViolation(PROPERTY, LISTENER, "+cg.threads.break_arrays")){
+      int[] a = new int[2];
+      AT t1 = new AT(a, 1);
+      t1.start();
+      AT t2 = new AT(a, 1);
+      t2.start();
+    }
+  }
+  
+  
+  @Test
+  public void testNoArrayRaceElements () {
+    if (verifyNoPropertyViolation(LISTENER, "+cg.threads.break_arrays")){
+      final int[] shared = new int[2];
+
+      Runnable r1 = new Runnable(){
+        int[] a = shared;
+        @Override
+               public void run() {
+          a[0] = 0;
+        }
+      };
+
+      Runnable r2 = new Runnable(){
+        int[] a = shared;
+        @Override
+               public void run() {
+          a[1] = 1;
+        }
+      };
+
+      Thread t1 = new Thread(r1);
+      Thread t2 = new Thread(r2);
+
+      t1.start();
+      t2.start();
+    }
+  }
+
+
+  //--- these are tests to check false positives
+
+  static class SameInsnRunnable implements Runnable {
+    SharedObject o = new SharedObject();
+
+    @Override
+       public void run () {
+      o.instanceField = 42;  // same insn, different 'o', no race
+    }
+  }
+
+  @Test
+  public void testSameInsnOtherObject () {
+    if (verifyNoPropertyViolation(LISTENER)) {
+      SameInsnRunnable r1 = new SameInsnRunnable();
+      SameInsnRunnable r2 = new SameInsnRunnable();
+
+      Thread t = new Thread(r1);
+      t.start();
+
+      r2.run();
+    }
+  }
+
+  @Test
+  public void testSameObjectOtherField() {
+    if (verifyNoPropertyViolation(LISTENER)) {
+      final SharedObject o = new SharedObject();
+
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          o.instanceField = 42;
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      o.whatEver = -42;  // different field, no race
+    }
+  }
+  
+  
+  //--- try variations of locks
+  
+  class AnotherSharedObject {
+    Object lock1 = new Object();
+    Object lock2 = new Object();
+    
+    int x = 0;
+  }
+  
+  @Test
+  public void testNoSync() {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+
+      final AnotherSharedObject o = new AnotherSharedObject();
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          o.x++;
+          if (o.x == 0) {
+            throw new RuntimeException("testNoSync race");
+          }
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      o.x--;
+    }
+  }
+  
+  
+  @Test
+  public void testTSync() {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+
+      final AnotherSharedObject o = new AnotherSharedObject();
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (o.lock1) {
+            o.x++;
+            if (o.x == 0) {
+              throw new RuntimeException("testT1Sync race");
+            }
+          }
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      // no sync
+      o.x--;
+    }
+  }
+  
+  @Test
+  public void testMainSync () {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+
+      final AnotherSharedObject o = new AnotherSharedObject();
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          // not synchronized
+          o.x++;
+          if (o.x == 0) {
+            throw new RuntimeException("testMainSync race");
+          }
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      synchronized (o.lock1) {
+        o.x--;
+      }
+    }
+  }
+  
+  @Test
+  public void testBothSync () {
+    if (verifyNoPropertyViolation()) {
+      final AnotherSharedObject o = new AnotherSharedObject();
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (o.lock1) {
+            o.x++;
+            if (o.x == 0) {
+              throw new RuntimeException("testBothSync race??");
+            }
+          }
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      synchronized (o.lock1) {
+        o.x = 0;
+      }
+    }
+  }
+
+  @Test
+  public void testWrongSync () {
+    if (verifyUnhandledException("java.lang.RuntimeException")) {
+
+      final AnotherSharedObject o = new AnotherSharedObject();
+
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (o.lock1) {
+            o.x++;
+            if (o.x == 0) {
+              throw new RuntimeException("testWrongSync race");
+            }
+          }
+        }
+      };
+      Thread t = new Thread(r);
+      t.start();
+
+      synchronized (o.lock2) {
+        o.x--;
+      }
+    }
+  }
+}
+
+
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest-output b/src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest-output
new file mode 100644 (file)
index 0000000..b05b5ea
--- /dev/null
@@ -0,0 +1,17 @@
+main starting T
+main finished
+T started
+T sleeping
+T finished
+~~~~~
+main starting T
+T started
+T sleeping
+main finished
+T finished
+~~~~~
+main starting T
+T started
+T sleeping
+T finished
+main finished
diff --git a/src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest.java b/src/tests/gov/nasa/jpf/test/mc/threads/SchedulesTest.java
new file mode 100644 (file)
index 0000000..63c9f50
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.mc.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+
+
+public class SchedulesTest extends TestJPF {
+  
+  @Test public void testSleep () {
+
+    if (verifyNoPropertyViolation("+cg.threads.break_start=true",
+                                  "+cg.threads.break_yield=true",
+                                  "+cg.threads.break_sleep=true",
+                                  "+listener=.listener.PathOutputMonitor",
+                                  "+pom.all=test/gov/nasa/jpf/test/mc/threads/SchedulesTest-output")) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          System.out.println("T started");
+          try {
+            System.out.println("T sleeping");
+            Thread.sleep(100);
+          } catch (InterruptedException ix) {
+            throw new RuntimeException("unexpected interrupt");
+          }
+          System.out.println("T finished");
+        }
+      };
+
+      Thread t = new Thread(r);
+      System.out.println("main starting T");
+      t.start();
+
+      System.out.println("main finished");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/AnnotationTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/AnnotationTest.java
new file mode 100644 (file)
index 0000000..ec307d8
--- /dev/null
@@ -0,0 +1,556 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.bytecode.GETFIELD;
+import gov.nasa.jpf.jvm.bytecode.JVMInvokeInstruction;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.AnnotationInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.Instruction;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+
+public class AnnotationTest extends TestJPF {
+
+  @Test //----------------------------------------------------------------------
+  @A1("foo")
+  public void testStringValueOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testStringValueOk");
+        A1 annotation = method.getAnnotation(A1.class);
+
+        assert ("foo".equals(annotation.value()));
+        
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A1 {
+    String value();
+  }
+
+
+  @Test //----------------------------------------------------------------------
+  @A2({"foo", "boo"})
+  public void testStringArrayValueOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testStringArrayValueOk");
+        A2 annotation = method.getAnnotation(A2.class);
+
+        Object v = annotation.value();
+        assert v instanceof String[];
+
+        String[] a = (String[])v;
+        assert a.length == 2;
+
+        assert "foo".equals(a[0]);
+        assert "boo".equals(a[1]);
+
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A2 {
+    String[] value();
+  }
+
+  @Test //----------------------------------------------------------------------
+  @A3(Long.MAX_VALUE)
+  public void testLongValueOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testLongValueOk");
+        A3 annotation = method.getAnnotation(A3.class);
+
+        assert (annotation.value() == Long.MAX_VALUE);
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A3 {
+    long value();
+  }
+
+
+  @Test //----------------------------------------------------------------------
+  @A4(a="one",b=42.0)
+  public void testNamedParamsOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testNamedParamsOk");
+        A4 annotation = method.getAnnotation(A4.class);
+
+        assert ("one".equals(annotation.a()));
+        assert ( 42.0 == annotation.b());
+
+        System.out.println(annotation);
+
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A4 {
+    String a();
+    double b();
+  }
+
+
+  @Test //----------------------------------------------------------------------
+  @A5(b="foo")
+  public void testPartialDefaultParamsOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testPartialDefaultParamsOk");
+        A5 annotation = method.getAnnotation(A5.class);
+
+        assert ("whatever".equals(annotation.a()));
+
+        System.out.println(annotation);
+
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A5 {
+    String a() default "whatever";
+    String b();
+  }
+
+  @Test //----------------------------------------------------------------------
+  @A6
+  public void testSingleDefaultParamOk () {
+    if (verifyNoPropertyViolation()) {
+      try {
+        java.lang.reflect.Method method =
+                AnnotationTest.class.getMethod("testSingleDefaultParamOk");
+        A6 annotation = method.getAnnotation(A6.class);
+        
+        assert ("whatever".equals(annotation.value()));
+
+        System.out.println(annotation);
+
+      } catch (SecurityException e) {
+        e.printStackTrace();
+      } catch (NoSuchMethodException e) {
+        e.printStackTrace();
+      }
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A6 {
+    String value() default "whatever";
+  }
+  
+  @A6
+  @Test
+  public void testAnnotationClass() throws ClassNotFoundException, NoSuchMethodException {
+    if (verifyNoPropertyViolation()) { 
+      Class clazz = Class.forName("gov.nasa.jpf.test.vm.basic.AnnotationTest");
+      Method method = clazz.getDeclaredMethod("testAnnotationClass");
+      Annotation annotations[] = method.getAnnotations();
+      
+      for (int i=0; i<annotations.length; i++){
+        System.out.printf("  a[%d] = %s\n", i, annotations[i].toString());
+      }
+      
+      assertEquals(2, annotations.length);
+      assertNotNull(annotations[0]);
+      assertNotNull(annotations[1]);      
+
+      assertTrue(annotations[0] instanceof A6);
+      assertTrue(annotations[1] instanceof Test);
+    }
+  }
+
+  //--------------------------------------------------------------------
+
+  public enum MyEnum {
+    ONE, TWO
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A7 {
+    MyEnum value();
+  }
+
+  @Test
+  @A7(MyEnum.ONE)
+  public void testEnumValue() throws ClassNotFoundException, NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Class clazz = Class.forName("gov.nasa.jpf.test.vm.basic.AnnotationTest");  // Any class outside of this file will do.
+      Method method = clazz.getDeclaredMethod("testEnumValue");               // Any method with an annotation will do.
+      Annotation annotations[] = method.getAnnotations();
+
+      assertEquals(2, annotations.length);
+      assertNotNull(annotations[1]);
+
+      assertTrue(annotations[1] instanceof A7);
+      A7 ann = (A7)annotations[1];
+      assertTrue( ann.value() == MyEnum.ONE);
+    }
+  }
+
+  //--------------------------------------------------------------------
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A8 {
+    Class value();
+  }
+
+  @Test
+  @A8(AnnotationTest.class)
+  public void testClassValue() throws ClassNotFoundException, NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Class clazz = Class.forName("gov.nasa.jpf.test.vm.basic.AnnotationTest");  // Any class outside of this file will do.
+      Method method = clazz.getDeclaredMethod("testClassValue");               // Any method with an annotation will do.
+      Annotation annotations[] = method.getAnnotations();
+
+      assertEquals(2, annotations.length);
+      assertNotNull(annotations[1]);
+
+      assertTrue(annotations[1] instanceof A8);
+      A8 ann = (A8)annotations[1];
+      assertTrue( ann.value() == AnnotationTest.class);
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A11 {
+      Class<?>[] value();
+  }
+
+  @Test
+  @A11({ AnnotationTest.class, Class.class })
+  public void testClassArrayValueOk() throws ClassNotFoundException, SecurityException, NoSuchMethodException {
+    if (verifyNoPropertyViolation()) {
+      Class<?> clazz = Class.forName(AnnotationTest.class.getName());
+      Method method = clazz.getDeclaredMethod("testClassArrayValueOk");
+      Annotation[] annotations = method.getAnnotations();
+      assertEquals(2, annotations.length);
+      assertNotNull(annotations[1]);
+
+      assertTrue(annotations[1] instanceof A11);
+      A11 ann = (A11) annotations[1];
+      assertTrue(ann.value()[0] == AnnotationTest.class);
+      assertTrue(ann.value()[1] == Class.class);
+    }
+  }
+
+  //-------------------------------------------------------------------
+  static class MyClass {
+    @A1("the answer")
+    int data = 42;
+  }
+  
+  public static class DataListener extends ListenerAdapter {
+
+    @Override
+    public void executeInstruction(VM vm, ThreadInfo ti, Instruction insnToExecute){
+      if (insnToExecute instanceof GETFIELD){
+        FieldInfo fi = ((GETFIELD)insnToExecute).getFieldInfo();
+        if (fi.getName().equals("data")){
+          AnnotationInfo ai = fi.getAnnotation("gov.nasa.jpf.test.vm.basic.AnnotationTest$A1");
+          System.out.println("annotation for " + fi.getFullName() + " = " + ai);
+          
+          if (ai != null){
+            String val = ai.getValueAsString("value");
+            System.out.println("   value = " + val);
+            
+            if (val == null || !val.equals("the answer")){
+              fail("wrong @A1 value = " + val);
+            }
+          } else {
+            fail("no @A1 annotation for field " + fi.getFullName());
+          }
+        }
+      }
+    }
+  }
+  
+  @Test
+  public void testFieldAnnotation(){
+    if (verifyNoPropertyViolation("+listener=.test.vm.basic.AnnotationTest$DataListener")){
+      MyClass obj = new MyClass();
+      int d = obj.data;
+    }
+  }
+  
+  //-------------------------------------------------------------------
+  public static class ArgListener extends ListenerAdapter {
+
+    @Override
+    public void executeInstruction (VM vm, ThreadInfo ti, Instruction insnToExecute){
+      if (insnToExecute instanceof JVMInvokeInstruction){
+        MethodInfo mi = ((JVMInvokeInstruction)insnToExecute).getInvokedMethod();
+        if (mi.getName().equals("foo")){
+          System.out.println("-- called method: " + mi.getUniqueName());
+          
+          AnnotationInfo[][] pai = mi.getParameterAnnotations();
+          
+          assert pai != null : "no parameter annotations found";
+          assert pai.length == 2 : "wrong number of parameter annotation arrays: " + pai.length;
+          assert pai[0] != null : "no parameter annotation for first argument found";
+          assert pai[0].length == 1 : "wrong number of annotations for first argument: "+ pai[0].length;
+          assert pai[1] != null : "no parameter annotation for second argument found";
+          assert pai[1].length == 0 : "wrong number of annotations for first argument: "+ pai[1].length;
+          
+          for (int i=0; i<pai.length; i++){
+            System.out.println("-- annotations for parameter: " + i);
+            AnnotationInfo[] ai = pai[i];
+            if (ai != null && ai.length > 0) {
+              for (int j = 0; j < ai.length; j++) {
+                assert (ai[i] != null) : "null annotation for paramter: " + j;
+                System.out.println(ai[i].asString());
+              }
+            } else {
+              System.out.println("none");
+            }
+          }
+        }
+      }
+    }
+  }
+  
+  public void foo (@A1("arghh") MyClass x, String s){
+    // nothing
+  }
+  
+  @Test
+  public void testParameterAnnotation(){
+    if (verifyNoPropertyViolation("+listener=.test.vm.basic.AnnotationTest$ArgListener")){
+      MyClass obj = new MyClass();
+      foo( obj, "blah");
+    }        
+  }
+  
+  //---------------------------------------------------------------
+  
+  @Retention(RetentionPolicy.RUNTIME)
+  @Inherited
+  public @interface A9 {
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A10 {
+  }
+  
+  @A9()
+  public static class Parent {
+  }
+
+  @A10
+  public static class Child1 extends Parent {
+  }
+
+  public static class Child2 extends Child1 {
+  }
+  @Test
+  public void getAnnotationsTest () {
+    if (verifyNoPropertyViolation()) {      
+      assertTrue(Parent.class.getAnnotations().length == 1);
+      assertTrue(Child1.class.getAnnotations().length == 2);
+      assertTrue(Child2.class.getAnnotations().length == 1);
+    }
+  }
+  
+
+  //---------------------------------------------------------------
+  // test for RuntimeVisibleAnnotations attributes that in turn have
+  // element_value entries
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A12 { // this one has the string value
+    String value();
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @A12("Whatever")
+  @interface A13 {
+    // this one has a RuntimeVisibleAnnotation attribute for A11 with a
+    // String entry value
+  }
+
+  @A13 // causes loading of @C
+  @Test
+  public void testRecursiveRuntimeVisibleAnnotationValue(){
+    if (verifyNoPropertyViolation()){
+      // nothing to do other than just causing the loading of A12
+    }
+  }
+  
+  
+  //---------------------------------------------------------------
+  // test of char annotations
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A14 {
+    char value();
+  }
+  
+  @Test
+  @A14('x')
+  public void testCharAnnotation(){
+    if (verifyNoPropertyViolation()){
+      try {
+        Class<?> clazz = Class.forName(AnnotationTest.class.getName());
+        Method method = clazz.getDeclaredMethod("testCharAnnotation");
+        Annotation[] annotations = method.getAnnotations();
+        assertEquals(2, annotations.length);
+        assertNotNull(annotations[1]);
+
+        assertTrue(annotations[1] instanceof A14);
+        A14 ann = (A14) annotations[1];
+        assertTrue(ann.value() == 'x');
+        
+      } catch (Throwable t){
+        t.printStackTrace();
+        fail("unexpected exception: " + t);
+      }
+    }
+  }
+  
+  //---------------------------------------------------------------
+  // test of char annotations
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A15 {
+    float value();
+  }
+  
+  @Test
+  @A15(12.34f)
+  public void testFloatAnnotation(){
+    if (verifyNoPropertyViolation()){
+      try {
+        Class<?> clazz = Class.forName(AnnotationTest.class.getName());
+        Method method = clazz.getDeclaredMethod("testFloatAnnotation");
+        Annotation[] annotations = method.getAnnotations();
+        assertEquals(2, annotations.length);
+        assertNotNull(annotations[1]);
+
+        assertTrue(annotations[1] instanceof A15);
+        A15 ann = (A15) annotations[1];
+        assertTrue(Math.abs(ann.value() - 12.34f) < 0.00001);
+        
+      } catch (Throwable t){
+        t.printStackTrace();
+        fail("unexpected exception: " + t);
+      }
+    }
+  }
+
+  // test of char annotations
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A16 {
+    double value();
+  }
+  
+  @Test
+  @A16(Double.MAX_VALUE)
+  public void testDoubleAnnotation(){
+    if (verifyNoPropertyViolation()){
+      try {
+        Class<?> clazz = Class.forName(AnnotationTest.class.getName());
+        Method method = clazz.getDeclaredMethod("testDoubleAnnotation");
+        Annotation[] annotations = method.getAnnotations();
+        assertEquals(2, annotations.length);
+        assertNotNull(annotations[1]);
+
+        assertTrue(annotations[1] instanceof A16);
+        A16 ann = (A16) annotations[1];
+        assertTrue(ann.value() == Double.MAX_VALUE);
+        
+      } catch (Throwable t){
+        t.printStackTrace();
+        fail("unexpected exception: " + t);
+      }
+    }
+  }
+
+  // test of char annotations
+  @Retention(RetentionPolicy.RUNTIME)
+  public @interface A17 {
+    long value();
+  }
+  
+  @Test
+  @A17(Long.MAX_VALUE)
+  public void testLongAnnotation(){
+    if (verifyNoPropertyViolation()){
+      try {
+        Class<?> clazz = Class.forName(AnnotationTest.class.getName());
+        Method method = clazz.getDeclaredMethod("testLongAnnotation");
+        Annotation[] annotations = method.getAnnotations();
+        assertEquals(2, annotations.length);
+        assertNotNull(annotations[1]);
+
+        assertTrue(annotations[1] instanceof A17);
+        A17 ann = (A17) annotations[1];
+        assertTrue(ann.value() == Long.MAX_VALUE);
+        
+      } catch (Throwable t){
+        t.printStackTrace();
+        fail("unexpected exception: " + t);
+      }
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/ArrayTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/ArrayTest.java
new file mode 100644 (file)
index 0000000..cd6e8b4
--- /dev/null
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * JPF part of unit test for standard VM array operations.
+ */
+public class ArrayTest extends TestJPF {
+
+  @Test
+  public void test2DArray() {
+    if (verifyNoPropertyViolation()) {
+      long[][] a = new long[2][3];
+
+      a[0][1] = 42;
+
+      assert (a.getClass().isArray());
+      assert (a.getClass().getName().equals("[[J"));
+      assert (a.getClass().getComponentType().getName().equals("[J"));
+      assert (a[0][1] == 42);
+    }
+  }
+
+  @Test
+  public void test2DStringArray() {
+    if (verifyNoPropertyViolation()) {
+      String[][] a = new String[3][5];
+
+      a[2][2] = "fortytwo";
+
+      assert (a.getClass().isArray());
+      assert (a.getClass().getName().equals("[[Ljava.lang.String;"));
+      assert (a.getClass().getComponentType().getName().equals("[Ljava.lang.String;"));
+      assert (a[2][2].equals("fortytwo"));
+    }
+  }
+
+  @Test
+  public void testAoBX() {
+    if (verifyNoPropertyViolation()) {
+      int[] a = new int[2];
+
+      assert (a.length == 2);
+
+      try {
+        a[2] = 42;
+      } catch (ArrayIndexOutOfBoundsException aobx) {
+        return;
+      }
+
+      throw new RuntimeException("array bounds check failed (no ArrayIndexOutOfBoundsException)");
+    }
+  }
+
+  @Test
+  public void testArrayStoreException() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Object x[] = new String[1];
+        x[0] = new Integer(42);
+      } catch (ArrayStoreException osx) {
+        return;
+      }
+
+      throw new RuntimeException("array element type check failed (no ArrayStoreException)");
+    }
+  }
+
+  @Test
+  public void testArrayStoreExceptionArraycopy() {
+    if (verifyNoPropertyViolation()) {
+
+      boolean caught = false;
+      try {
+        System.arraycopy(new Object[]{new Integer(42)}, 0, new String[2], 0, 1);
+      } catch (ArrayStoreException x) {
+        caught = true;
+      }
+      if (!caught) {
+        throw new RuntimeException("array element type check failed (no ArrayStoreException: Integer[] -> String[])");
+      }
+
+      caught = false;
+      try {
+        System.arraycopy(new Object[]{new Integer(42)}, 0, new int[2], 0, 1);
+      } catch (ArrayStoreException x) {
+        caught = true;
+      }
+      if (!caught) {
+        throw new RuntimeException("array element type check failed (no ArrayStoreException: Integer[] -> int[])");
+      }
+
+      caught = false;
+      try {
+        System.arraycopy(new int[]{42, 42}, 0, new long[2], 0, 1);
+      } catch (ArrayStoreException x) {
+        caught = true;
+      }
+      if (!caught) {
+        throw new RuntimeException("array element type check failed (no ArrayStoreException: int[] -> long[])");
+      }
+
+      caught = false;
+      try {
+        System.arraycopy(new int[]{42, 42}, 0, new float[2], 0, 1);
+      } catch (ArrayStoreException x) {
+        caught = true;
+      }
+      if (!caught) {
+        throw new RuntimeException("array element type check failed (no ArrayStoreException: int[] -> float[])");
+      }
+
+      caught = false;
+      try {
+        System.arraycopy(new double[]{42, 42}, 0, new long[2], 0, 1);
+      } catch (ArrayStoreException x) {
+        caught = true;
+      }
+      if (!caught) {
+        throw new RuntimeException("array element type check failed (no ArrayStoreException: double[] -> long[])");
+      }
+    }
+  }
+
+  @Test
+  public void testCharArray() {
+    if (verifyNoPropertyViolation()) {
+
+      char[] a = new char[5];
+
+      a[2] = 'Z';
+
+      assert (a.getClass().isArray());
+      assert (a.getClass().getName().equals("[C"));
+      assert (a.getClass().getComponentType() == char.class);
+      assert (a[2] == 'Z');
+    }
+  }
+
+  @Test
+  public void testIntArray() {
+    if (verifyNoPropertyViolation()) {
+
+      int[] a = new int[10];
+
+      a[1] = 42;
+
+      assert (a.getClass().isArray());
+      assert (a.getClass().getName().equals("[I"));
+      assert (a.getClass().getComponentType() == int.class);
+      assert (a[1] == 42);
+    }
+  }
+
+  @Test
+  public void testStringArray() {
+    if (verifyNoPropertyViolation()) {
+
+      String[] a = {"one", "two", "three"};
+
+      assert (a.getClass().isArray());
+      assert (a.getClass().getName().equals("[Ljava.lang.String;"));
+      assert (a.getClass().getComponentType() == String.class);
+      assert (a[1].equals("two"));
+    }
+  }
+
+  @Test
+  public void testArrayCopy() {
+    if (verifyNoPropertyViolation()) {
+
+      String s1 = "1";
+      String s2 = "2";
+      String s3 = "3";
+      String[] sa = {s1, s2, s3};
+      String[] sb = {s3, s2, s1};
+
+      System.arraycopy(sa, 0, sb, 1, 2);
+      assert (sb[0] == s3 && sb[1] == s1 && sb[2] == s2);
+
+      System.arraycopy(sa, 0, sa, 1, 2);
+      assert (sa[0] == s1 && sa[1] == s1 && sa[2] == s2);
+
+      System.arraycopy(sb, 1, sb, 0, 2);
+      assert (sb[0] == s1 && sb[1] == s2 && sb[2] == s2);
+
+      System.arraycopy(sa, 3, sb, 0, 0);
+      assert (sb[0] == s1 && sb[1] == s2 && sb[2] == s2);
+
+
+      long[] la = {1L, 2L, 3L};
+      long[] lb = {3L, 2L, 1L};
+
+      System.arraycopy(la, 0, lb, 1, 2);
+      assert (lb[0] == 3 && lb[1] == 1 && lb[2] == 2);
+
+      System.arraycopy(la, 0, la, 1, 2);
+      assert (la[0] == 1 && la[1] == 1 && la[2] == 2);
+
+      System.arraycopy(lb, 1, lb, 0, 2);
+      assert (lb[0] == 1 && lb[1] == 2 && lb[2] == 2);
+
+      System.arraycopy(la, 3, lb, 0, 0);
+      assert (lb[0] == 1 && lb[1] == 2 && lb[2] == 2);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/AssertTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/AssertTest.java
new file mode 100644 (file)
index 0000000..b4be749
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+
+import gov.nasa.jpf.test.java.net.LoadUtility;
+import org.junit.Test;
+
+/**
+ * JPF part of assertion test
+ */
+public class AssertTest extends LoadUtility {
+
+  @Test public void testAssertionViolation () {
+    if (verifyAssertionErrorDetails("oops, assertion failed")){
+      int i = 1;
+      assert i == 0 : "oops, assertion failed";
+    }
+  }
+
+  @Test public void testNoAssertionViolation () {
+    if (verifyNoPropertyViolation("+vm.disable_assertions=*AssertTest")){
+      int i = 1;
+      assert i == 0 : "oops, assertion failed";
+    }
+  }
+
+  public static void invokeAssertFalse(ClassLoader loader, String cname) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException  {
+    Class<?> c = loader.loadClass(cname);
+    Method m = c.getMethod("assertFalse", new Class<?>[0]);
+    m.invoke(null, new Object[0]);
+  }
+
+  @Test
+  public void testSetClassAssertionStatus1() {
+    if (verifyAssertionErrorDetails("oops, assertion failed")) {
+      ClassLoader cl = ClassLoader.getSystemClassLoader();
+
+      // this should change the "desiredAssertionStatus()" return value to false, 
+      // but it shouldn't change the actual assertion status since the class has 
+      // been already initialized
+      cl.setClassAssertionStatus("gov.nasa.jpf.test.vm.basic.AssertTest", false);
+      assertFalse(AssertTest.class.desiredAssertionStatus());
+
+      // throw AssertionError
+      assert false : "oops, assertion failed";
+    }
+  }
+
+  @Test
+  public void testSetClassAssertionStatus2() throws MalformedURLException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalAccessException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader loader = new URLClassLoader(urls);
+
+      String c1 = pkg + ".Class1";
+      String c2 = pkg + ".Class2";
+      loader.setClassAssertionStatus(c1, false);
+
+      try {
+        invokeAssertFalse(loader, c1);
+      } catch(Exception ae) {
+        fail("setClassAssertionStatus should have avoided this!");
+      }
+
+      loader.setClassAssertionStatus(c2, false);
+      assertFalse(loader.loadClass(c2).desiredAssertionStatus());
+      try {
+        invokeAssertFalse(loader, c2);
+        fail();
+      } catch (InvocationTargetException e) {
+        // success
+      }
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testSetPackageAssertionStatus() throws MalformedURLException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalAccessException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader loader = new URLClassLoader(urls);
+      loader.setPackageAssertionStatus(pkg, false);
+
+      String c1 = pkg + ".Class1";
+      try {
+        invokeAssertFalse(loader, c1);
+      } catch(Exception ae) {
+        fail("setPackageAssertionStatus should have avoided this!");
+      }
+
+      loader.setPackageAssertionStatus(pkg, true);
+      assertTrue(loader.loadClass(c1).desiredAssertionStatus());
+      try {
+        invokeAssertFalse(loader, c1);
+      } catch(Exception ae) {
+        fail("setPackageAssertionStatus shouldn't change actual assertion status");
+      }
+    }
+    movePkgBack();
+  }
+
+  @Test
+  public void testSetDefaultAssertionStatus() throws MalformedURLException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalAccessException {
+    movePkgOut();
+    if (verifyNoPropertyViolation()) {
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader loader = new URLClassLoader(urls);
+      loader.setDefaultAssertionStatus(false);
+
+      String c1 = pkg + ".Class1";
+      try {
+        invokeAssertFalse(loader, c1);
+      } catch(Exception ae) {
+        fail("setDefaultAssertionStatus should have avoided this!");
+      }
+
+      // shouldn't have any effect on the actual assertion status of the class since 
+      // it has been already initialized
+      loader.setDefaultAssertionStatus(true);
+      assertTrue(loader.loadClass(c1).desiredAssertionStatus());
+      try {
+        invokeAssertFalse(loader, c1);
+      } catch(Exception ae) {
+        fail("setDefaultAssertionStatus shouldn't change actual assertion status");
+      }
+    }
+    movePkgBack();
+  }
+  
+  @Test
+  public void testClearAssertionStatus() throws MalformedURLException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    movePkgOut();
+    if (verifyNoPropertyViolation("+vm.disable_assertions=*")) {
+
+      URL[] urls = { new URL(dirUrl) };
+      URLClassLoader loader = new URLClassLoader(urls);
+      loader.setDefaultAssertionStatus(true);
+
+      String c1 = pkg + ".Class1";
+      try {
+        invokeAssertFalse(loader, c1);
+        fail();
+      } catch(InvocationTargetException ae) {
+        // success!
+      }
+      
+      // shouldn't have any effect on the actual assertion status of the class since 
+      // it has been already initialized
+      loader.clearAssertionStatus();
+      assertFalse(loader.loadClass(c1).desiredAssertionStatus());
+      try {
+        invokeAssertFalse(loader, c1);
+        fail();
+      } catch(Exception ae) {
+        // success!
+      }
+
+      String c3 = pkg + ".Class3";
+      try {
+        invokeAssertFalse(loader, c3);
+      } catch(Exception ae) {
+        fail("clearAssertionStatus() should have set the assertion status for the " +
+                       "loader to false");
+      }
+    }
+    movePkgBack();
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/CastTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/CastTest.java
new file mode 100644 (file)
index 0000000..ea916c3
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * test cast operations
+ */
+public class CastTest extends TestJPF {
+  
+  @SuppressWarnings("cast")
+  @Test public void testCast () {
+    if (verifyNoPropertyViolation()){
+      B b = new B();
+      A a = b;
+
+      B bb = (B) a;
+      K k = a;
+      I i = (I) a;
+
+      C c = new C();
+      k = c;
+    }
+  }
+
+  @Test public void testCastFail () {
+    if (verifyUnhandledException("java.lang.ClassCastException")){
+      A a = new A();
+      I i = (I) a;
+    }
+  }
+
+  @Test public void testArrayCast () {
+    if (verifyNoPropertyViolation()){
+      String[] sa = new String[1];
+      Object o = sa;
+      Object[] ol = (Object[])o; // that should succeed
+    }
+  }
+  
+  @Test public void testArrayCastFail() {
+    if (verifyUnhandledException("java.lang.ClassCastException")){
+      String[] sa = new String[1];
+      Object o = sa ;
+      Number[] na = (Number[])o; // that should fail
+    }
+  }
+  
+  @Test public void testPrimitiveArrayCast() {
+    if (verifyNoPropertyViolation()){
+      int[] a = new int[10];
+      Object o = a;
+      int[] b = (int[]) o;
+    }
+  }
+  
+  //--- helper types and methods
+  
+  static interface I {
+  }
+
+  static interface J extends I {
+  }
+
+  static interface K {
+  }
+  
+  static class A implements K {
+  }
+
+  static class B extends A implements J {
+  }
+  
+  static class C extends B {
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/ClassInitTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/ClassInitTest.java
new file mode 100644 (file)
index 0000000..9708906
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * basic regression test for class initialization
+ */
+public class ClassInitTest extends TestJPF {
+
+    static class Root {
+        static int data;
+        static {
+            System.out.println("in Root.<clinit>()");
+            data = 42;
+        }
+    }
+
+    static class Base extends Root {
+        static int data;
+        static {
+            System.out.println("in Base.<clinit>()");
+            data = Root.data + 1;
+        }
+    }
+
+    static class Derived extends Base {
+        static int data;
+        static {
+            System.out.println("in Derived.<clinit>()");
+            data = Base.data + 1;
+        }
+    }
+
+    @Test
+    public void testClinits (){
+        if (verifyNoPropertyViolation()){
+            int n = Derived.data;
+            assertTrue(n == 44);
+        }
+    }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/EndStateListener.java b/src/tests/gov/nasa/jpf/test/vm/basic/EndStateListener.java
new file mode 100644 (file)
index 0000000..60b16b6
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.search.Search;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.ThreadList;
+
+/**
+ * listener that checks SUT and JPF consistency in program end states
+ */
+public class EndStateListener extends ListenerAdapter {
+
+  @Override
+  public void stateAdvanced (Search search){
+    if (search.isEndState()){
+
+      VM vm = search.getVM();
+      ThreadList tl = vm.getThreadList();
+
+      for (ThreadInfo ti : tl){
+        System.out.println("EndStateListener checking thread: " + ti.getStateDescription());
+
+        // check if there are no alive threads anymore
+        assert ti.isTerminated();
+
+        // check if none of the threads still holds a lock
+        assert !ti.hasLockedObjects();
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/EndStateTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/EndStateTest.java
new file mode 100644 (file)
index 0000000..9829dbf
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * test SUT end states
+ */
+public class EndStateTest extends TestJPF {
+
+  @Test 
+  public void testSingleThread () {
+    if (verifyNoPropertyViolation("+listener=.test.vm.basic.EndStateListener")){
+      System.out.println("** this is testSingleThread - it should succeed");      
+    }
+  }
+
+  @Test 
+  public void testMultipleThreads () {
+    if (verifyNoPropertyViolation("+listener=.test.vm.basic.EndStateListener")){
+      System.out.println("** this is testMultipleThreads - it should succeed");
+
+      Thread t = new Thread() {
+        @Override
+               public synchronized void run() {
+          System.out.println("** this is " + Thread.currentThread().getName() + " terminating");
+        }
+      };
+      t.start();
+
+      synchronized(this){
+        System.out.println("** this is thread main terminating");
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/EnumTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/EnumTest.java
new file mode 100644 (file)
index 0000000..e73e4f0
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import java.util.EnumSet;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+public class EnumTest extends TestJPF {
+
+  //--- helper type
+  enum A {
+    ONE,
+    TWO;
+  }
+
+  
+  @Test
+  public void testValueOf () {
+    if (verifyNoPropertyViolation()) {
+      assert A.valueOf("ONE") == A.ONE;
+    }
+  }
+
+  @Test
+  public void testEnumerate () {
+    if (verifyNoPropertyViolation()){
+      boolean[] seen = new boolean[2];
+
+      for (A a : A.values()) {
+        switch (a) {
+          case ONE:
+            System.out.println("this is ONE");
+            break;
+          case TWO:
+            System.out.println("this is TWO");
+            break;
+          default:
+            throw new RuntimeException("unknown enumeration constant");
+        }
+        seen[a.ordinal()] = true;
+      }
+
+      for (boolean b : seen){
+        assert b : "unseen enum constant";
+      }
+    }
+  }
+
+  enum Option {
+    A
+  }
+
+  @Test
+  public void testEnumSet() {
+    
+    if (verifyNoPropertyViolation()){
+      //Option o = Option.A; // <2do> init missing
+      
+      EnumSet<Option> options = EnumSet.allOf(Option.class);
+
+      for (Option option : options) {
+        System.out.println(option);
+        assert option != null;
+      }
+    }
+  }
+  
+}
+
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/ExceptionHandlingTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/ExceptionHandlingTest.java
new file mode 100644 (file)
index 0000000..d2d32f0
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+/**
+ * JPF unit test for exception handling
+ */
+@SuppressWarnings("null")
+public class ExceptionHandlingTest extends TestJPF {
+  int data;
+
+  void foo () {
+  }
+  
+  static void bar () {
+    ExceptionHandlingTest o = null;
+    o.foo();
+  }
+  
+  @Test public void testNPE () {
+    if (verifyUnhandledException("java.lang.NullPointerException")){
+      ExceptionHandlingTest o = null;
+      o.data = -1;
+
+      assert false : "should never get here";
+    }
+  }
+  
+  @Test public void testNPECall () {
+    if (verifyUnhandledException("java.lang.NullPointerException")){
+      ExceptionHandlingTest o = null;
+      o.foo();
+
+      assert false : "should never get here";
+    }
+  }
+
+  @Test public void testArrayIndexOutOfBoundsLow () {
+    if (verifyUnhandledException("java.lang.ArrayIndexOutOfBoundsException")){
+      int[] a = new int[10];
+      a[-1] = 0;
+
+      assert false : "should never get here";
+    }
+  }
+
+  @Test public void testArrayIndexOutOfBoundsHigh () {
+    if (verifyUnhandledException("java.lang.ArrayIndexOutOfBoundsException")){
+      int[] a = new int[10];
+      a[10] = 0;
+
+      assert false : "should never get here";
+    }
+  }
+
+  @Test public void testLocalHandler () {
+    if (verifyNoPropertyViolation()){
+      try {
+        ExceptionHandlingTest o = null;
+        o.data = 0;
+      } catch (IllegalArgumentException iax) {
+        assert false : "should never get here";
+      } catch (NullPointerException npe) {
+        return;
+      } catch (Exception x) {
+        assert false : "should never get here";
+      }
+
+      assert false : "should never get here";
+    }
+  }
+
+  @Test public void testCallerHandler () {
+    if (verifyNoPropertyViolation()){
+      try {
+        bar();
+      } catch (Throwable t) {
+        return;
+      }
+
+      assert false : "should never get here";
+    }
+  }
+  
+  @Test public void testEmptyHandler () {
+    if (verifyNoPropertyViolation()){
+      try {
+        throw new RuntimeException("should be empty-handled");
+      } catch (Throwable t) {
+        // nothing
+      }
+    }
+  }
+  
+  @Test public void testEmptyTryBlock () {
+    if (verifyNoPropertyViolation()){
+      try {
+        // nothing
+      } catch (Throwable t) {
+        assert false : "should never get here";
+      }
+    }
+  }
+  
+  @Test public void testStackTrace() {
+    if (verifyNoPropertyViolation()){
+
+      Throwable x = new Throwable();
+      StackTraceElement[] st = x.getStackTrace();
+
+      //x.printStackTrace();
+      for (int i=0; i<st.length; i++){
+        System.out.print("\t at ");
+        System.out.print(st[i].getClassName());
+        System.out.print('.');
+        System.out.print(st[i].getMethodName());
+        System.out.print('(');
+        System.out.print(st[i].getFileName());
+        System.out.print(':');
+        System.out.print(st[i].getLineNumber());
+        System.out.println(')');
+      }
+
+      // note - direct call stackframes should not show up here, they are JPF internal
+      assert st.length == 3 : "wrong stack trace depth";
+
+      assert st[0].getClassName().equals(ExceptionHandlingTest.class.getName());
+      assert st[0].getMethodName().equals("testStackTrace");
+
+      assert st[1].getClassName().equals(Method.class.getName());
+      assert st[1].getMethodName().equals("invoke");
+
+      assert st[2].getClassName().equals(TestJPF.class.getName());
+      assert st[2].getMethodName().equals("runTestMethod");
+    }
+  }  
+}
+
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/FieldTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/FieldTest.java
new file mode 100644 (file)
index 0000000..2fd8a31
--- /dev/null
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * basic test of field access operations
+ */
+class TestFieldBase extends TestJPF {
+  static boolean s_base_Z = true;
+  static byte    s_base_B = 16;
+  static char    s_base_C = 'A';
+  static short   s_base_S = 2048;
+  static int     s_base_I = 0x8000;
+  static long    s_base_J = 0x800000;
+  static float   s_base_F = 1.23f;
+  static double  s_base_D = 3.45;
+  static Object  s_base_o = new String("a static string");
+  boolean        base_Z = true;
+  byte           base_B = 16;
+  char           base_C = 'A';
+  short          base_S = 2048;
+  int            base_I = 0x8000;
+  long           base_J = 0x800000;
+  float          base_F = 1.23f;
+  double         base_D = 3.45;
+  Object         base_o = new String("a instance string");
+}
+
+
+public class FieldTest extends TestFieldBase {
+  boolean s_Z = s_base_Z;
+  byte    s_B = s_base_B;
+  char    s_C = s_base_C;
+  short   s_S = s_base_S;
+  int     s_I = s_base_I;
+  long    s_J = s_base_J;
+  float   s_F = s_base_F;
+  double  s_D = s_base_D;
+  Object  s_o = s_base_o;
+  boolean _Z = base_Z;
+  byte    _B = base_B;
+  char    _C = base_C;
+  short   _S = base_S;
+  int     _I = base_I;
+  long    _J = base_J;
+  float   _F = base_F;
+  double  _D = base_D;
+  Object  _o = base_o;
+
+  @Test public void testReadInstance () {
+    if (verifyNoPropertyViolation()) {
+      assert _Z == base_Z;
+      assert _Z == true;
+
+      assert _B == base_B;
+      assert _B == 16;
+
+      assert _C == base_C;
+      assert _C == 'A';
+
+      assert _I == base_I;
+      assert _I == 0x8000;
+
+      assert _J == base_J;
+      assert _J == 0x800000;
+
+      assert _F == base_F;
+      assert _F == 1.23f;
+
+      assert _D == base_D;
+      assert _D == 3.45;
+
+      assert _o.equals(base_o);
+      assert _o.equals("a instance string");
+    }
+  }
+
+  @Test public void testReadStatic () {
+    if (verifyNoPropertyViolation()) {
+      assert s_Z == s_base_Z;
+      assert s_Z == true;
+
+      assert s_B == s_base_B;
+      assert s_B == 16;
+
+      assert s_C == s_base_C;
+      assert s_C == 'A';
+
+      assert s_I == s_base_I;
+      assert s_I == 0x8000;
+
+      assert s_J == s_base_J;
+      assert s_J == 0x800000;
+
+      assert s_F == s_base_F;
+      assert s_F == 1.23f;
+
+      assert s_D == s_base_D;
+      assert s_D == 3.45;
+
+      assert s_o.equals(s_base_o);
+      assert s_o.equals("a static string");
+    }
+  }
+
+  @Test public void testWriteInstance () {
+    if (verifyNoPropertyViolation()) {
+      _Z = false;
+      assert _Z == false;
+      base_Z = _Z;
+      assert base_Z == _Z;
+
+      _B = 17;
+      assert _B == 17;
+      base_B = _B;
+      assert base_B == _B;
+
+      _C = 'B';
+      assert _C == 'B';
+      base_C = _C;
+      assert base_C == _C;
+
+      _I = 12345;
+      assert _I == 12345;
+      base_I = _I;
+      assert base_I == _I;
+
+      _J = 12345678;
+      assert _J == 12345678;
+      base_J = _J;
+      assert base_J == _J;
+
+      _F = 7.65f;
+      assert _F == 7.65f;
+      base_F = _F;
+      assert base_F == _F;
+
+      _D = 6.54;
+      assert _D == 6.54;
+      base_D = _D;
+      assert base_D == _D;
+
+      _o = new Integer(42);
+      assert _o.equals(new Integer(42));
+      base_o = _o;
+      assert base_o.equals(_o);
+    }
+  }
+
+  @Test public void testWriteStatic () {
+    if (verifyNoPropertyViolation()) {
+      s_Z = false;
+      assert s_Z == false;
+      s_base_Z = s_Z;
+      assert s_base_Z == s_Z;
+
+      s_B = 17;
+      assert s_B == 17;
+      s_base_B = s_B;
+      assert s_base_B == s_B;
+
+      s_C = 'B';
+      assert s_C == 'B';
+      s_base_C = s_C;
+      assert s_base_C == s_C;
+
+      s_I = 12345;
+      assert s_I == 12345;
+      s_base_I = s_I;
+      assert s_base_I == s_I;
+
+      s_J = 12345678;
+      assert s_J == 12345678;
+      s_base_J = s_J;
+      assert s_base_J == s_J;
+
+      s_F = 7.65f;
+      assert s_F == 7.65f;
+      s_base_F = s_F;
+      assert s_base_F == s_F;
+
+      s_D = 6.54;
+      assert s_D == 6.54;
+      s_base_D = s_D;
+      assert s_base_D == s_D;
+
+      s_o = new Integer(42);
+      assert s_o.equals(new Integer(42));
+      s_base_o = s_o;
+      assert s_base_o.equals(s_o);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/InitializeInterfaceClassObjectRefTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/InitializeInterfaceClassObjectRefTest.java
new file mode 100644 (file)
index 0000000..4dffa63
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+
+import org.junit.Test;
+
+/*
+ * VM.registerStartupClass must be kept in sync with ClassInfo.registerClass.
+ * This test ensures that the interfaces of the main class are registered 
+ * properly.  The old VM.registerStartupClass code wasn't initializing the
+ * class object of the interfaces.
+ */
+public class InitializeInterfaceClassObjectRefTest extends TestJPF implements InitializeInterfaceClassObjectRefTestInterface
+{
+   @Test
+   public void test()
+   {
+      if (verifyUnhandledExceptionDetails(RuntimeException.class.getName(), "This test throws an expected exception.", "+log.finest+=,gov.nasa.jpf.vm.ClassInfo"))
+      {
+         // Throw an exception to avoid backtracking.  Backtracking will wipe out the class object ref.
+         throw new RuntimeException("This test throws an expected exception.");
+      }
+      else
+      {
+         ClassInfo ci = ClassLoaderInfo.getCurrentResolvedClassInfo( InitializeInterfaceClassObjectRefTestInterface.class.getName());
+         
+         if (ci.getClassObjectRef() < 0)
+            throw new AssertionError("ci.getClassObjectRef() < 0 : " + ci.getClassObjectRef());
+      }
+   }
+}
+
+interface InitializeInterfaceClassObjectRefTestInterface
+{
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/JPFAttrAnnotationTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/JPFAttrAnnotationTest.java
new file mode 100644 (file)
index 0000000..5999230
--- /dev/null
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.annotation.JPFAttribute;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.VM;
+import org.junit.Test;
+
+/**
+ * regression test for JPFAttribute annotations
+ */
+public class JPFAttrAnnotationTest extends TestJPF {
+
+  static final String LISTENER = "gov.nasa.jpf.test.vm.basic.JPFAttrAnnotationTest$LoadListener";
+  static final String ATTR_CLS = "gov.nasa.jpf.test.vm.basic.JPFAttrAnnotationTest$MyAttr"; // needs to const
+  static final String TGT_CLS = "gov.nasa.jpf.test.vm.basic.JPFAttrAnnotationTest$SomeClass";
+  
+  public static class MyAttr {}
+  
+  @JPFAttribute(ATTR_CLS)
+  public static class SomeClass {
+
+    Object data1 = 42;
+    
+    @JPFAttribute(ATTR_CLS)
+    Object data2 = "whatever";
+    
+    public void foo(){}
+    
+    @JPFAttribute(ATTR_CLS)
+    public void bar(){
+      System.out.println("SomeClass.bar() executed");
+    }
+  }
+  
+  public static class LoadListener extends ListenerAdapter {
+    
+    @Override
+    public void classLoaded (VM vm, ClassInfo ci){
+      if (ci.getName().equals(TGT_CLS)){
+        System.out.println("#--- checking attribute annotations of " + ci.getName());
+        
+        assertTrue( ci.hasAttr(MyAttr.class));
+        System.out.println("# class attr Ok");
+        
+        MethodInfo mi = ci.getMethod("bar()V", false);
+        assertTrue( mi.hasAttr(MyAttr.class));
+        System.out.println("# method bar() attr Ok");
+       
+        mi = ci.getMethod("foo()V", false);
+        assertFalse( mi.hasAttr(MyAttr.class));
+        
+        FieldInfo fi = ci.getDeclaredInstanceField("data2");
+        assertTrue( fi.hasAttr(MyAttr.class));
+        System.out.println("# field data2 attr Ok");
+
+        fi = ci.getDeclaredInstanceField("data1");
+        assertFalse( fi.hasAttr(MyAttr.class));
+      }
+    }
+  }
+  
+  @Test
+  public void testAttrs(){
+    if (verifyNoPropertyViolation("+listener=" + LISTENER)){
+      SomeClass o = new SomeClass();
+      o.bar();
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/LargeCodeTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/LargeCodeTest.java
new file mode 100644 (file)
index 0000000..0f2ffe5
--- /dev/null
@@ -0,0 +1,5053 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * test large methods (e.g. synthesized by code generators)
+ */
+public class LargeCodeTest extends TestJPF {
+
+
+  @Test public void testGotoW () {
+    if (verifyNoPropertyViolation()){
+      for (int i = 1; i < 5000; i++) {
+        int ret = switchIt(i);
+        assert (ret == 10000 + i) : "wrong switchtable lookup";
+      }
+
+      assert (switchIt(0) == 42) : "goto_w failed";
+    }
+  }
+
+
+
+  int switchIt (int i) {
+    int ret = 0;
+    switch (i) {
+    case 1: ret = 10001; break;
+    case 2: ret = 10002; break;
+    case 3: ret = 10003; break;
+    case 4: ret = 10004; break;
+    case 5: ret = 10005; break;
+    case 6: ret = 10006; break;
+    case 7: ret = 10007; break;
+    case 8: ret = 10008; break;
+    case 9: ret = 10009; break;
+    case 10: ret = 10010; break;
+    case 11: ret = 10011; break;
+    case 12: ret = 10012; break;
+    case 13: ret = 10013; break;
+    case 14: ret = 10014; break;
+    case 15: ret = 10015; break;
+    case 16: ret = 10016; break;
+    case 17: ret = 10017; break;
+    case 18: ret = 10018; break;
+    case 19: ret = 10019; break;
+    case 20: ret = 10020; break;
+    case 21: ret = 10021; break;
+    case 22: ret = 10022; break;
+    case 23: ret = 10023; break;
+    case 24: ret = 10024; break;
+    case 25: ret = 10025; break;
+    case 26: ret = 10026; break;
+    case 27: ret = 10027; break;
+    case 28: ret = 10028; break;
+    case 29: ret = 10029; break;
+    case 30: ret = 10030; break;
+    case 31: ret = 10031; break;
+    case 32: ret = 10032; break;
+    case 33: ret = 10033; break;
+    case 34: ret = 10034; break;
+    case 35: ret = 10035; break;
+    case 36: ret = 10036; break;
+    case 37: ret = 10037; break;
+    case 38: ret = 10038; break;
+    case 39: ret = 10039; break;
+    case 40: ret = 10040; break;
+    case 41: ret = 10041; break;
+    case 42: ret = 10042; break;
+    case 43: ret = 10043; break;
+    case 44: ret = 10044; break;
+    case 45: ret = 10045; break;
+    case 46: ret = 10046; break;
+    case 47: ret = 10047; break;
+    case 48: ret = 10048; break;
+    case 49: ret = 10049; break;
+    case 50: ret = 10050; break;
+    case 51: ret = 10051; break;
+    case 52: ret = 10052; break;
+    case 53: ret = 10053; break;
+    case 54: ret = 10054; break;
+    case 55: ret = 10055; break;
+    case 56: ret = 10056; break;
+    case 57: ret = 10057; break;
+    case 58: ret = 10058; break;
+    case 59: ret = 10059; break;
+    case 60: ret = 10060; break;
+    case 61: ret = 10061; break;
+    case 62: ret = 10062; break;
+    case 63: ret = 10063; break;
+    case 64: ret = 10064; break;
+    case 65: ret = 10065; break;
+    case 66: ret = 10066; break;
+    case 67: ret = 10067; break;
+    case 68: ret = 10068; break;
+    case 69: ret = 10069; break;
+    case 70: ret = 10070; break;
+    case 71: ret = 10071; break;
+    case 72: ret = 10072; break;
+    case 73: ret = 10073; break;
+    case 74: ret = 10074; break;
+    case 75: ret = 10075; break;
+    case 76: ret = 10076; break;
+    case 77: ret = 10077; break;
+    case 78: ret = 10078; break;
+    case 79: ret = 10079; break;
+    case 80: ret = 10080; break;
+    case 81: ret = 10081; break;
+    case 82: ret = 10082; break;
+    case 83: ret = 10083; break;
+    case 84: ret = 10084; break;
+    case 85: ret = 10085; break;
+    case 86: ret = 10086; break;
+    case 87: ret = 10087; break;
+    case 88: ret = 10088; break;
+    case 89: ret = 10089; break;
+    case 90: ret = 10090; break;
+    case 91: ret = 10091; break;
+    case 92: ret = 10092; break;
+    case 93: ret = 10093; break;
+    case 94: ret = 10094; break;
+    case 95: ret = 10095; break;
+    case 96: ret = 10096; break;
+    case 97: ret = 10097; break;
+    case 98: ret = 10098; break;
+    case 99: ret = 10099; break;
+    case 100: ret = 10100; break;
+    case 101: ret = 10101; break;
+    case 102: ret = 10102; break;
+    case 103: ret = 10103; break;
+    case 104: ret = 10104; break;
+    case 105: ret = 10105; break;
+    case 106: ret = 10106; break;
+    case 107: ret = 10107; break;
+    case 108: ret = 10108; break;
+    case 109: ret = 10109; break;
+    case 110: ret = 10110; break;
+    case 111: ret = 10111; break;
+    case 112: ret = 10112; break;
+    case 113: ret = 10113; break;
+    case 114: ret = 10114; break;
+    case 115: ret = 10115; break;
+    case 116: ret = 10116; break;
+    case 117: ret = 10117; break;
+    case 118: ret = 10118; break;
+    case 119: ret = 10119; break;
+    case 120: ret = 10120; break;
+    case 121: ret = 10121; break;
+    case 122: ret = 10122; break;
+    case 123: ret = 10123; break;
+    case 124: ret = 10124; break;
+    case 125: ret = 10125; break;
+    case 126: ret = 10126; break;
+    case 127: ret = 10127; break;
+    case 128: ret = 10128; break;
+    case 129: ret = 10129; break;
+    case 130: ret = 10130; break;
+    case 131: ret = 10131; break;
+    case 132: ret = 10132; break;
+    case 133: ret = 10133; break;
+    case 134: ret = 10134; break;
+    case 135: ret = 10135; break;
+    case 136: ret = 10136; break;
+    case 137: ret = 10137; break;
+    case 138: ret = 10138; break;
+    case 139: ret = 10139; break;
+    case 140: ret = 10140; break;
+    case 141: ret = 10141; break;
+    case 142: ret = 10142; break;
+    case 143: ret = 10143; break;
+    case 144: ret = 10144; break;
+    case 145: ret = 10145; break;
+    case 146: ret = 10146; break;
+    case 147: ret = 10147; break;
+    case 148: ret = 10148; break;
+    case 149: ret = 10149; break;
+    case 150: ret = 10150; break;
+    case 151: ret = 10151; break;
+    case 152: ret = 10152; break;
+    case 153: ret = 10153; break;
+    case 154: ret = 10154; break;
+    case 155: ret = 10155; break;
+    case 156: ret = 10156; break;
+    case 157: ret = 10157; break;
+    case 158: ret = 10158; break;
+    case 159: ret = 10159; break;
+    case 160: ret = 10160; break;
+    case 161: ret = 10161; break;
+    case 162: ret = 10162; break;
+    case 163: ret = 10163; break;
+    case 164: ret = 10164; break;
+    case 165: ret = 10165; break;
+    case 166: ret = 10166; break;
+    case 167: ret = 10167; break;
+    case 168: ret = 10168; break;
+    case 169: ret = 10169; break;
+    case 170: ret = 10170; break;
+    case 171: ret = 10171; break;
+    case 172: ret = 10172; break;
+    case 173: ret = 10173; break;
+    case 174: ret = 10174; break;
+    case 175: ret = 10175; break;
+    case 176: ret = 10176; break;
+    case 177: ret = 10177; break;
+    case 178: ret = 10178; break;
+    case 179: ret = 10179; break;
+    case 180: ret = 10180; break;
+    case 181: ret = 10181; break;
+    case 182: ret = 10182; break;
+    case 183: ret = 10183; break;
+    case 184: ret = 10184; break;
+    case 185: ret = 10185; break;
+    case 186: ret = 10186; break;
+    case 187: ret = 10187; break;
+    case 188: ret = 10188; break;
+    case 189: ret = 10189; break;
+    case 190: ret = 10190; break;
+    case 191: ret = 10191; break;
+    case 192: ret = 10192; break;
+    case 193: ret = 10193; break;
+    case 194: ret = 10194; break;
+    case 195: ret = 10195; break;
+    case 196: ret = 10196; break;
+    case 197: ret = 10197; break;
+    case 198: ret = 10198; break;
+    case 199: ret = 10199; break;
+    case 200: ret = 10200; break;
+    case 201: ret = 10201; break;
+    case 202: ret = 10202; break;
+    case 203: ret = 10203; break;
+    case 204: ret = 10204; break;
+    case 205: ret = 10205; break;
+    case 206: ret = 10206; break;
+    case 207: ret = 10207; break;
+    case 208: ret = 10208; break;
+    case 209: ret = 10209; break;
+    case 210: ret = 10210; break;
+    case 211: ret = 10211; break;
+    case 212: ret = 10212; break;
+    case 213: ret = 10213; break;
+    case 214: ret = 10214; break;
+    case 215: ret = 10215; break;
+    case 216: ret = 10216; break;
+    case 217: ret = 10217; break;
+    case 218: ret = 10218; break;
+    case 219: ret = 10219; break;
+    case 220: ret = 10220; break;
+    case 221: ret = 10221; break;
+    case 222: ret = 10222; break;
+    case 223: ret = 10223; break;
+    case 224: ret = 10224; break;
+    case 225: ret = 10225; break;
+    case 226: ret = 10226; break;
+    case 227: ret = 10227; break;
+    case 228: ret = 10228; break;
+    case 229: ret = 10229; break;
+    case 230: ret = 10230; break;
+    case 231: ret = 10231; break;
+    case 232: ret = 10232; break;
+    case 233: ret = 10233; break;
+    case 234: ret = 10234; break;
+    case 235: ret = 10235; break;
+    case 236: ret = 10236; break;
+    case 237: ret = 10237; break;
+    case 238: ret = 10238; break;
+    case 239: ret = 10239; break;
+    case 240: ret = 10240; break;
+    case 241: ret = 10241; break;
+    case 242: ret = 10242; break;
+    case 243: ret = 10243; break;
+    case 244: ret = 10244; break;
+    case 245: ret = 10245; break;
+    case 246: ret = 10246; break;
+    case 247: ret = 10247; break;
+    case 248: ret = 10248; break;
+    case 249: ret = 10249; break;
+    case 250: ret = 10250; break;
+    case 251: ret = 10251; break;
+    case 252: ret = 10252; break;
+    case 253: ret = 10253; break;
+    case 254: ret = 10254; break;
+    case 255: ret = 10255; break;
+    case 256: ret = 10256; break;
+    case 257: ret = 10257; break;
+    case 258: ret = 10258; break;
+    case 259: ret = 10259; break;
+    case 260: ret = 10260; break;
+    case 261: ret = 10261; break;
+    case 262: ret = 10262; break;
+    case 263: ret = 10263; break;
+    case 264: ret = 10264; break;
+    case 265: ret = 10265; break;
+    case 266: ret = 10266; break;
+    case 267: ret = 10267; break;
+    case 268: ret = 10268; break;
+    case 269: ret = 10269; break;
+    case 270: ret = 10270; break;
+    case 271: ret = 10271; break;
+    case 272: ret = 10272; break;
+    case 273: ret = 10273; break;
+    case 274: ret = 10274; break;
+    case 275: ret = 10275; break;
+    case 276: ret = 10276; break;
+    case 277: ret = 10277; break;
+    case 278: ret = 10278; break;
+    case 279: ret = 10279; break;
+    case 280: ret = 10280; break;
+    case 281: ret = 10281; break;
+    case 282: ret = 10282; break;
+    case 283: ret = 10283; break;
+    case 284: ret = 10284; break;
+    case 285: ret = 10285; break;
+    case 286: ret = 10286; break;
+    case 287: ret = 10287; break;
+    case 288: ret = 10288; break;
+    case 289: ret = 10289; break;
+    case 290: ret = 10290; break;
+    case 291: ret = 10291; break;
+    case 292: ret = 10292; break;
+    case 293: ret = 10293; break;
+    case 294: ret = 10294; break;
+    case 295: ret = 10295; break;
+    case 296: ret = 10296; break;
+    case 297: ret = 10297; break;
+    case 298: ret = 10298; break;
+    case 299: ret = 10299; break;
+    case 300: ret = 10300; break;
+    case 301: ret = 10301; break;
+    case 302: ret = 10302; break;
+    case 303: ret = 10303; break;
+    case 304: ret = 10304; break;
+    case 305: ret = 10305; break;
+    case 306: ret = 10306; break;
+    case 307: ret = 10307; break;
+    case 308: ret = 10308; break;
+    case 309: ret = 10309; break;
+    case 310: ret = 10310; break;
+    case 311: ret = 10311; break;
+    case 312: ret = 10312; break;
+    case 313: ret = 10313; break;
+    case 314: ret = 10314; break;
+    case 315: ret = 10315; break;
+    case 316: ret = 10316; break;
+    case 317: ret = 10317; break;
+    case 318: ret = 10318; break;
+    case 319: ret = 10319; break;
+    case 320: ret = 10320; break;
+    case 321: ret = 10321; break;
+    case 322: ret = 10322; break;
+    case 323: ret = 10323; break;
+    case 324: ret = 10324; break;
+    case 325: ret = 10325; break;
+    case 326: ret = 10326; break;
+    case 327: ret = 10327; break;
+    case 328: ret = 10328; break;
+    case 329: ret = 10329; break;
+    case 330: ret = 10330; break;
+    case 331: ret = 10331; break;
+    case 332: ret = 10332; break;
+    case 333: ret = 10333; break;
+    case 334: ret = 10334; break;
+    case 335: ret = 10335; break;
+    case 336: ret = 10336; break;
+    case 337: ret = 10337; break;
+    case 338: ret = 10338; break;
+    case 339: ret = 10339; break;
+    case 340: ret = 10340; break;
+    case 341: ret = 10341; break;
+    case 342: ret = 10342; break;
+    case 343: ret = 10343; break;
+    case 344: ret = 10344; break;
+    case 345: ret = 10345; break;
+    case 346: ret = 10346; break;
+    case 347: ret = 10347; break;
+    case 348: ret = 10348; break;
+    case 349: ret = 10349; break;
+    case 350: ret = 10350; break;
+    case 351: ret = 10351; break;
+    case 352: ret = 10352; break;
+    case 353: ret = 10353; break;
+    case 354: ret = 10354; break;
+    case 355: ret = 10355; break;
+    case 356: ret = 10356; break;
+    case 357: ret = 10357; break;
+    case 358: ret = 10358; break;
+    case 359: ret = 10359; break;
+    case 360: ret = 10360; break;
+    case 361: ret = 10361; break;
+    case 362: ret = 10362; break;
+    case 363: ret = 10363; break;
+    case 364: ret = 10364; break;
+    case 365: ret = 10365; break;
+    case 366: ret = 10366; break;
+    case 367: ret = 10367; break;
+    case 368: ret = 10368; break;
+    case 369: ret = 10369; break;
+    case 370: ret = 10370; break;
+    case 371: ret = 10371; break;
+    case 372: ret = 10372; break;
+    case 373: ret = 10373; break;
+    case 374: ret = 10374; break;
+    case 375: ret = 10375; break;
+    case 376: ret = 10376; break;
+    case 377: ret = 10377; break;
+    case 378: ret = 10378; break;
+    case 379: ret = 10379; break;
+    case 380: ret = 10380; break;
+    case 381: ret = 10381; break;
+    case 382: ret = 10382; break;
+    case 383: ret = 10383; break;
+    case 384: ret = 10384; break;
+    case 385: ret = 10385; break;
+    case 386: ret = 10386; break;
+    case 387: ret = 10387; break;
+    case 388: ret = 10388; break;
+    case 389: ret = 10389; break;
+    case 390: ret = 10390; break;
+    case 391: ret = 10391; break;
+    case 392: ret = 10392; break;
+    case 393: ret = 10393; break;
+    case 394: ret = 10394; break;
+    case 395: ret = 10395; break;
+    case 396: ret = 10396; break;
+    case 397: ret = 10397; break;
+    case 398: ret = 10398; break;
+    case 399: ret = 10399; break;
+    case 400: ret = 10400; break;
+    case 401: ret = 10401; break;
+    case 402: ret = 10402; break;
+    case 403: ret = 10403; break;
+    case 404: ret = 10404; break;
+    case 405: ret = 10405; break;
+    case 406: ret = 10406; break;
+    case 407: ret = 10407; break;
+    case 408: ret = 10408; break;
+    case 409: ret = 10409; break;
+    case 410: ret = 10410; break;
+    case 411: ret = 10411; break;
+    case 412: ret = 10412; break;
+    case 413: ret = 10413; break;
+    case 414: ret = 10414; break;
+    case 415: ret = 10415; break;
+    case 416: ret = 10416; break;
+    case 417: ret = 10417; break;
+    case 418: ret = 10418; break;
+    case 419: ret = 10419; break;
+    case 420: ret = 10420; break;
+    case 421: ret = 10421; break;
+    case 422: ret = 10422; break;
+    case 423: ret = 10423; break;
+    case 424: ret = 10424; break;
+    case 425: ret = 10425; break;
+    case 426: ret = 10426; break;
+    case 427: ret = 10427; break;
+    case 428: ret = 10428; break;
+    case 429: ret = 10429; break;
+    case 430: ret = 10430; break;
+    case 431: ret = 10431; break;
+    case 432: ret = 10432; break;
+    case 433: ret = 10433; break;
+    case 434: ret = 10434; break;
+    case 435: ret = 10435; break;
+    case 436: ret = 10436; break;
+    case 437: ret = 10437; break;
+    case 438: ret = 10438; break;
+    case 439: ret = 10439; break;
+    case 440: ret = 10440; break;
+    case 441: ret = 10441; break;
+    case 442: ret = 10442; break;
+    case 443: ret = 10443; break;
+    case 444: ret = 10444; break;
+    case 445: ret = 10445; break;
+    case 446: ret = 10446; break;
+    case 447: ret = 10447; break;
+    case 448: ret = 10448; break;
+    case 449: ret = 10449; break;
+    case 450: ret = 10450; break;
+    case 451: ret = 10451; break;
+    case 452: ret = 10452; break;
+    case 453: ret = 10453; break;
+    case 454: ret = 10454; break;
+    case 455: ret = 10455; break;
+    case 456: ret = 10456; break;
+    case 457: ret = 10457; break;
+    case 458: ret = 10458; break;
+    case 459: ret = 10459; break;
+    case 460: ret = 10460; break;
+    case 461: ret = 10461; break;
+    case 462: ret = 10462; break;
+    case 463: ret = 10463; break;
+    case 464: ret = 10464; break;
+    case 465: ret = 10465; break;
+    case 466: ret = 10466; break;
+    case 467: ret = 10467; break;
+    case 468: ret = 10468; break;
+    case 469: ret = 10469; break;
+    case 470: ret = 10470; break;
+    case 471: ret = 10471; break;
+    case 472: ret = 10472; break;
+    case 473: ret = 10473; break;
+    case 474: ret = 10474; break;
+    case 475: ret = 10475; break;
+    case 476: ret = 10476; break;
+    case 477: ret = 10477; break;
+    case 478: ret = 10478; break;
+    case 479: ret = 10479; break;
+    case 480: ret = 10480; break;
+    case 481: ret = 10481; break;
+    case 482: ret = 10482; break;
+    case 483: ret = 10483; break;
+    case 484: ret = 10484; break;
+    case 485: ret = 10485; break;
+    case 486: ret = 10486; break;
+    case 487: ret = 10487; break;
+    case 488: ret = 10488; break;
+    case 489: ret = 10489; break;
+    case 490: ret = 10490; break;
+    case 491: ret = 10491; break;
+    case 492: ret = 10492; break;
+    case 493: ret = 10493; break;
+    case 494: ret = 10494; break;
+    case 495: ret = 10495; break;
+    case 496: ret = 10496; break;
+    case 497: ret = 10497; break;
+    case 498: ret = 10498; break;
+    case 499: ret = 10499; break;
+    case 500: ret = 10500; break;
+    case 501: ret = 10501; break;
+    case 502: ret = 10502; break;
+    case 503: ret = 10503; break;
+    case 504: ret = 10504; break;
+    case 505: ret = 10505; break;
+    case 506: ret = 10506; break;
+    case 507: ret = 10507; break;
+    case 508: ret = 10508; break;
+    case 509: ret = 10509; break;
+    case 510: ret = 10510; break;
+    case 511: ret = 10511; break;
+    case 512: ret = 10512; break;
+    case 513: ret = 10513; break;
+    case 514: ret = 10514; break;
+    case 515: ret = 10515; break;
+    case 516: ret = 10516; break;
+    case 517: ret = 10517; break;
+    case 518: ret = 10518; break;
+    case 519: ret = 10519; break;
+    case 520: ret = 10520; break;
+    case 521: ret = 10521; break;
+    case 522: ret = 10522; break;
+    case 523: ret = 10523; break;
+    case 524: ret = 10524; break;
+    case 525: ret = 10525; break;
+    case 526: ret = 10526; break;
+    case 527: ret = 10527; break;
+    case 528: ret = 10528; break;
+    case 529: ret = 10529; break;
+    case 530: ret = 10530; break;
+    case 531: ret = 10531; break;
+    case 532: ret = 10532; break;
+    case 533: ret = 10533; break;
+    case 534: ret = 10534; break;
+    case 535: ret = 10535; break;
+    case 536: ret = 10536; break;
+    case 537: ret = 10537; break;
+    case 538: ret = 10538; break;
+    case 539: ret = 10539; break;
+    case 540: ret = 10540; break;
+    case 541: ret = 10541; break;
+    case 542: ret = 10542; break;
+    case 543: ret = 10543; break;
+    case 544: ret = 10544; break;
+    case 545: ret = 10545; break;
+    case 546: ret = 10546; break;
+    case 547: ret = 10547; break;
+    case 548: ret = 10548; break;
+    case 549: ret = 10549; break;
+    case 550: ret = 10550; break;
+    case 551: ret = 10551; break;
+    case 552: ret = 10552; break;
+    case 553: ret = 10553; break;
+    case 554: ret = 10554; break;
+    case 555: ret = 10555; break;
+    case 556: ret = 10556; break;
+    case 557: ret = 10557; break;
+    case 558: ret = 10558; break;
+    case 559: ret = 10559; break;
+    case 560: ret = 10560; break;
+    case 561: ret = 10561; break;
+    case 562: ret = 10562; break;
+    case 563: ret = 10563; break;
+    case 564: ret = 10564; break;
+    case 565: ret = 10565; break;
+    case 566: ret = 10566; break;
+    case 567: ret = 10567; break;
+    case 568: ret = 10568; break;
+    case 569: ret = 10569; break;
+    case 570: ret = 10570; break;
+    case 571: ret = 10571; break;
+    case 572: ret = 10572; break;
+    case 573: ret = 10573; break;
+    case 574: ret = 10574; break;
+    case 575: ret = 10575; break;
+    case 576: ret = 10576; break;
+    case 577: ret = 10577; break;
+    case 578: ret = 10578; break;
+    case 579: ret = 10579; break;
+    case 580: ret = 10580; break;
+    case 581: ret = 10581; break;
+    case 582: ret = 10582; break;
+    case 583: ret = 10583; break;
+    case 584: ret = 10584; break;
+    case 585: ret = 10585; break;
+    case 586: ret = 10586; break;
+    case 587: ret = 10587; break;
+    case 588: ret = 10588; break;
+    case 589: ret = 10589; break;
+    case 590: ret = 10590; break;
+    case 591: ret = 10591; break;
+    case 592: ret = 10592; break;
+    case 593: ret = 10593; break;
+    case 594: ret = 10594; break;
+    case 595: ret = 10595; break;
+    case 596: ret = 10596; break;
+    case 597: ret = 10597; break;
+    case 598: ret = 10598; break;
+    case 599: ret = 10599; break;
+    case 600: ret = 10600; break;
+    case 601: ret = 10601; break;
+    case 602: ret = 10602; break;
+    case 603: ret = 10603; break;
+    case 604: ret = 10604; break;
+    case 605: ret = 10605; break;
+    case 606: ret = 10606; break;
+    case 607: ret = 10607; break;
+    case 608: ret = 10608; break;
+    case 609: ret = 10609; break;
+    case 610: ret = 10610; break;
+    case 611: ret = 10611; break;
+    case 612: ret = 10612; break;
+    case 613: ret = 10613; break;
+    case 614: ret = 10614; break;
+    case 615: ret = 10615; break;
+    case 616: ret = 10616; break;
+    case 617: ret = 10617; break;
+    case 618: ret = 10618; break;
+    case 619: ret = 10619; break;
+    case 620: ret = 10620; break;
+    case 621: ret = 10621; break;
+    case 622: ret = 10622; break;
+    case 623: ret = 10623; break;
+    case 624: ret = 10624; break;
+    case 625: ret = 10625; break;
+    case 626: ret = 10626; break;
+    case 627: ret = 10627; break;
+    case 628: ret = 10628; break;
+    case 629: ret = 10629; break;
+    case 630: ret = 10630; break;
+    case 631: ret = 10631; break;
+    case 632: ret = 10632; break;
+    case 633: ret = 10633; break;
+    case 634: ret = 10634; break;
+    case 635: ret = 10635; break;
+    case 636: ret = 10636; break;
+    case 637: ret = 10637; break;
+    case 638: ret = 10638; break;
+    case 639: ret = 10639; break;
+    case 640: ret = 10640; break;
+    case 641: ret = 10641; break;
+    case 642: ret = 10642; break;
+    case 643: ret = 10643; break;
+    case 644: ret = 10644; break;
+    case 645: ret = 10645; break;
+    case 646: ret = 10646; break;
+    case 647: ret = 10647; break;
+    case 648: ret = 10648; break;
+    case 649: ret = 10649; break;
+    case 650: ret = 10650; break;
+    case 651: ret = 10651; break;
+    case 652: ret = 10652; break;
+    case 653: ret = 10653; break;
+    case 654: ret = 10654; break;
+    case 655: ret = 10655; break;
+    case 656: ret = 10656; break;
+    case 657: ret = 10657; break;
+    case 658: ret = 10658; break;
+    case 659: ret = 10659; break;
+    case 660: ret = 10660; break;
+    case 661: ret = 10661; break;
+    case 662: ret = 10662; break;
+    case 663: ret = 10663; break;
+    case 664: ret = 10664; break;
+    case 665: ret = 10665; break;
+    case 666: ret = 10666; break;
+    case 667: ret = 10667; break;
+    case 668: ret = 10668; break;
+    case 669: ret = 10669; break;
+    case 670: ret = 10670; break;
+    case 671: ret = 10671; break;
+    case 672: ret = 10672; break;
+    case 673: ret = 10673; break;
+    case 674: ret = 10674; break;
+    case 675: ret = 10675; break;
+    case 676: ret = 10676; break;
+    case 677: ret = 10677; break;
+    case 678: ret = 10678; break;
+    case 679: ret = 10679; break;
+    case 680: ret = 10680; break;
+    case 681: ret = 10681; break;
+    case 682: ret = 10682; break;
+    case 683: ret = 10683; break;
+    case 684: ret = 10684; break;
+    case 685: ret = 10685; break;
+    case 686: ret = 10686; break;
+    case 687: ret = 10687; break;
+    case 688: ret = 10688; break;
+    case 689: ret = 10689; break;
+    case 690: ret = 10690; break;
+    case 691: ret = 10691; break;
+    case 692: ret = 10692; break;
+    case 693: ret = 10693; break;
+    case 694: ret = 10694; break;
+    case 695: ret = 10695; break;
+    case 696: ret = 10696; break;
+    case 697: ret = 10697; break;
+    case 698: ret = 10698; break;
+    case 699: ret = 10699; break;
+    case 700: ret = 10700; break;
+    case 701: ret = 10701; break;
+    case 702: ret = 10702; break;
+    case 703: ret = 10703; break;
+    case 704: ret = 10704; break;
+    case 705: ret = 10705; break;
+    case 706: ret = 10706; break;
+    case 707: ret = 10707; break;
+    case 708: ret = 10708; break;
+    case 709: ret = 10709; break;
+    case 710: ret = 10710; break;
+    case 711: ret = 10711; break;
+    case 712: ret = 10712; break;
+    case 713: ret = 10713; break;
+    case 714: ret = 10714; break;
+    case 715: ret = 10715; break;
+    case 716: ret = 10716; break;
+    case 717: ret = 10717; break;
+    case 718: ret = 10718; break;
+    case 719: ret = 10719; break;
+    case 720: ret = 10720; break;
+    case 721: ret = 10721; break;
+    case 722: ret = 10722; break;
+    case 723: ret = 10723; break;
+    case 724: ret = 10724; break;
+    case 725: ret = 10725; break;
+    case 726: ret = 10726; break;
+    case 727: ret = 10727; break;
+    case 728: ret = 10728; break;
+    case 729: ret = 10729; break;
+    case 730: ret = 10730; break;
+    case 731: ret = 10731; break;
+    case 732: ret = 10732; break;
+    case 733: ret = 10733; break;
+    case 734: ret = 10734; break;
+    case 735: ret = 10735; break;
+    case 736: ret = 10736; break;
+    case 737: ret = 10737; break;
+    case 738: ret = 10738; break;
+    case 739: ret = 10739; break;
+    case 740: ret = 10740; break;
+    case 741: ret = 10741; break;
+    case 742: ret = 10742; break;
+    case 743: ret = 10743; break;
+    case 744: ret = 10744; break;
+    case 745: ret = 10745; break;
+    case 746: ret = 10746; break;
+    case 747: ret = 10747; break;
+    case 748: ret = 10748; break;
+    case 749: ret = 10749; break;
+    case 750: ret = 10750; break;
+    case 751: ret = 10751; break;
+    case 752: ret = 10752; break;
+    case 753: ret = 10753; break;
+    case 754: ret = 10754; break;
+    case 755: ret = 10755; break;
+    case 756: ret = 10756; break;
+    case 757: ret = 10757; break;
+    case 758: ret = 10758; break;
+    case 759: ret = 10759; break;
+    case 760: ret = 10760; break;
+    case 761: ret = 10761; break;
+    case 762: ret = 10762; break;
+    case 763: ret = 10763; break;
+    case 764: ret = 10764; break;
+    case 765: ret = 10765; break;
+    case 766: ret = 10766; break;
+    case 767: ret = 10767; break;
+    case 768: ret = 10768; break;
+    case 769: ret = 10769; break;
+    case 770: ret = 10770; break;
+    case 771: ret = 10771; break;
+    case 772: ret = 10772; break;
+    case 773: ret = 10773; break;
+    case 774: ret = 10774; break;
+    case 775: ret = 10775; break;
+    case 776: ret = 10776; break;
+    case 777: ret = 10777; break;
+    case 778: ret = 10778; break;
+    case 779: ret = 10779; break;
+    case 780: ret = 10780; break;
+    case 781: ret = 10781; break;
+    case 782: ret = 10782; break;
+    case 783: ret = 10783; break;
+    case 784: ret = 10784; break;
+    case 785: ret = 10785; break;
+    case 786: ret = 10786; break;
+    case 787: ret = 10787; break;
+    case 788: ret = 10788; break;
+    case 789: ret = 10789; break;
+    case 790: ret = 10790; break;
+    case 791: ret = 10791; break;
+    case 792: ret = 10792; break;
+    case 793: ret = 10793; break;
+    case 794: ret = 10794; break;
+    case 795: ret = 10795; break;
+    case 796: ret = 10796; break;
+    case 797: ret = 10797; break;
+    case 798: ret = 10798; break;
+    case 799: ret = 10799; break;
+    case 800: ret = 10800; break;
+    case 801: ret = 10801; break;
+    case 802: ret = 10802; break;
+    case 803: ret = 10803; break;
+    case 804: ret = 10804; break;
+    case 805: ret = 10805; break;
+    case 806: ret = 10806; break;
+    case 807: ret = 10807; break;
+    case 808: ret = 10808; break;
+    case 809: ret = 10809; break;
+    case 810: ret = 10810; break;
+    case 811: ret = 10811; break;
+    case 812: ret = 10812; break;
+    case 813: ret = 10813; break;
+    case 814: ret = 10814; break;
+    case 815: ret = 10815; break;
+    case 816: ret = 10816; break;
+    case 817: ret = 10817; break;
+    case 818: ret = 10818; break;
+    case 819: ret = 10819; break;
+    case 820: ret = 10820; break;
+    case 821: ret = 10821; break;
+    case 822: ret = 10822; break;
+    case 823: ret = 10823; break;
+    case 824: ret = 10824; break;
+    case 825: ret = 10825; break;
+    case 826: ret = 10826; break;
+    case 827: ret = 10827; break;
+    case 828: ret = 10828; break;
+    case 829: ret = 10829; break;
+    case 830: ret = 10830; break;
+    case 831: ret = 10831; break;
+    case 832: ret = 10832; break;
+    case 833: ret = 10833; break;
+    case 834: ret = 10834; break;
+    case 835: ret = 10835; break;
+    case 836: ret = 10836; break;
+    case 837: ret = 10837; break;
+    case 838: ret = 10838; break;
+    case 839: ret = 10839; break;
+    case 840: ret = 10840; break;
+    case 841: ret = 10841; break;
+    case 842: ret = 10842; break;
+    case 843: ret = 10843; break;
+    case 844: ret = 10844; break;
+    case 845: ret = 10845; break;
+    case 846: ret = 10846; break;
+    case 847: ret = 10847; break;
+    case 848: ret = 10848; break;
+    case 849: ret = 10849; break;
+    case 850: ret = 10850; break;
+    case 851: ret = 10851; break;
+    case 852: ret = 10852; break;
+    case 853: ret = 10853; break;
+    case 854: ret = 10854; break;
+    case 855: ret = 10855; break;
+    case 856: ret = 10856; break;
+    case 857: ret = 10857; break;
+    case 858: ret = 10858; break;
+    case 859: ret = 10859; break;
+    case 860: ret = 10860; break;
+    case 861: ret = 10861; break;
+    case 862: ret = 10862; break;
+    case 863: ret = 10863; break;
+    case 864: ret = 10864; break;
+    case 865: ret = 10865; break;
+    case 866: ret = 10866; break;
+    case 867: ret = 10867; break;
+    case 868: ret = 10868; break;
+    case 869: ret = 10869; break;
+    case 870: ret = 10870; break;
+    case 871: ret = 10871; break;
+    case 872: ret = 10872; break;
+    case 873: ret = 10873; break;
+    case 874: ret = 10874; break;
+    case 875: ret = 10875; break;
+    case 876: ret = 10876; break;
+    case 877: ret = 10877; break;
+    case 878: ret = 10878; break;
+    case 879: ret = 10879; break;
+    case 880: ret = 10880; break;
+    case 881: ret = 10881; break;
+    case 882: ret = 10882; break;
+    case 883: ret = 10883; break;
+    case 884: ret = 10884; break;
+    case 885: ret = 10885; break;
+    case 886: ret = 10886; break;
+    case 887: ret = 10887; break;
+    case 888: ret = 10888; break;
+    case 889: ret = 10889; break;
+    case 890: ret = 10890; break;
+    case 891: ret = 10891; break;
+    case 892: ret = 10892; break;
+    case 893: ret = 10893; break;
+    case 894: ret = 10894; break;
+    case 895: ret = 10895; break;
+    case 896: ret = 10896; break;
+    case 897: ret = 10897; break;
+    case 898: ret = 10898; break;
+    case 899: ret = 10899; break;
+    case 900: ret = 10900; break;
+    case 901: ret = 10901; break;
+    case 902: ret = 10902; break;
+    case 903: ret = 10903; break;
+    case 904: ret = 10904; break;
+    case 905: ret = 10905; break;
+    case 906: ret = 10906; break;
+    case 907: ret = 10907; break;
+    case 908: ret = 10908; break;
+    case 909: ret = 10909; break;
+    case 910: ret = 10910; break;
+    case 911: ret = 10911; break;
+    case 912: ret = 10912; break;
+    case 913: ret = 10913; break;
+    case 914: ret = 10914; break;
+    case 915: ret = 10915; break;
+    case 916: ret = 10916; break;
+    case 917: ret = 10917; break;
+    case 918: ret = 10918; break;
+    case 919: ret = 10919; break;
+    case 920: ret = 10920; break;
+    case 921: ret = 10921; break;
+    case 922: ret = 10922; break;
+    case 923: ret = 10923; break;
+    case 924: ret = 10924; break;
+    case 925: ret = 10925; break;
+    case 926: ret = 10926; break;
+    case 927: ret = 10927; break;
+    case 928: ret = 10928; break;
+    case 929: ret = 10929; break;
+    case 930: ret = 10930; break;
+    case 931: ret = 10931; break;
+    case 932: ret = 10932; break;
+    case 933: ret = 10933; break;
+    case 934: ret = 10934; break;
+    case 935: ret = 10935; break;
+    case 936: ret = 10936; break;
+    case 937: ret = 10937; break;
+    case 938: ret = 10938; break;
+    case 939: ret = 10939; break;
+    case 940: ret = 10940; break;
+    case 941: ret = 10941; break;
+    case 942: ret = 10942; break;
+    case 943: ret = 10943; break;
+    case 944: ret = 10944; break;
+    case 945: ret = 10945; break;
+    case 946: ret = 10946; break;
+    case 947: ret = 10947; break;
+    case 948: ret = 10948; break;
+    case 949: ret = 10949; break;
+    case 950: ret = 10950; break;
+    case 951: ret = 10951; break;
+    case 952: ret = 10952; break;
+    case 953: ret = 10953; break;
+    case 954: ret = 10954; break;
+    case 955: ret = 10955; break;
+    case 956: ret = 10956; break;
+    case 957: ret = 10957; break;
+    case 958: ret = 10958; break;
+    case 959: ret = 10959; break;
+    case 960: ret = 10960; break;
+    case 961: ret = 10961; break;
+    case 962: ret = 10962; break;
+    case 963: ret = 10963; break;
+    case 964: ret = 10964; break;
+    case 965: ret = 10965; break;
+    case 966: ret = 10966; break;
+    case 967: ret = 10967; break;
+    case 968: ret = 10968; break;
+    case 969: ret = 10969; break;
+    case 970: ret = 10970; break;
+    case 971: ret = 10971; break;
+    case 972: ret = 10972; break;
+    case 973: ret = 10973; break;
+    case 974: ret = 10974; break;
+    case 975: ret = 10975; break;
+    case 976: ret = 10976; break;
+    case 977: ret = 10977; break;
+    case 978: ret = 10978; break;
+    case 979: ret = 10979; break;
+    case 980: ret = 10980; break;
+    case 981: ret = 10981; break;
+    case 982: ret = 10982; break;
+    case 983: ret = 10983; break;
+    case 984: ret = 10984; break;
+    case 985: ret = 10985; break;
+    case 986: ret = 10986; break;
+    case 987: ret = 10987; break;
+    case 988: ret = 10988; break;
+    case 989: ret = 10989; break;
+    case 990: ret = 10990; break;
+    case 991: ret = 10991; break;
+    case 992: ret = 10992; break;
+    case 993: ret = 10993; break;
+    case 994: ret = 10994; break;
+    case 995: ret = 10995; break;
+    case 996: ret = 10996; break;
+    case 997: ret = 10997; break;
+    case 998: ret = 10998; break;
+    case 999: ret = 10999; break;
+    case 1000: ret = 11000; break;
+    case 1001: ret = 11001; break;
+    case 1002: ret = 11002; break;
+    case 1003: ret = 11003; break;
+    case 1004: ret = 11004; break;
+    case 1005: ret = 11005; break;
+    case 1006: ret = 11006; break;
+    case 1007: ret = 11007; break;
+    case 1008: ret = 11008; break;
+    case 1009: ret = 11009; break;
+    case 1010: ret = 11010; break;
+    case 1011: ret = 11011; break;
+    case 1012: ret = 11012; break;
+    case 1013: ret = 11013; break;
+    case 1014: ret = 11014; break;
+    case 1015: ret = 11015; break;
+    case 1016: ret = 11016; break;
+    case 1017: ret = 11017; break;
+    case 1018: ret = 11018; break;
+    case 1019: ret = 11019; break;
+    case 1020: ret = 11020; break;
+    case 1021: ret = 11021; break;
+    case 1022: ret = 11022; break;
+    case 1023: ret = 11023; break;
+    case 1024: ret = 11024; break;
+    case 1025: ret = 11025; break;
+    case 1026: ret = 11026; break;
+    case 1027: ret = 11027; break;
+    case 1028: ret = 11028; break;
+    case 1029: ret = 11029; break;
+    case 1030: ret = 11030; break;
+    case 1031: ret = 11031; break;
+    case 1032: ret = 11032; break;
+    case 1033: ret = 11033; break;
+    case 1034: ret = 11034; break;
+    case 1035: ret = 11035; break;
+    case 1036: ret = 11036; break;
+    case 1037: ret = 11037; break;
+    case 1038: ret = 11038; break;
+    case 1039: ret = 11039; break;
+    case 1040: ret = 11040; break;
+    case 1041: ret = 11041; break;
+    case 1042: ret = 11042; break;
+    case 1043: ret = 11043; break;
+    case 1044: ret = 11044; break;
+    case 1045: ret = 11045; break;
+    case 1046: ret = 11046; break;
+    case 1047: ret = 11047; break;
+    case 1048: ret = 11048; break;
+    case 1049: ret = 11049; break;
+    case 1050: ret = 11050; break;
+    case 1051: ret = 11051; break;
+    case 1052: ret = 11052; break;
+    case 1053: ret = 11053; break;
+    case 1054: ret = 11054; break;
+    case 1055: ret = 11055; break;
+    case 1056: ret = 11056; break;
+    case 1057: ret = 11057; break;
+    case 1058: ret = 11058; break;
+    case 1059: ret = 11059; break;
+    case 1060: ret = 11060; break;
+    case 1061: ret = 11061; break;
+    case 1062: ret = 11062; break;
+    case 1063: ret = 11063; break;
+    case 1064: ret = 11064; break;
+    case 1065: ret = 11065; break;
+    case 1066: ret = 11066; break;
+    case 1067: ret = 11067; break;
+    case 1068: ret = 11068; break;
+    case 1069: ret = 11069; break;
+    case 1070: ret = 11070; break;
+    case 1071: ret = 11071; break;
+    case 1072: ret = 11072; break;
+    case 1073: ret = 11073; break;
+    case 1074: ret = 11074; break;
+    case 1075: ret = 11075; break;
+    case 1076: ret = 11076; break;
+    case 1077: ret = 11077; break;
+    case 1078: ret = 11078; break;
+    case 1079: ret = 11079; break;
+    case 1080: ret = 11080; break;
+    case 1081: ret = 11081; break;
+    case 1082: ret = 11082; break;
+    case 1083: ret = 11083; break;
+    case 1084: ret = 11084; break;
+    case 1085: ret = 11085; break;
+    case 1086: ret = 11086; break;
+    case 1087: ret = 11087; break;
+    case 1088: ret = 11088; break;
+    case 1089: ret = 11089; break;
+    case 1090: ret = 11090; break;
+    case 1091: ret = 11091; break;
+    case 1092: ret = 11092; break;
+    case 1093: ret = 11093; break;
+    case 1094: ret = 11094; break;
+    case 1095: ret = 11095; break;
+    case 1096: ret = 11096; break;
+    case 1097: ret = 11097; break;
+    case 1098: ret = 11098; break;
+    case 1099: ret = 11099; break;
+    case 1100: ret = 11100; break;
+    case 1101: ret = 11101; break;
+    case 1102: ret = 11102; break;
+    case 1103: ret = 11103; break;
+    case 1104: ret = 11104; break;
+    case 1105: ret = 11105; break;
+    case 1106: ret = 11106; break;
+    case 1107: ret = 11107; break;
+    case 1108: ret = 11108; break;
+    case 1109: ret = 11109; break;
+    case 1110: ret = 11110; break;
+    case 1111: ret = 11111; break;
+    case 1112: ret = 11112; break;
+    case 1113: ret = 11113; break;
+    case 1114: ret = 11114; break;
+    case 1115: ret = 11115; break;
+    case 1116: ret = 11116; break;
+    case 1117: ret = 11117; break;
+    case 1118: ret = 11118; break;
+    case 1119: ret = 11119; break;
+    case 1120: ret = 11120; break;
+    case 1121: ret = 11121; break;
+    case 1122: ret = 11122; break;
+    case 1123: ret = 11123; break;
+    case 1124: ret = 11124; break;
+    case 1125: ret = 11125; break;
+    case 1126: ret = 11126; break;
+    case 1127: ret = 11127; break;
+    case 1128: ret = 11128; break;
+    case 1129: ret = 11129; break;
+    case 1130: ret = 11130; break;
+    case 1131: ret = 11131; break;
+    case 1132: ret = 11132; break;
+    case 1133: ret = 11133; break;
+    case 1134: ret = 11134; break;
+    case 1135: ret = 11135; break;
+    case 1136: ret = 11136; break;
+    case 1137: ret = 11137; break;
+    case 1138: ret = 11138; break;
+    case 1139: ret = 11139; break;
+    case 1140: ret = 11140; break;
+    case 1141: ret = 11141; break;
+    case 1142: ret = 11142; break;
+    case 1143: ret = 11143; break;
+    case 1144: ret = 11144; break;
+    case 1145: ret = 11145; break;
+    case 1146: ret = 11146; break;
+    case 1147: ret = 11147; break;
+    case 1148: ret = 11148; break;
+    case 1149: ret = 11149; break;
+    case 1150: ret = 11150; break;
+    case 1151: ret = 11151; break;
+    case 1152: ret = 11152; break;
+    case 1153: ret = 11153; break;
+    case 1154: ret = 11154; break;
+    case 1155: ret = 11155; break;
+    case 1156: ret = 11156; break;
+    case 1157: ret = 11157; break;
+    case 1158: ret = 11158; break;
+    case 1159: ret = 11159; break;
+    case 1160: ret = 11160; break;
+    case 1161: ret = 11161; break;
+    case 1162: ret = 11162; break;
+    case 1163: ret = 11163; break;
+    case 1164: ret = 11164; break;
+    case 1165: ret = 11165; break;
+    case 1166: ret = 11166; break;
+    case 1167: ret = 11167; break;
+    case 1168: ret = 11168; break;
+    case 1169: ret = 11169; break;
+    case 1170: ret = 11170; break;
+    case 1171: ret = 11171; break;
+    case 1172: ret = 11172; break;
+    case 1173: ret = 11173; break;
+    case 1174: ret = 11174; break;
+    case 1175: ret = 11175; break;
+    case 1176: ret = 11176; break;
+    case 1177: ret = 11177; break;
+    case 1178: ret = 11178; break;
+    case 1179: ret = 11179; break;
+    case 1180: ret = 11180; break;
+    case 1181: ret = 11181; break;
+    case 1182: ret = 11182; break;
+    case 1183: ret = 11183; break;
+    case 1184: ret = 11184; break;
+    case 1185: ret = 11185; break;
+    case 1186: ret = 11186; break;
+    case 1187: ret = 11187; break;
+    case 1188: ret = 11188; break;
+    case 1189: ret = 11189; break;
+    case 1190: ret = 11190; break;
+    case 1191: ret = 11191; break;
+    case 1192: ret = 11192; break;
+    case 1193: ret = 11193; break;
+    case 1194: ret = 11194; break;
+    case 1195: ret = 11195; break;
+    case 1196: ret = 11196; break;
+    case 1197: ret = 11197; break;
+    case 1198: ret = 11198; break;
+    case 1199: ret = 11199; break;
+    case 1200: ret = 11200; break;
+    case 1201: ret = 11201; break;
+    case 1202: ret = 11202; break;
+    case 1203: ret = 11203; break;
+    case 1204: ret = 11204; break;
+    case 1205: ret = 11205; break;
+    case 1206: ret = 11206; break;
+    case 1207: ret = 11207; break;
+    case 1208: ret = 11208; break;
+    case 1209: ret = 11209; break;
+    case 1210: ret = 11210; break;
+    case 1211: ret = 11211; break;
+    case 1212: ret = 11212; break;
+    case 1213: ret = 11213; break;
+    case 1214: ret = 11214; break;
+    case 1215: ret = 11215; break;
+    case 1216: ret = 11216; break;
+    case 1217: ret = 11217; break;
+    case 1218: ret = 11218; break;
+    case 1219: ret = 11219; break;
+    case 1220: ret = 11220; break;
+    case 1221: ret = 11221; break;
+    case 1222: ret = 11222; break;
+    case 1223: ret = 11223; break;
+    case 1224: ret = 11224; break;
+    case 1225: ret = 11225; break;
+    case 1226: ret = 11226; break;
+    case 1227: ret = 11227; break;
+    case 1228: ret = 11228; break;
+    case 1229: ret = 11229; break;
+    case 1230: ret = 11230; break;
+    case 1231: ret = 11231; break;
+    case 1232: ret = 11232; break;
+    case 1233: ret = 11233; break;
+    case 1234: ret = 11234; break;
+    case 1235: ret = 11235; break;
+    case 1236: ret = 11236; break;
+    case 1237: ret = 11237; break;
+    case 1238: ret = 11238; break;
+    case 1239: ret = 11239; break;
+    case 1240: ret = 11240; break;
+    case 1241: ret = 11241; break;
+    case 1242: ret = 11242; break;
+    case 1243: ret = 11243; break;
+    case 1244: ret = 11244; break;
+    case 1245: ret = 11245; break;
+    case 1246: ret = 11246; break;
+    case 1247: ret = 11247; break;
+    case 1248: ret = 11248; break;
+    case 1249: ret = 11249; break;
+    case 1250: ret = 11250; break;
+    case 1251: ret = 11251; break;
+    case 1252: ret = 11252; break;
+    case 1253: ret = 11253; break;
+    case 1254: ret = 11254; break;
+    case 1255: ret = 11255; break;
+    case 1256: ret = 11256; break;
+    case 1257: ret = 11257; break;
+    case 1258: ret = 11258; break;
+    case 1259: ret = 11259; break;
+    case 1260: ret = 11260; break;
+    case 1261: ret = 11261; break;
+    case 1262: ret = 11262; break;
+    case 1263: ret = 11263; break;
+    case 1264: ret = 11264; break;
+    case 1265: ret = 11265; break;
+    case 1266: ret = 11266; break;
+    case 1267: ret = 11267; break;
+    case 1268: ret = 11268; break;
+    case 1269: ret = 11269; break;
+    case 1270: ret = 11270; break;
+    case 1271: ret = 11271; break;
+    case 1272: ret = 11272; break;
+    case 1273: ret = 11273; break;
+    case 1274: ret = 11274; break;
+    case 1275: ret = 11275; break;
+    case 1276: ret = 11276; break;
+    case 1277: ret = 11277; break;
+    case 1278: ret = 11278; break;
+    case 1279: ret = 11279; break;
+    case 1280: ret = 11280; break;
+    case 1281: ret = 11281; break;
+    case 1282: ret = 11282; break;
+    case 1283: ret = 11283; break;
+    case 1284: ret = 11284; break;
+    case 1285: ret = 11285; break;
+    case 1286: ret = 11286; break;
+    case 1287: ret = 11287; break;
+    case 1288: ret = 11288; break;
+    case 1289: ret = 11289; break;
+    case 1290: ret = 11290; break;
+    case 1291: ret = 11291; break;
+    case 1292: ret = 11292; break;
+    case 1293: ret = 11293; break;
+    case 1294: ret = 11294; break;
+    case 1295: ret = 11295; break;
+    case 1296: ret = 11296; break;
+    case 1297: ret = 11297; break;
+    case 1298: ret = 11298; break;
+    case 1299: ret = 11299; break;
+    case 1300: ret = 11300; break;
+    case 1301: ret = 11301; break;
+    case 1302: ret = 11302; break;
+    case 1303: ret = 11303; break;
+    case 1304: ret = 11304; break;
+    case 1305: ret = 11305; break;
+    case 1306: ret = 11306; break;
+    case 1307: ret = 11307; break;
+    case 1308: ret = 11308; break;
+    case 1309: ret = 11309; break;
+    case 1310: ret = 11310; break;
+    case 1311: ret = 11311; break;
+    case 1312: ret = 11312; break;
+    case 1313: ret = 11313; break;
+    case 1314: ret = 11314; break;
+    case 1315: ret = 11315; break;
+    case 1316: ret = 11316; break;
+    case 1317: ret = 11317; break;
+    case 1318: ret = 11318; break;
+    case 1319: ret = 11319; break;
+    case 1320: ret = 11320; break;
+    case 1321: ret = 11321; break;
+    case 1322: ret = 11322; break;
+    case 1323: ret = 11323; break;
+    case 1324: ret = 11324; break;
+    case 1325: ret = 11325; break;
+    case 1326: ret = 11326; break;
+    case 1327: ret = 11327; break;
+    case 1328: ret = 11328; break;
+    case 1329: ret = 11329; break;
+    case 1330: ret = 11330; break;
+    case 1331: ret = 11331; break;
+    case 1332: ret = 11332; break;
+    case 1333: ret = 11333; break;
+    case 1334: ret = 11334; break;
+    case 1335: ret = 11335; break;
+    case 1336: ret = 11336; break;
+    case 1337: ret = 11337; break;
+    case 1338: ret = 11338; break;
+    case 1339: ret = 11339; break;
+    case 1340: ret = 11340; break;
+    case 1341: ret = 11341; break;
+    case 1342: ret = 11342; break;
+    case 1343: ret = 11343; break;
+    case 1344: ret = 11344; break;
+    case 1345: ret = 11345; break;
+    case 1346: ret = 11346; break;
+    case 1347: ret = 11347; break;
+    case 1348: ret = 11348; break;
+    case 1349: ret = 11349; break;
+    case 1350: ret = 11350; break;
+    case 1351: ret = 11351; break;
+    case 1352: ret = 11352; break;
+    case 1353: ret = 11353; break;
+    case 1354: ret = 11354; break;
+    case 1355: ret = 11355; break;
+    case 1356: ret = 11356; break;
+    case 1357: ret = 11357; break;
+    case 1358: ret = 11358; break;
+    case 1359: ret = 11359; break;
+    case 1360: ret = 11360; break;
+    case 1361: ret = 11361; break;
+    case 1362: ret = 11362; break;
+    case 1363: ret = 11363; break;
+    case 1364: ret = 11364; break;
+    case 1365: ret = 11365; break;
+    case 1366: ret = 11366; break;
+    case 1367: ret = 11367; break;
+    case 1368: ret = 11368; break;
+    case 1369: ret = 11369; break;
+    case 1370: ret = 11370; break;
+    case 1371: ret = 11371; break;
+    case 1372: ret = 11372; break;
+    case 1373: ret = 11373; break;
+    case 1374: ret = 11374; break;
+    case 1375: ret = 11375; break;
+    case 1376: ret = 11376; break;
+    case 1377: ret = 11377; break;
+    case 1378: ret = 11378; break;
+    case 1379: ret = 11379; break;
+    case 1380: ret = 11380; break;
+    case 1381: ret = 11381; break;
+    case 1382: ret = 11382; break;
+    case 1383: ret = 11383; break;
+    case 1384: ret = 11384; break;
+    case 1385: ret = 11385; break;
+    case 1386: ret = 11386; break;
+    case 1387: ret = 11387; break;
+    case 1388: ret = 11388; break;
+    case 1389: ret = 11389; break;
+    case 1390: ret = 11390; break;
+    case 1391: ret = 11391; break;
+    case 1392: ret = 11392; break;
+    case 1393: ret = 11393; break;
+    case 1394: ret = 11394; break;
+    case 1395: ret = 11395; break;
+    case 1396: ret = 11396; break;
+    case 1397: ret = 11397; break;
+    case 1398: ret = 11398; break;
+    case 1399: ret = 11399; break;
+    case 1400: ret = 11400; break;
+    case 1401: ret = 11401; break;
+    case 1402: ret = 11402; break;
+    case 1403: ret = 11403; break;
+    case 1404: ret = 11404; break;
+    case 1405: ret = 11405; break;
+    case 1406: ret = 11406; break;
+    case 1407: ret = 11407; break;
+    case 1408: ret = 11408; break;
+    case 1409: ret = 11409; break;
+    case 1410: ret = 11410; break;
+    case 1411: ret = 11411; break;
+    case 1412: ret = 11412; break;
+    case 1413: ret = 11413; break;
+    case 1414: ret = 11414; break;
+    case 1415: ret = 11415; break;
+    case 1416: ret = 11416; break;
+    case 1417: ret = 11417; break;
+    case 1418: ret = 11418; break;
+    case 1419: ret = 11419; break;
+    case 1420: ret = 11420; break;
+    case 1421: ret = 11421; break;
+    case 1422: ret = 11422; break;
+    case 1423: ret = 11423; break;
+    case 1424: ret = 11424; break;
+    case 1425: ret = 11425; break;
+    case 1426: ret = 11426; break;
+    case 1427: ret = 11427; break;
+    case 1428: ret = 11428; break;
+    case 1429: ret = 11429; break;
+    case 1430: ret = 11430; break;
+    case 1431: ret = 11431; break;
+    case 1432: ret = 11432; break;
+    case 1433: ret = 11433; break;
+    case 1434: ret = 11434; break;
+    case 1435: ret = 11435; break;
+    case 1436: ret = 11436; break;
+    case 1437: ret = 11437; break;
+    case 1438: ret = 11438; break;
+    case 1439: ret = 11439; break;
+    case 1440: ret = 11440; break;
+    case 1441: ret = 11441; break;
+    case 1442: ret = 11442; break;
+    case 1443: ret = 11443; break;
+    case 1444: ret = 11444; break;
+    case 1445: ret = 11445; break;
+    case 1446: ret = 11446; break;
+    case 1447: ret = 11447; break;
+    case 1448: ret = 11448; break;
+    case 1449: ret = 11449; break;
+    case 1450: ret = 11450; break;
+    case 1451: ret = 11451; break;
+    case 1452: ret = 11452; break;
+    case 1453: ret = 11453; break;
+    case 1454: ret = 11454; break;
+    case 1455: ret = 11455; break;
+    case 1456: ret = 11456; break;
+    case 1457: ret = 11457; break;
+    case 1458: ret = 11458; break;
+    case 1459: ret = 11459; break;
+    case 1460: ret = 11460; break;
+    case 1461: ret = 11461; break;
+    case 1462: ret = 11462; break;
+    case 1463: ret = 11463; break;
+    case 1464: ret = 11464; break;
+    case 1465: ret = 11465; break;
+    case 1466: ret = 11466; break;
+    case 1467: ret = 11467; break;
+    case 1468: ret = 11468; break;
+    case 1469: ret = 11469; break;
+    case 1470: ret = 11470; break;
+    case 1471: ret = 11471; break;
+    case 1472: ret = 11472; break;
+    case 1473: ret = 11473; break;
+    case 1474: ret = 11474; break;
+    case 1475: ret = 11475; break;
+    case 1476: ret = 11476; break;
+    case 1477: ret = 11477; break;
+    case 1478: ret = 11478; break;
+    case 1479: ret = 11479; break;
+    case 1480: ret = 11480; break;
+    case 1481: ret = 11481; break;
+    case 1482: ret = 11482; break;
+    case 1483: ret = 11483; break;
+    case 1484: ret = 11484; break;
+    case 1485: ret = 11485; break;
+    case 1486: ret = 11486; break;
+    case 1487: ret = 11487; break;
+    case 1488: ret = 11488; break;
+    case 1489: ret = 11489; break;
+    case 1490: ret = 11490; break;
+    case 1491: ret = 11491; break;
+    case 1492: ret = 11492; break;
+    case 1493: ret = 11493; break;
+    case 1494: ret = 11494; break;
+    case 1495: ret = 11495; break;
+    case 1496: ret = 11496; break;
+    case 1497: ret = 11497; break;
+    case 1498: ret = 11498; break;
+    case 1499: ret = 11499; break;
+    case 1500: ret = 11500; break;
+    case 1501: ret = 11501; break;
+    case 1502: ret = 11502; break;
+    case 1503: ret = 11503; break;
+    case 1504: ret = 11504; break;
+    case 1505: ret = 11505; break;
+    case 1506: ret = 11506; break;
+    case 1507: ret = 11507; break;
+    case 1508: ret = 11508; break;
+    case 1509: ret = 11509; break;
+    case 1510: ret = 11510; break;
+    case 1511: ret = 11511; break;
+    case 1512: ret = 11512; break;
+    case 1513: ret = 11513; break;
+    case 1514: ret = 11514; break;
+    case 1515: ret = 11515; break;
+    case 1516: ret = 11516; break;
+    case 1517: ret = 11517; break;
+    case 1518: ret = 11518; break;
+    case 1519: ret = 11519; break;
+    case 1520: ret = 11520; break;
+    case 1521: ret = 11521; break;
+    case 1522: ret = 11522; break;
+    case 1523: ret = 11523; break;
+    case 1524: ret = 11524; break;
+    case 1525: ret = 11525; break;
+    case 1526: ret = 11526; break;
+    case 1527: ret = 11527; break;
+    case 1528: ret = 11528; break;
+    case 1529: ret = 11529; break;
+    case 1530: ret = 11530; break;
+    case 1531: ret = 11531; break;
+    case 1532: ret = 11532; break;
+    case 1533: ret = 11533; break;
+    case 1534: ret = 11534; break;
+    case 1535: ret = 11535; break;
+    case 1536: ret = 11536; break;
+    case 1537: ret = 11537; break;
+    case 1538: ret = 11538; break;
+    case 1539: ret = 11539; break;
+    case 1540: ret = 11540; break;
+    case 1541: ret = 11541; break;
+    case 1542: ret = 11542; break;
+    case 1543: ret = 11543; break;
+    case 1544: ret = 11544; break;
+    case 1545: ret = 11545; break;
+    case 1546: ret = 11546; break;
+    case 1547: ret = 11547; break;
+    case 1548: ret = 11548; break;
+    case 1549: ret = 11549; break;
+    case 1550: ret = 11550; break;
+    case 1551: ret = 11551; break;
+    case 1552: ret = 11552; break;
+    case 1553: ret = 11553; break;
+    case 1554: ret = 11554; break;
+    case 1555: ret = 11555; break;
+    case 1556: ret = 11556; break;
+    case 1557: ret = 11557; break;
+    case 1558: ret = 11558; break;
+    case 1559: ret = 11559; break;
+    case 1560: ret = 11560; break;
+    case 1561: ret = 11561; break;
+    case 1562: ret = 11562; break;
+    case 1563: ret = 11563; break;
+    case 1564: ret = 11564; break;
+    case 1565: ret = 11565; break;
+    case 1566: ret = 11566; break;
+    case 1567: ret = 11567; break;
+    case 1568: ret = 11568; break;
+    case 1569: ret = 11569; break;
+    case 1570: ret = 11570; break;
+    case 1571: ret = 11571; break;
+    case 1572: ret = 11572; break;
+    case 1573: ret = 11573; break;
+    case 1574: ret = 11574; break;
+    case 1575: ret = 11575; break;
+    case 1576: ret = 11576; break;
+    case 1577: ret = 11577; break;
+    case 1578: ret = 11578; break;
+    case 1579: ret = 11579; break;
+    case 1580: ret = 11580; break;
+    case 1581: ret = 11581; break;
+    case 1582: ret = 11582; break;
+    case 1583: ret = 11583; break;
+    case 1584: ret = 11584; break;
+    case 1585: ret = 11585; break;
+    case 1586: ret = 11586; break;
+    case 1587: ret = 11587; break;
+    case 1588: ret = 11588; break;
+    case 1589: ret = 11589; break;
+    case 1590: ret = 11590; break;
+    case 1591: ret = 11591; break;
+    case 1592: ret = 11592; break;
+    case 1593: ret = 11593; break;
+    case 1594: ret = 11594; break;
+    case 1595: ret = 11595; break;
+    case 1596: ret = 11596; break;
+    case 1597: ret = 11597; break;
+    case 1598: ret = 11598; break;
+    case 1599: ret = 11599; break;
+    case 1600: ret = 11600; break;
+    case 1601: ret = 11601; break;
+    case 1602: ret = 11602; break;
+    case 1603: ret = 11603; break;
+    case 1604: ret = 11604; break;
+    case 1605: ret = 11605; break;
+    case 1606: ret = 11606; break;
+    case 1607: ret = 11607; break;
+    case 1608: ret = 11608; break;
+    case 1609: ret = 11609; break;
+    case 1610: ret = 11610; break;
+    case 1611: ret = 11611; break;
+    case 1612: ret = 11612; break;
+    case 1613: ret = 11613; break;
+    case 1614: ret = 11614; break;
+    case 1615: ret = 11615; break;
+    case 1616: ret = 11616; break;
+    case 1617: ret = 11617; break;
+    case 1618: ret = 11618; break;
+    case 1619: ret = 11619; break;
+    case 1620: ret = 11620; break;
+    case 1621: ret = 11621; break;
+    case 1622: ret = 11622; break;
+    case 1623: ret = 11623; break;
+    case 1624: ret = 11624; break;
+    case 1625: ret = 11625; break;
+    case 1626: ret = 11626; break;
+    case 1627: ret = 11627; break;
+    case 1628: ret = 11628; break;
+    case 1629: ret = 11629; break;
+    case 1630: ret = 11630; break;
+    case 1631: ret = 11631; break;
+    case 1632: ret = 11632; break;
+    case 1633: ret = 11633; break;
+    case 1634: ret = 11634; break;
+    case 1635: ret = 11635; break;
+    case 1636: ret = 11636; break;
+    case 1637: ret = 11637; break;
+    case 1638: ret = 11638; break;
+    case 1639: ret = 11639; break;
+    case 1640: ret = 11640; break;
+    case 1641: ret = 11641; break;
+    case 1642: ret = 11642; break;
+    case 1643: ret = 11643; break;
+    case 1644: ret = 11644; break;
+    case 1645: ret = 11645; break;
+    case 1646: ret = 11646; break;
+    case 1647: ret = 11647; break;
+    case 1648: ret = 11648; break;
+    case 1649: ret = 11649; break;
+    case 1650: ret = 11650; break;
+    case 1651: ret = 11651; break;
+    case 1652: ret = 11652; break;
+    case 1653: ret = 11653; break;
+    case 1654: ret = 11654; break;
+    case 1655: ret = 11655; break;
+    case 1656: ret = 11656; break;
+    case 1657: ret = 11657; break;
+    case 1658: ret = 11658; break;
+    case 1659: ret = 11659; break;
+    case 1660: ret = 11660; break;
+    case 1661: ret = 11661; break;
+    case 1662: ret = 11662; break;
+    case 1663: ret = 11663; break;
+    case 1664: ret = 11664; break;
+    case 1665: ret = 11665; break;
+    case 1666: ret = 11666; break;
+    case 1667: ret = 11667; break;
+    case 1668: ret = 11668; break;
+    case 1669: ret = 11669; break;
+    case 1670: ret = 11670; break;
+    case 1671: ret = 11671; break;
+    case 1672: ret = 11672; break;
+    case 1673: ret = 11673; break;
+    case 1674: ret = 11674; break;
+    case 1675: ret = 11675; break;
+    case 1676: ret = 11676; break;
+    case 1677: ret = 11677; break;
+    case 1678: ret = 11678; break;
+    case 1679: ret = 11679; break;
+    case 1680: ret = 11680; break;
+    case 1681: ret = 11681; break;
+    case 1682: ret = 11682; break;
+    case 1683: ret = 11683; break;
+    case 1684: ret = 11684; break;
+    case 1685: ret = 11685; break;
+    case 1686: ret = 11686; break;
+    case 1687: ret = 11687; break;
+    case 1688: ret = 11688; break;
+    case 1689: ret = 11689; break;
+    case 1690: ret = 11690; break;
+    case 1691: ret = 11691; break;
+    case 1692: ret = 11692; break;
+    case 1693: ret = 11693; break;
+    case 1694: ret = 11694; break;
+    case 1695: ret = 11695; break;
+    case 1696: ret = 11696; break;
+    case 1697: ret = 11697; break;
+    case 1698: ret = 11698; break;
+    case 1699: ret = 11699; break;
+    case 1700: ret = 11700; break;
+    case 1701: ret = 11701; break;
+    case 1702: ret = 11702; break;
+    case 1703: ret = 11703; break;
+    case 1704: ret = 11704; break;
+    case 1705: ret = 11705; break;
+    case 1706: ret = 11706; break;
+    case 1707: ret = 11707; break;
+    case 1708: ret = 11708; break;
+    case 1709: ret = 11709; break;
+    case 1710: ret = 11710; break;
+    case 1711: ret = 11711; break;
+    case 1712: ret = 11712; break;
+    case 1713: ret = 11713; break;
+    case 1714: ret = 11714; break;
+    case 1715: ret = 11715; break;
+    case 1716: ret = 11716; break;
+    case 1717: ret = 11717; break;
+    case 1718: ret = 11718; break;
+    case 1719: ret = 11719; break;
+    case 1720: ret = 11720; break;
+    case 1721: ret = 11721; break;
+    case 1722: ret = 11722; break;
+    case 1723: ret = 11723; break;
+    case 1724: ret = 11724; break;
+    case 1725: ret = 11725; break;
+    case 1726: ret = 11726; break;
+    case 1727: ret = 11727; break;
+    case 1728: ret = 11728; break;
+    case 1729: ret = 11729; break;
+    case 1730: ret = 11730; break;
+    case 1731: ret = 11731; break;
+    case 1732: ret = 11732; break;
+    case 1733: ret = 11733; break;
+    case 1734: ret = 11734; break;
+    case 1735: ret = 11735; break;
+    case 1736: ret = 11736; break;
+    case 1737: ret = 11737; break;
+    case 1738: ret = 11738; break;
+    case 1739: ret = 11739; break;
+    case 1740: ret = 11740; break;
+    case 1741: ret = 11741; break;
+    case 1742: ret = 11742; break;
+    case 1743: ret = 11743; break;
+    case 1744: ret = 11744; break;
+    case 1745: ret = 11745; break;
+    case 1746: ret = 11746; break;
+    case 1747: ret = 11747; break;
+    case 1748: ret = 11748; break;
+    case 1749: ret = 11749; break;
+    case 1750: ret = 11750; break;
+    case 1751: ret = 11751; break;
+    case 1752: ret = 11752; break;
+    case 1753: ret = 11753; break;
+    case 1754: ret = 11754; break;
+    case 1755: ret = 11755; break;
+    case 1756: ret = 11756; break;
+    case 1757: ret = 11757; break;
+    case 1758: ret = 11758; break;
+    case 1759: ret = 11759; break;
+    case 1760: ret = 11760; break;
+    case 1761: ret = 11761; break;
+    case 1762: ret = 11762; break;
+    case 1763: ret = 11763; break;
+    case 1764: ret = 11764; break;
+    case 1765: ret = 11765; break;
+    case 1766: ret = 11766; break;
+    case 1767: ret = 11767; break;
+    case 1768: ret = 11768; break;
+    case 1769: ret = 11769; break;
+    case 1770: ret = 11770; break;
+    case 1771: ret = 11771; break;
+    case 1772: ret = 11772; break;
+    case 1773: ret = 11773; break;
+    case 1774: ret = 11774; break;
+    case 1775: ret = 11775; break;
+    case 1776: ret = 11776; break;
+    case 1777: ret = 11777; break;
+    case 1778: ret = 11778; break;
+    case 1779: ret = 11779; break;
+    case 1780: ret = 11780; break;
+    case 1781: ret = 11781; break;
+    case 1782: ret = 11782; break;
+    case 1783: ret = 11783; break;
+    case 1784: ret = 11784; break;
+    case 1785: ret = 11785; break;
+    case 1786: ret = 11786; break;
+    case 1787: ret = 11787; break;
+    case 1788: ret = 11788; break;
+    case 1789: ret = 11789; break;
+    case 1790: ret = 11790; break;
+    case 1791: ret = 11791; break;
+    case 1792: ret = 11792; break;
+    case 1793: ret = 11793; break;
+    case 1794: ret = 11794; break;
+    case 1795: ret = 11795; break;
+    case 1796: ret = 11796; break;
+    case 1797: ret = 11797; break;
+    case 1798: ret = 11798; break;
+    case 1799: ret = 11799; break;
+    case 1800: ret = 11800; break;
+    case 1801: ret = 11801; break;
+    case 1802: ret = 11802; break;
+    case 1803: ret = 11803; break;
+    case 1804: ret = 11804; break;
+    case 1805: ret = 11805; break;
+    case 1806: ret = 11806; break;
+    case 1807: ret = 11807; break;
+    case 1808: ret = 11808; break;
+    case 1809: ret = 11809; break;
+    case 1810: ret = 11810; break;
+    case 1811: ret = 11811; break;
+    case 1812: ret = 11812; break;
+    case 1813: ret = 11813; break;
+    case 1814: ret = 11814; break;
+    case 1815: ret = 11815; break;
+    case 1816: ret = 11816; break;
+    case 1817: ret = 11817; break;
+    case 1818: ret = 11818; break;
+    case 1819: ret = 11819; break;
+    case 1820: ret = 11820; break;
+    case 1821: ret = 11821; break;
+    case 1822: ret = 11822; break;
+    case 1823: ret = 11823; break;
+    case 1824: ret = 11824; break;
+    case 1825: ret = 11825; break;
+    case 1826: ret = 11826; break;
+    case 1827: ret = 11827; break;
+    case 1828: ret = 11828; break;
+    case 1829: ret = 11829; break;
+    case 1830: ret = 11830; break;
+    case 1831: ret = 11831; break;
+    case 1832: ret = 11832; break;
+    case 1833: ret = 11833; break;
+    case 1834: ret = 11834; break;
+    case 1835: ret = 11835; break;
+    case 1836: ret = 11836; break;
+    case 1837: ret = 11837; break;
+    case 1838: ret = 11838; break;
+    case 1839: ret = 11839; break;
+    case 1840: ret = 11840; break;
+    case 1841: ret = 11841; break;
+    case 1842: ret = 11842; break;
+    case 1843: ret = 11843; break;
+    case 1844: ret = 11844; break;
+    case 1845: ret = 11845; break;
+    case 1846: ret = 11846; break;
+    case 1847: ret = 11847; break;
+    case 1848: ret = 11848; break;
+    case 1849: ret = 11849; break;
+    case 1850: ret = 11850; break;
+    case 1851: ret = 11851; break;
+    case 1852: ret = 11852; break;
+    case 1853: ret = 11853; break;
+    case 1854: ret = 11854; break;
+    case 1855: ret = 11855; break;
+    case 1856: ret = 11856; break;
+    case 1857: ret = 11857; break;
+    case 1858: ret = 11858; break;
+    case 1859: ret = 11859; break;
+    case 1860: ret = 11860; break;
+    case 1861: ret = 11861; break;
+    case 1862: ret = 11862; break;
+    case 1863: ret = 11863; break;
+    case 1864: ret = 11864; break;
+    case 1865: ret = 11865; break;
+    case 1866: ret = 11866; break;
+    case 1867: ret = 11867; break;
+    case 1868: ret = 11868; break;
+    case 1869: ret = 11869; break;
+    case 1870: ret = 11870; break;
+    case 1871: ret = 11871; break;
+    case 1872: ret = 11872; break;
+    case 1873: ret = 11873; break;
+    case 1874: ret = 11874; break;
+    case 1875: ret = 11875; break;
+    case 1876: ret = 11876; break;
+    case 1877: ret = 11877; break;
+    case 1878: ret = 11878; break;
+    case 1879: ret = 11879; break;
+    case 1880: ret = 11880; break;
+    case 1881: ret = 11881; break;
+    case 1882: ret = 11882; break;
+    case 1883: ret = 11883; break;
+    case 1884: ret = 11884; break;
+    case 1885: ret = 11885; break;
+    case 1886: ret = 11886; break;
+    case 1887: ret = 11887; break;
+    case 1888: ret = 11888; break;
+    case 1889: ret = 11889; break;
+    case 1890: ret = 11890; break;
+    case 1891: ret = 11891; break;
+    case 1892: ret = 11892; break;
+    case 1893: ret = 11893; break;
+    case 1894: ret = 11894; break;
+    case 1895: ret = 11895; break;
+    case 1896: ret = 11896; break;
+    case 1897: ret = 11897; break;
+    case 1898: ret = 11898; break;
+    case 1899: ret = 11899; break;
+    case 1900: ret = 11900; break;
+    case 1901: ret = 11901; break;
+    case 1902: ret = 11902; break;
+    case 1903: ret = 11903; break;
+    case 1904: ret = 11904; break;
+    case 1905: ret = 11905; break;
+    case 1906: ret = 11906; break;
+    case 1907: ret = 11907; break;
+    case 1908: ret = 11908; break;
+    case 1909: ret = 11909; break;
+    case 1910: ret = 11910; break;
+    case 1911: ret = 11911; break;
+    case 1912: ret = 11912; break;
+    case 1913: ret = 11913; break;
+    case 1914: ret = 11914; break;
+    case 1915: ret = 11915; break;
+    case 1916: ret = 11916; break;
+    case 1917: ret = 11917; break;
+    case 1918: ret = 11918; break;
+    case 1919: ret = 11919; break;
+    case 1920: ret = 11920; break;
+    case 1921: ret = 11921; break;
+    case 1922: ret = 11922; break;
+    case 1923: ret = 11923; break;
+    case 1924: ret = 11924; break;
+    case 1925: ret = 11925; break;
+    case 1926: ret = 11926; break;
+    case 1927: ret = 11927; break;
+    case 1928: ret = 11928; break;
+    case 1929: ret = 11929; break;
+    case 1930: ret = 11930; break;
+    case 1931: ret = 11931; break;
+    case 1932: ret = 11932; break;
+    case 1933: ret = 11933; break;
+    case 1934: ret = 11934; break;
+    case 1935: ret = 11935; break;
+    case 1936: ret = 11936; break;
+    case 1937: ret = 11937; break;
+    case 1938: ret = 11938; break;
+    case 1939: ret = 11939; break;
+    case 1940: ret = 11940; break;
+    case 1941: ret = 11941; break;
+    case 1942: ret = 11942; break;
+    case 1943: ret = 11943; break;
+    case 1944: ret = 11944; break;
+    case 1945: ret = 11945; break;
+    case 1946: ret = 11946; break;
+    case 1947: ret = 11947; break;
+    case 1948: ret = 11948; break;
+    case 1949: ret = 11949; break;
+    case 1950: ret = 11950; break;
+    case 1951: ret = 11951; break;
+    case 1952: ret = 11952; break;
+    case 1953: ret = 11953; break;
+    case 1954: ret = 11954; break;
+    case 1955: ret = 11955; break;
+    case 1956: ret = 11956; break;
+    case 1957: ret = 11957; break;
+    case 1958: ret = 11958; break;
+    case 1959: ret = 11959; break;
+    case 1960: ret = 11960; break;
+    case 1961: ret = 11961; break;
+    case 1962: ret = 11962; break;
+    case 1963: ret = 11963; break;
+    case 1964: ret = 11964; break;
+    case 1965: ret = 11965; break;
+    case 1966: ret = 11966; break;
+    case 1967: ret = 11967; break;
+    case 1968: ret = 11968; break;
+    case 1969: ret = 11969; break;
+    case 1970: ret = 11970; break;
+    case 1971: ret = 11971; break;
+    case 1972: ret = 11972; break;
+    case 1973: ret = 11973; break;
+    case 1974: ret = 11974; break;
+    case 1975: ret = 11975; break;
+    case 1976: ret = 11976; break;
+    case 1977: ret = 11977; break;
+    case 1978: ret = 11978; break;
+    case 1979: ret = 11979; break;
+    case 1980: ret = 11980; break;
+    case 1981: ret = 11981; break;
+    case 1982: ret = 11982; break;
+    case 1983: ret = 11983; break;
+    case 1984: ret = 11984; break;
+    case 1985: ret = 11985; break;
+    case 1986: ret = 11986; break;
+    case 1987: ret = 11987; break;
+    case 1988: ret = 11988; break;
+    case 1989: ret = 11989; break;
+    case 1990: ret = 11990; break;
+    case 1991: ret = 11991; break;
+    case 1992: ret = 11992; break;
+    case 1993: ret = 11993; break;
+    case 1994: ret = 11994; break;
+    case 1995: ret = 11995; break;
+    case 1996: ret = 11996; break;
+    case 1997: ret = 11997; break;
+    case 1998: ret = 11998; break;
+    case 1999: ret = 11999; break;
+    case 2000: ret = 12000; break;
+    case 2001: ret = 12001; break;
+    case 2002: ret = 12002; break;
+    case 2003: ret = 12003; break;
+    case 2004: ret = 12004; break;
+    case 2005: ret = 12005; break;
+    case 2006: ret = 12006; break;
+    case 2007: ret = 12007; break;
+    case 2008: ret = 12008; break;
+    case 2009: ret = 12009; break;
+    case 2010: ret = 12010; break;
+    case 2011: ret = 12011; break;
+    case 2012: ret = 12012; break;
+    case 2013: ret = 12013; break;
+    case 2014: ret = 12014; break;
+    case 2015: ret = 12015; break;
+    case 2016: ret = 12016; break;
+    case 2017: ret = 12017; break;
+    case 2018: ret = 12018; break;
+    case 2019: ret = 12019; break;
+    case 2020: ret = 12020; break;
+    case 2021: ret = 12021; break;
+    case 2022: ret = 12022; break;
+    case 2023: ret = 12023; break;
+    case 2024: ret = 12024; break;
+    case 2025: ret = 12025; break;
+    case 2026: ret = 12026; break;
+    case 2027: ret = 12027; break;
+    case 2028: ret = 12028; break;
+    case 2029: ret = 12029; break;
+    case 2030: ret = 12030; break;
+    case 2031: ret = 12031; break;
+    case 2032: ret = 12032; break;
+    case 2033: ret = 12033; break;
+    case 2034: ret = 12034; break;
+    case 2035: ret = 12035; break;
+    case 2036: ret = 12036; break;
+    case 2037: ret = 12037; break;
+    case 2038: ret = 12038; break;
+    case 2039: ret = 12039; break;
+    case 2040: ret = 12040; break;
+    case 2041: ret = 12041; break;
+    case 2042: ret = 12042; break;
+    case 2043: ret = 12043; break;
+    case 2044: ret = 12044; break;
+    case 2045: ret = 12045; break;
+    case 2046: ret = 12046; break;
+    case 2047: ret = 12047; break;
+    case 2048: ret = 12048; break;
+    case 2049: ret = 12049; break;
+    case 2050: ret = 12050; break;
+    case 2051: ret = 12051; break;
+    case 2052: ret = 12052; break;
+    case 2053: ret = 12053; break;
+    case 2054: ret = 12054; break;
+    case 2055: ret = 12055; break;
+    case 2056: ret = 12056; break;
+    case 2057: ret = 12057; break;
+    case 2058: ret = 12058; break;
+    case 2059: ret = 12059; break;
+    case 2060: ret = 12060; break;
+    case 2061: ret = 12061; break;
+    case 2062: ret = 12062; break;
+    case 2063: ret = 12063; break;
+    case 2064: ret = 12064; break;
+    case 2065: ret = 12065; break;
+    case 2066: ret = 12066; break;
+    case 2067: ret = 12067; break;
+    case 2068: ret = 12068; break;
+    case 2069: ret = 12069; break;
+    case 2070: ret = 12070; break;
+    case 2071: ret = 12071; break;
+    case 2072: ret = 12072; break;
+    case 2073: ret = 12073; break;
+    case 2074: ret = 12074; break;
+    case 2075: ret = 12075; break;
+    case 2076: ret = 12076; break;
+    case 2077: ret = 12077; break;
+    case 2078: ret = 12078; break;
+    case 2079: ret = 12079; break;
+    case 2080: ret = 12080; break;
+    case 2081: ret = 12081; break;
+    case 2082: ret = 12082; break;
+    case 2083: ret = 12083; break;
+    case 2084: ret = 12084; break;
+    case 2085: ret = 12085; break;
+    case 2086: ret = 12086; break;
+    case 2087: ret = 12087; break;
+    case 2088: ret = 12088; break;
+    case 2089: ret = 12089; break;
+    case 2090: ret = 12090; break;
+    case 2091: ret = 12091; break;
+    case 2092: ret = 12092; break;
+    case 2093: ret = 12093; break;
+    case 2094: ret = 12094; break;
+    case 2095: ret = 12095; break;
+    case 2096: ret = 12096; break;
+    case 2097: ret = 12097; break;
+    case 2098: ret = 12098; break;
+    case 2099: ret = 12099; break;
+    case 2100: ret = 12100; break;
+    case 2101: ret = 12101; break;
+    case 2102: ret = 12102; break;
+    case 2103: ret = 12103; break;
+    case 2104: ret = 12104; break;
+    case 2105: ret = 12105; break;
+    case 2106: ret = 12106; break;
+    case 2107: ret = 12107; break;
+    case 2108: ret = 12108; break;
+    case 2109: ret = 12109; break;
+    case 2110: ret = 12110; break;
+    case 2111: ret = 12111; break;
+    case 2112: ret = 12112; break;
+    case 2113: ret = 12113; break;
+    case 2114: ret = 12114; break;
+    case 2115: ret = 12115; break;
+    case 2116: ret = 12116; break;
+    case 2117: ret = 12117; break;
+    case 2118: ret = 12118; break;
+    case 2119: ret = 12119; break;
+    case 2120: ret = 12120; break;
+    case 2121: ret = 12121; break;
+    case 2122: ret = 12122; break;
+    case 2123: ret = 12123; break;
+    case 2124: ret = 12124; break;
+    case 2125: ret = 12125; break;
+    case 2126: ret = 12126; break;
+    case 2127: ret = 12127; break;
+    case 2128: ret = 12128; break;
+    case 2129: ret = 12129; break;
+    case 2130: ret = 12130; break;
+    case 2131: ret = 12131; break;
+    case 2132: ret = 12132; break;
+    case 2133: ret = 12133; break;
+    case 2134: ret = 12134; break;
+    case 2135: ret = 12135; break;
+    case 2136: ret = 12136; break;
+    case 2137: ret = 12137; break;
+    case 2138: ret = 12138; break;
+    case 2139: ret = 12139; break;
+    case 2140: ret = 12140; break;
+    case 2141: ret = 12141; break;
+    case 2142: ret = 12142; break;
+    case 2143: ret = 12143; break;
+    case 2144: ret = 12144; break;
+    case 2145: ret = 12145; break;
+    case 2146: ret = 12146; break;
+    case 2147: ret = 12147; break;
+    case 2148: ret = 12148; break;
+    case 2149: ret = 12149; break;
+    case 2150: ret = 12150; break;
+    case 2151: ret = 12151; break;
+    case 2152: ret = 12152; break;
+    case 2153: ret = 12153; break;
+    case 2154: ret = 12154; break;
+    case 2155: ret = 12155; break;
+    case 2156: ret = 12156; break;
+    case 2157: ret = 12157; break;
+    case 2158: ret = 12158; break;
+    case 2159: ret = 12159; break;
+    case 2160: ret = 12160; break;
+    case 2161: ret = 12161; break;
+    case 2162: ret = 12162; break;
+    case 2163: ret = 12163; break;
+    case 2164: ret = 12164; break;
+    case 2165: ret = 12165; break;
+    case 2166: ret = 12166; break;
+    case 2167: ret = 12167; break;
+    case 2168: ret = 12168; break;
+    case 2169: ret = 12169; break;
+    case 2170: ret = 12170; break;
+    case 2171: ret = 12171; break;
+    case 2172: ret = 12172; break;
+    case 2173: ret = 12173; break;
+    case 2174: ret = 12174; break;
+    case 2175: ret = 12175; break;
+    case 2176: ret = 12176; break;
+    case 2177: ret = 12177; break;
+    case 2178: ret = 12178; break;
+    case 2179: ret = 12179; break;
+    case 2180: ret = 12180; break;
+    case 2181: ret = 12181; break;
+    case 2182: ret = 12182; break;
+    case 2183: ret = 12183; break;
+    case 2184: ret = 12184; break;
+    case 2185: ret = 12185; break;
+    case 2186: ret = 12186; break;
+    case 2187: ret = 12187; break;
+    case 2188: ret = 12188; break;
+    case 2189: ret = 12189; break;
+    case 2190: ret = 12190; break;
+    case 2191: ret = 12191; break;
+    case 2192: ret = 12192; break;
+    case 2193: ret = 12193; break;
+    case 2194: ret = 12194; break;
+    case 2195: ret = 12195; break;
+    case 2196: ret = 12196; break;
+    case 2197: ret = 12197; break;
+    case 2198: ret = 12198; break;
+    case 2199: ret = 12199; break;
+    case 2200: ret = 12200; break;
+    case 2201: ret = 12201; break;
+    case 2202: ret = 12202; break;
+    case 2203: ret = 12203; break;
+    case 2204: ret = 12204; break;
+    case 2205: ret = 12205; break;
+    case 2206: ret = 12206; break;
+    case 2207: ret = 12207; break;
+    case 2208: ret = 12208; break;
+    case 2209: ret = 12209; break;
+    case 2210: ret = 12210; break;
+    case 2211: ret = 12211; break;
+    case 2212: ret = 12212; break;
+    case 2213: ret = 12213; break;
+    case 2214: ret = 12214; break;
+    case 2215: ret = 12215; break;
+    case 2216: ret = 12216; break;
+    case 2217: ret = 12217; break;
+    case 2218: ret = 12218; break;
+    case 2219: ret = 12219; break;
+    case 2220: ret = 12220; break;
+    case 2221: ret = 12221; break;
+    case 2222: ret = 12222; break;
+    case 2223: ret = 12223; break;
+    case 2224: ret = 12224; break;
+    case 2225: ret = 12225; break;
+    case 2226: ret = 12226; break;
+    case 2227: ret = 12227; break;
+    case 2228: ret = 12228; break;
+    case 2229: ret = 12229; break;
+    case 2230: ret = 12230; break;
+    case 2231: ret = 12231; break;
+    case 2232: ret = 12232; break;
+    case 2233: ret = 12233; break;
+    case 2234: ret = 12234; break;
+    case 2235: ret = 12235; break;
+    case 2236: ret = 12236; break;
+    case 2237: ret = 12237; break;
+    case 2238: ret = 12238; break;
+    case 2239: ret = 12239; break;
+    case 2240: ret = 12240; break;
+    case 2241: ret = 12241; break;
+    case 2242: ret = 12242; break;
+    case 2243: ret = 12243; break;
+    case 2244: ret = 12244; break;
+    case 2245: ret = 12245; break;
+    case 2246: ret = 12246; break;
+    case 2247: ret = 12247; break;
+    case 2248: ret = 12248; break;
+    case 2249: ret = 12249; break;
+    case 2250: ret = 12250; break;
+    case 2251: ret = 12251; break;
+    case 2252: ret = 12252; break;
+    case 2253: ret = 12253; break;
+    case 2254: ret = 12254; break;
+    case 2255: ret = 12255; break;
+    case 2256: ret = 12256; break;
+    case 2257: ret = 12257; break;
+    case 2258: ret = 12258; break;
+    case 2259: ret = 12259; break;
+    case 2260: ret = 12260; break;
+    case 2261: ret = 12261; break;
+    case 2262: ret = 12262; break;
+    case 2263: ret = 12263; break;
+    case 2264: ret = 12264; break;
+    case 2265: ret = 12265; break;
+    case 2266: ret = 12266; break;
+    case 2267: ret = 12267; break;
+    case 2268: ret = 12268; break;
+    case 2269: ret = 12269; break;
+    case 2270: ret = 12270; break;
+    case 2271: ret = 12271; break;
+    case 2272: ret = 12272; break;
+    case 2273: ret = 12273; break;
+    case 2274: ret = 12274; break;
+    case 2275: ret = 12275; break;
+    case 2276: ret = 12276; break;
+    case 2277: ret = 12277; break;
+    case 2278: ret = 12278; break;
+    case 2279: ret = 12279; break;
+    case 2280: ret = 12280; break;
+    case 2281: ret = 12281; break;
+    case 2282: ret = 12282; break;
+    case 2283: ret = 12283; break;
+    case 2284: ret = 12284; break;
+    case 2285: ret = 12285; break;
+    case 2286: ret = 12286; break;
+    case 2287: ret = 12287; break;
+    case 2288: ret = 12288; break;
+    case 2289: ret = 12289; break;
+    case 2290: ret = 12290; break;
+    case 2291: ret = 12291; break;
+    case 2292: ret = 12292; break;
+    case 2293: ret = 12293; break;
+    case 2294: ret = 12294; break;
+    case 2295: ret = 12295; break;
+    case 2296: ret = 12296; break;
+    case 2297: ret = 12297; break;
+    case 2298: ret = 12298; break;
+    case 2299: ret = 12299; break;
+    case 2300: ret = 12300; break;
+    case 2301: ret = 12301; break;
+    case 2302: ret = 12302; break;
+    case 2303: ret = 12303; break;
+    case 2304: ret = 12304; break;
+    case 2305: ret = 12305; break;
+    case 2306: ret = 12306; break;
+    case 2307: ret = 12307; break;
+    case 2308: ret = 12308; break;
+    case 2309: ret = 12309; break;
+    case 2310: ret = 12310; break;
+    case 2311: ret = 12311; break;
+    case 2312: ret = 12312; break;
+    case 2313: ret = 12313; break;
+    case 2314: ret = 12314; break;
+    case 2315: ret = 12315; break;
+    case 2316: ret = 12316; break;
+    case 2317: ret = 12317; break;
+    case 2318: ret = 12318; break;
+    case 2319: ret = 12319; break;
+    case 2320: ret = 12320; break;
+    case 2321: ret = 12321; break;
+    case 2322: ret = 12322; break;
+    case 2323: ret = 12323; break;
+    case 2324: ret = 12324; break;
+    case 2325: ret = 12325; break;
+    case 2326: ret = 12326; break;
+    case 2327: ret = 12327; break;
+    case 2328: ret = 12328; break;
+    case 2329: ret = 12329; break;
+    case 2330: ret = 12330; break;
+    case 2331: ret = 12331; break;
+    case 2332: ret = 12332; break;
+    case 2333: ret = 12333; break;
+    case 2334: ret = 12334; break;
+    case 2335: ret = 12335; break;
+    case 2336: ret = 12336; break;
+    case 2337: ret = 12337; break;
+    case 2338: ret = 12338; break;
+    case 2339: ret = 12339; break;
+    case 2340: ret = 12340; break;
+    case 2341: ret = 12341; break;
+    case 2342: ret = 12342; break;
+    case 2343: ret = 12343; break;
+    case 2344: ret = 12344; break;
+    case 2345: ret = 12345; break;
+    case 2346: ret = 12346; break;
+    case 2347: ret = 12347; break;
+    case 2348: ret = 12348; break;
+    case 2349: ret = 12349; break;
+    case 2350: ret = 12350; break;
+    case 2351: ret = 12351; break;
+    case 2352: ret = 12352; break;
+    case 2353: ret = 12353; break;
+    case 2354: ret = 12354; break;
+    case 2355: ret = 12355; break;
+    case 2356: ret = 12356; break;
+    case 2357: ret = 12357; break;
+    case 2358: ret = 12358; break;
+    case 2359: ret = 12359; break;
+    case 2360: ret = 12360; break;
+    case 2361: ret = 12361; break;
+    case 2362: ret = 12362; break;
+    case 2363: ret = 12363; break;
+    case 2364: ret = 12364; break;
+    case 2365: ret = 12365; break;
+    case 2366: ret = 12366; break;
+    case 2367: ret = 12367; break;
+    case 2368: ret = 12368; break;
+    case 2369: ret = 12369; break;
+    case 2370: ret = 12370; break;
+    case 2371: ret = 12371; break;
+    case 2372: ret = 12372; break;
+    case 2373: ret = 12373; break;
+    case 2374: ret = 12374; break;
+    case 2375: ret = 12375; break;
+    case 2376: ret = 12376; break;
+    case 2377: ret = 12377; break;
+    case 2378: ret = 12378; break;
+    case 2379: ret = 12379; break;
+    case 2380: ret = 12380; break;
+    case 2381: ret = 12381; break;
+    case 2382: ret = 12382; break;
+    case 2383: ret = 12383; break;
+    case 2384: ret = 12384; break;
+    case 2385: ret = 12385; break;
+    case 2386: ret = 12386; break;
+    case 2387: ret = 12387; break;
+    case 2388: ret = 12388; break;
+    case 2389: ret = 12389; break;
+    case 2390: ret = 12390; break;
+    case 2391: ret = 12391; break;
+    case 2392: ret = 12392; break;
+    case 2393: ret = 12393; break;
+    case 2394: ret = 12394; break;
+    case 2395: ret = 12395; break;
+    case 2396: ret = 12396; break;
+    case 2397: ret = 12397; break;
+    case 2398: ret = 12398; break;
+    case 2399: ret = 12399; break;
+    case 2400: ret = 12400; break;
+    case 2401: ret = 12401; break;
+    case 2402: ret = 12402; break;
+    case 2403: ret = 12403; break;
+    case 2404: ret = 12404; break;
+    case 2405: ret = 12405; break;
+    case 2406: ret = 12406; break;
+    case 2407: ret = 12407; break;
+    case 2408: ret = 12408; break;
+    case 2409: ret = 12409; break;
+    case 2410: ret = 12410; break;
+    case 2411: ret = 12411; break;
+    case 2412: ret = 12412; break;
+    case 2413: ret = 12413; break;
+    case 2414: ret = 12414; break;
+    case 2415: ret = 12415; break;
+    case 2416: ret = 12416; break;
+    case 2417: ret = 12417; break;
+    case 2418: ret = 12418; break;
+    case 2419: ret = 12419; break;
+    case 2420: ret = 12420; break;
+    case 2421: ret = 12421; break;
+    case 2422: ret = 12422; break;
+    case 2423: ret = 12423; break;
+    case 2424: ret = 12424; break;
+    case 2425: ret = 12425; break;
+    case 2426: ret = 12426; break;
+    case 2427: ret = 12427; break;
+    case 2428: ret = 12428; break;
+    case 2429: ret = 12429; break;
+    case 2430: ret = 12430; break;
+    case 2431: ret = 12431; break;
+    case 2432: ret = 12432; break;
+    case 2433: ret = 12433; break;
+    case 2434: ret = 12434; break;
+    case 2435: ret = 12435; break;
+    case 2436: ret = 12436; break;
+    case 2437: ret = 12437; break;
+    case 2438: ret = 12438; break;
+    case 2439: ret = 12439; break;
+    case 2440: ret = 12440; break;
+    case 2441: ret = 12441; break;
+    case 2442: ret = 12442; break;
+    case 2443: ret = 12443; break;
+    case 2444: ret = 12444; break;
+    case 2445: ret = 12445; break;
+    case 2446: ret = 12446; break;
+    case 2447: ret = 12447; break;
+    case 2448: ret = 12448; break;
+    case 2449: ret = 12449; break;
+    case 2450: ret = 12450; break;
+    case 2451: ret = 12451; break;
+    case 2452: ret = 12452; break;
+    case 2453: ret = 12453; break;
+    case 2454: ret = 12454; break;
+    case 2455: ret = 12455; break;
+    case 2456: ret = 12456; break;
+    case 2457: ret = 12457; break;
+    case 2458: ret = 12458; break;
+    case 2459: ret = 12459; break;
+    case 2460: ret = 12460; break;
+    case 2461: ret = 12461; break;
+    case 2462: ret = 12462; break;
+    case 2463: ret = 12463; break;
+    case 2464: ret = 12464; break;
+    case 2465: ret = 12465; break;
+    case 2466: ret = 12466; break;
+    case 2467: ret = 12467; break;
+    case 2468: ret = 12468; break;
+    case 2469: ret = 12469; break;
+    case 2470: ret = 12470; break;
+    case 2471: ret = 12471; break;
+    case 2472: ret = 12472; break;
+    case 2473: ret = 12473; break;
+    case 2474: ret = 12474; break;
+    case 2475: ret = 12475; break;
+    case 2476: ret = 12476; break;
+    case 2477: ret = 12477; break;
+    case 2478: ret = 12478; break;
+    case 2479: ret = 12479; break;
+    case 2480: ret = 12480; break;
+    case 2481: ret = 12481; break;
+    case 2482: ret = 12482; break;
+    case 2483: ret = 12483; break;
+    case 2484: ret = 12484; break;
+    case 2485: ret = 12485; break;
+    case 2486: ret = 12486; break;
+    case 2487: ret = 12487; break;
+    case 2488: ret = 12488; break;
+    case 2489: ret = 12489; break;
+    case 2490: ret = 12490; break;
+    case 2491: ret = 12491; break;
+    case 2492: ret = 12492; break;
+    case 2493: ret = 12493; break;
+    case 2494: ret = 12494; break;
+    case 2495: ret = 12495; break;
+    case 2496: ret = 12496; break;
+    case 2497: ret = 12497; break;
+    case 2498: ret = 12498; break;
+    case 2499: ret = 12499; break;
+    case 2500: ret = 12500; break;
+    case 2501: ret = 12501; break;
+    case 2502: ret = 12502; break;
+    case 2503: ret = 12503; break;
+    case 2504: ret = 12504; break;
+    case 2505: ret = 12505; break;
+    case 2506: ret = 12506; break;
+    case 2507: ret = 12507; break;
+    case 2508: ret = 12508; break;
+    case 2509: ret = 12509; break;
+    case 2510: ret = 12510; break;
+    case 2511: ret = 12511; break;
+    case 2512: ret = 12512; break;
+    case 2513: ret = 12513; break;
+    case 2514: ret = 12514; break;
+    case 2515: ret = 12515; break;
+    case 2516: ret = 12516; break;
+    case 2517: ret = 12517; break;
+    case 2518: ret = 12518; break;
+    case 2519: ret = 12519; break;
+    case 2520: ret = 12520; break;
+    case 2521: ret = 12521; break;
+    case 2522: ret = 12522; break;
+    case 2523: ret = 12523; break;
+    case 2524: ret = 12524; break;
+    case 2525: ret = 12525; break;
+    case 2526: ret = 12526; break;
+    case 2527: ret = 12527; break;
+    case 2528: ret = 12528; break;
+    case 2529: ret = 12529; break;
+    case 2530: ret = 12530; break;
+    case 2531: ret = 12531; break;
+    case 2532: ret = 12532; break;
+    case 2533: ret = 12533; break;
+    case 2534: ret = 12534; break;
+    case 2535: ret = 12535; break;
+    case 2536: ret = 12536; break;
+    case 2537: ret = 12537; break;
+    case 2538: ret = 12538; break;
+    case 2539: ret = 12539; break;
+    case 2540: ret = 12540; break;
+    case 2541: ret = 12541; break;
+    case 2542: ret = 12542; break;
+    case 2543: ret = 12543; break;
+    case 2544: ret = 12544; break;
+    case 2545: ret = 12545; break;
+    case 2546: ret = 12546; break;
+    case 2547: ret = 12547; break;
+    case 2548: ret = 12548; break;
+    case 2549: ret = 12549; break;
+    case 2550: ret = 12550; break;
+    case 2551: ret = 12551; break;
+    case 2552: ret = 12552; break;
+    case 2553: ret = 12553; break;
+    case 2554: ret = 12554; break;
+    case 2555: ret = 12555; break;
+    case 2556: ret = 12556; break;
+    case 2557: ret = 12557; break;
+    case 2558: ret = 12558; break;
+    case 2559: ret = 12559; break;
+    case 2560: ret = 12560; break;
+    case 2561: ret = 12561; break;
+    case 2562: ret = 12562; break;
+    case 2563: ret = 12563; break;
+    case 2564: ret = 12564; break;
+    case 2565: ret = 12565; break;
+    case 2566: ret = 12566; break;
+    case 2567: ret = 12567; break;
+    case 2568: ret = 12568; break;
+    case 2569: ret = 12569; break;
+    case 2570: ret = 12570; break;
+    case 2571: ret = 12571; break;
+    case 2572: ret = 12572; break;
+    case 2573: ret = 12573; break;
+    case 2574: ret = 12574; break;
+    case 2575: ret = 12575; break;
+    case 2576: ret = 12576; break;
+    case 2577: ret = 12577; break;
+    case 2578: ret = 12578; break;
+    case 2579: ret = 12579; break;
+    case 2580: ret = 12580; break;
+    case 2581: ret = 12581; break;
+    case 2582: ret = 12582; break;
+    case 2583: ret = 12583; break;
+    case 2584: ret = 12584; break;
+    case 2585: ret = 12585; break;
+    case 2586: ret = 12586; break;
+    case 2587: ret = 12587; break;
+    case 2588: ret = 12588; break;
+    case 2589: ret = 12589; break;
+    case 2590: ret = 12590; break;
+    case 2591: ret = 12591; break;
+    case 2592: ret = 12592; break;
+    case 2593: ret = 12593; break;
+    case 2594: ret = 12594; break;
+    case 2595: ret = 12595; break;
+    case 2596: ret = 12596; break;
+    case 2597: ret = 12597; break;
+    case 2598: ret = 12598; break;
+    case 2599: ret = 12599; break;
+    case 2600: ret = 12600; break;
+    case 2601: ret = 12601; break;
+    case 2602: ret = 12602; break;
+    case 2603: ret = 12603; break;
+    case 2604: ret = 12604; break;
+    case 2605: ret = 12605; break;
+    case 2606: ret = 12606; break;
+    case 2607: ret = 12607; break;
+    case 2608: ret = 12608; break;
+    case 2609: ret = 12609; break;
+    case 2610: ret = 12610; break;
+    case 2611: ret = 12611; break;
+    case 2612: ret = 12612; break;
+    case 2613: ret = 12613; break;
+    case 2614: ret = 12614; break;
+    case 2615: ret = 12615; break;
+    case 2616: ret = 12616; break;
+    case 2617: ret = 12617; break;
+    case 2618: ret = 12618; break;
+    case 2619: ret = 12619; break;
+    case 2620: ret = 12620; break;
+    case 2621: ret = 12621; break;
+    case 2622: ret = 12622; break;
+    case 2623: ret = 12623; break;
+    case 2624: ret = 12624; break;
+    case 2625: ret = 12625; break;
+    case 2626: ret = 12626; break;
+    case 2627: ret = 12627; break;
+    case 2628: ret = 12628; break;
+    case 2629: ret = 12629; break;
+    case 2630: ret = 12630; break;
+    case 2631: ret = 12631; break;
+    case 2632: ret = 12632; break;
+    case 2633: ret = 12633; break;
+    case 2634: ret = 12634; break;
+    case 2635: ret = 12635; break;
+    case 2636: ret = 12636; break;
+    case 2637: ret = 12637; break;
+    case 2638: ret = 12638; break;
+    case 2639: ret = 12639; break;
+    case 2640: ret = 12640; break;
+    case 2641: ret = 12641; break;
+    case 2642: ret = 12642; break;
+    case 2643: ret = 12643; break;
+    case 2644: ret = 12644; break;
+    case 2645: ret = 12645; break;
+    case 2646: ret = 12646; break;
+    case 2647: ret = 12647; break;
+    case 2648: ret = 12648; break;
+    case 2649: ret = 12649; break;
+    case 2650: ret = 12650; break;
+    case 2651: ret = 12651; break;
+    case 2652: ret = 12652; break;
+    case 2653: ret = 12653; break;
+    case 2654: ret = 12654; break;
+    case 2655: ret = 12655; break;
+    case 2656: ret = 12656; break;
+    case 2657: ret = 12657; break;
+    case 2658: ret = 12658; break;
+    case 2659: ret = 12659; break;
+    case 2660: ret = 12660; break;
+    case 2661: ret = 12661; break;
+    case 2662: ret = 12662; break;
+    case 2663: ret = 12663; break;
+    case 2664: ret = 12664; break;
+    case 2665: ret = 12665; break;
+    case 2666: ret = 12666; break;
+    case 2667: ret = 12667; break;
+    case 2668: ret = 12668; break;
+    case 2669: ret = 12669; break;
+    case 2670: ret = 12670; break;
+    case 2671: ret = 12671; break;
+    case 2672: ret = 12672; break;
+    case 2673: ret = 12673; break;
+    case 2674: ret = 12674; break;
+    case 2675: ret = 12675; break;
+    case 2676: ret = 12676; break;
+    case 2677: ret = 12677; break;
+    case 2678: ret = 12678; break;
+    case 2679: ret = 12679; break;
+    case 2680: ret = 12680; break;
+    case 2681: ret = 12681; break;
+    case 2682: ret = 12682; break;
+    case 2683: ret = 12683; break;
+    case 2684: ret = 12684; break;
+    case 2685: ret = 12685; break;
+    case 2686: ret = 12686; break;
+    case 2687: ret = 12687; break;
+    case 2688: ret = 12688; break;
+    case 2689: ret = 12689; break;
+    case 2690: ret = 12690; break;
+    case 2691: ret = 12691; break;
+    case 2692: ret = 12692; break;
+    case 2693: ret = 12693; break;
+    case 2694: ret = 12694; break;
+    case 2695: ret = 12695; break;
+    case 2696: ret = 12696; break;
+    case 2697: ret = 12697; break;
+    case 2698: ret = 12698; break;
+    case 2699: ret = 12699; break;
+    case 2700: ret = 12700; break;
+    case 2701: ret = 12701; break;
+    case 2702: ret = 12702; break;
+    case 2703: ret = 12703; break;
+    case 2704: ret = 12704; break;
+    case 2705: ret = 12705; break;
+    case 2706: ret = 12706; break;
+    case 2707: ret = 12707; break;
+    case 2708: ret = 12708; break;
+    case 2709: ret = 12709; break;
+    case 2710: ret = 12710; break;
+    case 2711: ret = 12711; break;
+    case 2712: ret = 12712; break;
+    case 2713: ret = 12713; break;
+    case 2714: ret = 12714; break;
+    case 2715: ret = 12715; break;
+    case 2716: ret = 12716; break;
+    case 2717: ret = 12717; break;
+    case 2718: ret = 12718; break;
+    case 2719: ret = 12719; break;
+    case 2720: ret = 12720; break;
+    case 2721: ret = 12721; break;
+    case 2722: ret = 12722; break;
+    case 2723: ret = 12723; break;
+    case 2724: ret = 12724; break;
+    case 2725: ret = 12725; break;
+    case 2726: ret = 12726; break;
+    case 2727: ret = 12727; break;
+    case 2728: ret = 12728; break;
+    case 2729: ret = 12729; break;
+    case 2730: ret = 12730; break;
+    case 2731: ret = 12731; break;
+    case 2732: ret = 12732; break;
+    case 2733: ret = 12733; break;
+    case 2734: ret = 12734; break;
+    case 2735: ret = 12735; break;
+    case 2736: ret = 12736; break;
+    case 2737: ret = 12737; break;
+    case 2738: ret = 12738; break;
+    case 2739: ret = 12739; break;
+    case 2740: ret = 12740; break;
+    case 2741: ret = 12741; break;
+    case 2742: ret = 12742; break;
+    case 2743: ret = 12743; break;
+    case 2744: ret = 12744; break;
+    case 2745: ret = 12745; break;
+    case 2746: ret = 12746; break;
+    case 2747: ret = 12747; break;
+    case 2748: ret = 12748; break;
+    case 2749: ret = 12749; break;
+    case 2750: ret = 12750; break;
+    case 2751: ret = 12751; break;
+    case 2752: ret = 12752; break;
+    case 2753: ret = 12753; break;
+    case 2754: ret = 12754; break;
+    case 2755: ret = 12755; break;
+    case 2756: ret = 12756; break;
+    case 2757: ret = 12757; break;
+    case 2758: ret = 12758; break;
+    case 2759: ret = 12759; break;
+    case 2760: ret = 12760; break;
+    case 2761: ret = 12761; break;
+    case 2762: ret = 12762; break;
+    case 2763: ret = 12763; break;
+    case 2764: ret = 12764; break;
+    case 2765: ret = 12765; break;
+    case 2766: ret = 12766; break;
+    case 2767: ret = 12767; break;
+    case 2768: ret = 12768; break;
+    case 2769: ret = 12769; break;
+    case 2770: ret = 12770; break;
+    case 2771: ret = 12771; break;
+    case 2772: ret = 12772; break;
+    case 2773: ret = 12773; break;
+    case 2774: ret = 12774; break;
+    case 2775: ret = 12775; break;
+    case 2776: ret = 12776; break;
+    case 2777: ret = 12777; break;
+    case 2778: ret = 12778; break;
+    case 2779: ret = 12779; break;
+    case 2780: ret = 12780; break;
+    case 2781: ret = 12781; break;
+    case 2782: ret = 12782; break;
+    case 2783: ret = 12783; break;
+    case 2784: ret = 12784; break;
+    case 2785: ret = 12785; break;
+    case 2786: ret = 12786; break;
+    case 2787: ret = 12787; break;
+    case 2788: ret = 12788; break;
+    case 2789: ret = 12789; break;
+    case 2790: ret = 12790; break;
+    case 2791: ret = 12791; break;
+    case 2792: ret = 12792; break;
+    case 2793: ret = 12793; break;
+    case 2794: ret = 12794; break;
+    case 2795: ret = 12795; break;
+    case 2796: ret = 12796; break;
+    case 2797: ret = 12797; break;
+    case 2798: ret = 12798; break;
+    case 2799: ret = 12799; break;
+    case 2800: ret = 12800; break;
+    case 2801: ret = 12801; break;
+    case 2802: ret = 12802; break;
+    case 2803: ret = 12803; break;
+    case 2804: ret = 12804; break;
+    case 2805: ret = 12805; break;
+    case 2806: ret = 12806; break;
+    case 2807: ret = 12807; break;
+    case 2808: ret = 12808; break;
+    case 2809: ret = 12809; break;
+    case 2810: ret = 12810; break;
+    case 2811: ret = 12811; break;
+    case 2812: ret = 12812; break;
+    case 2813: ret = 12813; break;
+    case 2814: ret = 12814; break;
+    case 2815: ret = 12815; break;
+    case 2816: ret = 12816; break;
+    case 2817: ret = 12817; break;
+    case 2818: ret = 12818; break;
+    case 2819: ret = 12819; break;
+    case 2820: ret = 12820; break;
+    case 2821: ret = 12821; break;
+    case 2822: ret = 12822; break;
+    case 2823: ret = 12823; break;
+    case 2824: ret = 12824; break;
+    case 2825: ret = 12825; break;
+    case 2826: ret = 12826; break;
+    case 2827: ret = 12827; break;
+    case 2828: ret = 12828; break;
+    case 2829: ret = 12829; break;
+    case 2830: ret = 12830; break;
+    case 2831: ret = 12831; break;
+    case 2832: ret = 12832; break;
+    case 2833: ret = 12833; break;
+    case 2834: ret = 12834; break;
+    case 2835: ret = 12835; break;
+    case 2836: ret = 12836; break;
+    case 2837: ret = 12837; break;
+    case 2838: ret = 12838; break;
+    case 2839: ret = 12839; break;
+    case 2840: ret = 12840; break;
+    case 2841: ret = 12841; break;
+    case 2842: ret = 12842; break;
+    case 2843: ret = 12843; break;
+    case 2844: ret = 12844; break;
+    case 2845: ret = 12845; break;
+    case 2846: ret = 12846; break;
+    case 2847: ret = 12847; break;
+    case 2848: ret = 12848; break;
+    case 2849: ret = 12849; break;
+    case 2850: ret = 12850; break;
+    case 2851: ret = 12851; break;
+    case 2852: ret = 12852; break;
+    case 2853: ret = 12853; break;
+    case 2854: ret = 12854; break;
+    case 2855: ret = 12855; break;
+    case 2856: ret = 12856; break;
+    case 2857: ret = 12857; break;
+    case 2858: ret = 12858; break;
+    case 2859: ret = 12859; break;
+    case 2860: ret = 12860; break;
+    case 2861: ret = 12861; break;
+    case 2862: ret = 12862; break;
+    case 2863: ret = 12863; break;
+    case 2864: ret = 12864; break;
+    case 2865: ret = 12865; break;
+    case 2866: ret = 12866; break;
+    case 2867: ret = 12867; break;
+    case 2868: ret = 12868; break;
+    case 2869: ret = 12869; break;
+    case 2870: ret = 12870; break;
+    case 2871: ret = 12871; break;
+    case 2872: ret = 12872; break;
+    case 2873: ret = 12873; break;
+    case 2874: ret = 12874; break;
+    case 2875: ret = 12875; break;
+    case 2876: ret = 12876; break;
+    case 2877: ret = 12877; break;
+    case 2878: ret = 12878; break;
+    case 2879: ret = 12879; break;
+    case 2880: ret = 12880; break;
+    case 2881: ret = 12881; break;
+    case 2882: ret = 12882; break;
+    case 2883: ret = 12883; break;
+    case 2884: ret = 12884; break;
+    case 2885: ret = 12885; break;
+    case 2886: ret = 12886; break;
+    case 2887: ret = 12887; break;
+    case 2888: ret = 12888; break;
+    case 2889: ret = 12889; break;
+    case 2890: ret = 12890; break;
+    case 2891: ret = 12891; break;
+    case 2892: ret = 12892; break;
+    case 2893: ret = 12893; break;
+    case 2894: ret = 12894; break;
+    case 2895: ret = 12895; break;
+    case 2896: ret = 12896; break;
+    case 2897: ret = 12897; break;
+    case 2898: ret = 12898; break;
+    case 2899: ret = 12899; break;
+    case 2900: ret = 12900; break;
+    case 2901: ret = 12901; break;
+    case 2902: ret = 12902; break;
+    case 2903: ret = 12903; break;
+    case 2904: ret = 12904; break;
+    case 2905: ret = 12905; break;
+    case 2906: ret = 12906; break;
+    case 2907: ret = 12907; break;
+    case 2908: ret = 12908; break;
+    case 2909: ret = 12909; break;
+    case 2910: ret = 12910; break;
+    case 2911: ret = 12911; break;
+    case 2912: ret = 12912; break;
+    case 2913: ret = 12913; break;
+    case 2914: ret = 12914; break;
+    case 2915: ret = 12915; break;
+    case 2916: ret = 12916; break;
+    case 2917: ret = 12917; break;
+    case 2918: ret = 12918; break;
+    case 2919: ret = 12919; break;
+    case 2920: ret = 12920; break;
+    case 2921: ret = 12921; break;
+    case 2922: ret = 12922; break;
+    case 2923: ret = 12923; break;
+    case 2924: ret = 12924; break;
+    case 2925: ret = 12925; break;
+    case 2926: ret = 12926; break;
+    case 2927: ret = 12927; break;
+    case 2928: ret = 12928; break;
+    case 2929: ret = 12929; break;
+    case 2930: ret = 12930; break;
+    case 2931: ret = 12931; break;
+    case 2932: ret = 12932; break;
+    case 2933: ret = 12933; break;
+    case 2934: ret = 12934; break;
+    case 2935: ret = 12935; break;
+    case 2936: ret = 12936; break;
+    case 2937: ret = 12937; break;
+    case 2938: ret = 12938; break;
+    case 2939: ret = 12939; break;
+    case 2940: ret = 12940; break;
+    case 2941: ret = 12941; break;
+    case 2942: ret = 12942; break;
+    case 2943: ret = 12943; break;
+    case 2944: ret = 12944; break;
+    case 2945: ret = 12945; break;
+    case 2946: ret = 12946; break;
+    case 2947: ret = 12947; break;
+    case 2948: ret = 12948; break;
+    case 2949: ret = 12949; break;
+    case 2950: ret = 12950; break;
+    case 2951: ret = 12951; break;
+    case 2952: ret = 12952; break;
+    case 2953: ret = 12953; break;
+    case 2954: ret = 12954; break;
+    case 2955: ret = 12955; break;
+    case 2956: ret = 12956; break;
+    case 2957: ret = 12957; break;
+    case 2958: ret = 12958; break;
+    case 2959: ret = 12959; break;
+    case 2960: ret = 12960; break;
+    case 2961: ret = 12961; break;
+    case 2962: ret = 12962; break;
+    case 2963: ret = 12963; break;
+    case 2964: ret = 12964; break;
+    case 2965: ret = 12965; break;
+    case 2966: ret = 12966; break;
+    case 2967: ret = 12967; break;
+    case 2968: ret = 12968; break;
+    case 2969: ret = 12969; break;
+    case 2970: ret = 12970; break;
+    case 2971: ret = 12971; break;
+    case 2972: ret = 12972; break;
+    case 2973: ret = 12973; break;
+    case 2974: ret = 12974; break;
+    case 2975: ret = 12975; break;
+    case 2976: ret = 12976; break;
+    case 2977: ret = 12977; break;
+    case 2978: ret = 12978; break;
+    case 2979: ret = 12979; break;
+    case 2980: ret = 12980; break;
+    case 2981: ret = 12981; break;
+    case 2982: ret = 12982; break;
+    case 2983: ret = 12983; break;
+    case 2984: ret = 12984; break;
+    case 2985: ret = 12985; break;
+    case 2986: ret = 12986; break;
+    case 2987: ret = 12987; break;
+    case 2988: ret = 12988; break;
+    case 2989: ret = 12989; break;
+    case 2990: ret = 12990; break;
+    case 2991: ret = 12991; break;
+    case 2992: ret = 12992; break;
+    case 2993: ret = 12993; break;
+    case 2994: ret = 12994; break;
+    case 2995: ret = 12995; break;
+    case 2996: ret = 12996; break;
+    case 2997: ret = 12997; break;
+    case 2998: ret = 12998; break;
+    case 2999: ret = 12999; break;
+    case 3000: ret = 13000; break;
+    case 3001: ret = 13001; break;
+    case 3002: ret = 13002; break;
+    case 3003: ret = 13003; break;
+    case 3004: ret = 13004; break;
+    case 3005: ret = 13005; break;
+    case 3006: ret = 13006; break;
+    case 3007: ret = 13007; break;
+    case 3008: ret = 13008; break;
+    case 3009: ret = 13009; break;
+    case 3010: ret = 13010; break;
+    case 3011: ret = 13011; break;
+    case 3012: ret = 13012; break;
+    case 3013: ret = 13013; break;
+    case 3014: ret = 13014; break;
+    case 3015: ret = 13015; break;
+    case 3016: ret = 13016; break;
+    case 3017: ret = 13017; break;
+    case 3018: ret = 13018; break;
+    case 3019: ret = 13019; break;
+    case 3020: ret = 13020; break;
+    case 3021: ret = 13021; break;
+    case 3022: ret = 13022; break;
+    case 3023: ret = 13023; break;
+    case 3024: ret = 13024; break;
+    case 3025: ret = 13025; break;
+    case 3026: ret = 13026; break;
+    case 3027: ret = 13027; break;
+    case 3028: ret = 13028; break;
+    case 3029: ret = 13029; break;
+    case 3030: ret = 13030; break;
+    case 3031: ret = 13031; break;
+    case 3032: ret = 13032; break;
+    case 3033: ret = 13033; break;
+    case 3034: ret = 13034; break;
+    case 3035: ret = 13035; break;
+    case 3036: ret = 13036; break;
+    case 3037: ret = 13037; break;
+    case 3038: ret = 13038; break;
+    case 3039: ret = 13039; break;
+    case 3040: ret = 13040; break;
+    case 3041: ret = 13041; break;
+    case 3042: ret = 13042; break;
+    case 3043: ret = 13043; break;
+    case 3044: ret = 13044; break;
+    case 3045: ret = 13045; break;
+    case 3046: ret = 13046; break;
+    case 3047: ret = 13047; break;
+    case 3048: ret = 13048; break;
+    case 3049: ret = 13049; break;
+    case 3050: ret = 13050; break;
+    case 3051: ret = 13051; break;
+    case 3052: ret = 13052; break;
+    case 3053: ret = 13053; break;
+    case 3054: ret = 13054; break;
+    case 3055: ret = 13055; break;
+    case 3056: ret = 13056; break;
+    case 3057: ret = 13057; break;
+    case 3058: ret = 13058; break;
+    case 3059: ret = 13059; break;
+    case 3060: ret = 13060; break;
+    case 3061: ret = 13061; break;
+    case 3062: ret = 13062; break;
+    case 3063: ret = 13063; break;
+    case 3064: ret = 13064; break;
+    case 3065: ret = 13065; break;
+    case 3066: ret = 13066; break;
+    case 3067: ret = 13067; break;
+    case 3068: ret = 13068; break;
+    case 3069: ret = 13069; break;
+    case 3070: ret = 13070; break;
+    case 3071: ret = 13071; break;
+    case 3072: ret = 13072; break;
+    case 3073: ret = 13073; break;
+    case 3074: ret = 13074; break;
+    case 3075: ret = 13075; break;
+    case 3076: ret = 13076; break;
+    case 3077: ret = 13077; break;
+    case 3078: ret = 13078; break;
+    case 3079: ret = 13079; break;
+    case 3080: ret = 13080; break;
+    case 3081: ret = 13081; break;
+    case 3082: ret = 13082; break;
+    case 3083: ret = 13083; break;
+    case 3084: ret = 13084; break;
+    case 3085: ret = 13085; break;
+    case 3086: ret = 13086; break;
+    case 3087: ret = 13087; break;
+    case 3088: ret = 13088; break;
+    case 3089: ret = 13089; break;
+    case 3090: ret = 13090; break;
+    case 3091: ret = 13091; break;
+    case 3092: ret = 13092; break;
+    case 3093: ret = 13093; break;
+    case 3094: ret = 13094; break;
+    case 3095: ret = 13095; break;
+    case 3096: ret = 13096; break;
+    case 3097: ret = 13097; break;
+    case 3098: ret = 13098; break;
+    case 3099: ret = 13099; break;
+    case 3100: ret = 13100; break;
+    case 3101: ret = 13101; break;
+    case 3102: ret = 13102; break;
+    case 3103: ret = 13103; break;
+    case 3104: ret = 13104; break;
+    case 3105: ret = 13105; break;
+    case 3106: ret = 13106; break;
+    case 3107: ret = 13107; break;
+    case 3108: ret = 13108; break;
+    case 3109: ret = 13109; break;
+    case 3110: ret = 13110; break;
+    case 3111: ret = 13111; break;
+    case 3112: ret = 13112; break;
+    case 3113: ret = 13113; break;
+    case 3114: ret = 13114; break;
+    case 3115: ret = 13115; break;
+    case 3116: ret = 13116; break;
+    case 3117: ret = 13117; break;
+    case 3118: ret = 13118; break;
+    case 3119: ret = 13119; break;
+    case 3120: ret = 13120; break;
+    case 3121: ret = 13121; break;
+    case 3122: ret = 13122; break;
+    case 3123: ret = 13123; break;
+    case 3124: ret = 13124; break;
+    case 3125: ret = 13125; break;
+    case 3126: ret = 13126; break;
+    case 3127: ret = 13127; break;
+    case 3128: ret = 13128; break;
+    case 3129: ret = 13129; break;
+    case 3130: ret = 13130; break;
+    case 3131: ret = 13131; break;
+    case 3132: ret = 13132; break;
+    case 3133: ret = 13133; break;
+    case 3134: ret = 13134; break;
+    case 3135: ret = 13135; break;
+    case 3136: ret = 13136; break;
+    case 3137: ret = 13137; break;
+    case 3138: ret = 13138; break;
+    case 3139: ret = 13139; break;
+    case 3140: ret = 13140; break;
+    case 3141: ret = 13141; break;
+    case 3142: ret = 13142; break;
+    case 3143: ret = 13143; break;
+    case 3144: ret = 13144; break;
+    case 3145: ret = 13145; break;
+    case 3146: ret = 13146; break;
+    case 3147: ret = 13147; break;
+    case 3148: ret = 13148; break;
+    case 3149: ret = 13149; break;
+    case 3150: ret = 13150; break;
+    case 3151: ret = 13151; break;
+    case 3152: ret = 13152; break;
+    case 3153: ret = 13153; break;
+    case 3154: ret = 13154; break;
+    case 3155: ret = 13155; break;
+    case 3156: ret = 13156; break;
+    case 3157: ret = 13157; break;
+    case 3158: ret = 13158; break;
+    case 3159: ret = 13159; break;
+    case 3160: ret = 13160; break;
+    case 3161: ret = 13161; break;
+    case 3162: ret = 13162; break;
+    case 3163: ret = 13163; break;
+    case 3164: ret = 13164; break;
+    case 3165: ret = 13165; break;
+    case 3166: ret = 13166; break;
+    case 3167: ret = 13167; break;
+    case 3168: ret = 13168; break;
+    case 3169: ret = 13169; break;
+    case 3170: ret = 13170; break;
+    case 3171: ret = 13171; break;
+    case 3172: ret = 13172; break;
+    case 3173: ret = 13173; break;
+    case 3174: ret = 13174; break;
+    case 3175: ret = 13175; break;
+    case 3176: ret = 13176; break;
+    case 3177: ret = 13177; break;
+    case 3178: ret = 13178; break;
+    case 3179: ret = 13179; break;
+    case 3180: ret = 13180; break;
+    case 3181: ret = 13181; break;
+    case 3182: ret = 13182; break;
+    case 3183: ret = 13183; break;
+    case 3184: ret = 13184; break;
+    case 3185: ret = 13185; break;
+    case 3186: ret = 13186; break;
+    case 3187: ret = 13187; break;
+    case 3188: ret = 13188; break;
+    case 3189: ret = 13189; break;
+    case 3190: ret = 13190; break;
+    case 3191: ret = 13191; break;
+    case 3192: ret = 13192; break;
+    case 3193: ret = 13193; break;
+    case 3194: ret = 13194; break;
+    case 3195: ret = 13195; break;
+    case 3196: ret = 13196; break;
+    case 3197: ret = 13197; break;
+    case 3198: ret = 13198; break;
+    case 3199: ret = 13199; break;
+    case 3200: ret = 13200; break;
+    case 3201: ret = 13201; break;
+    case 3202: ret = 13202; break;
+    case 3203: ret = 13203; break;
+    case 3204: ret = 13204; break;
+    case 3205: ret = 13205; break;
+    case 3206: ret = 13206; break;
+    case 3207: ret = 13207; break;
+    case 3208: ret = 13208; break;
+    case 3209: ret = 13209; break;
+    case 3210: ret = 13210; break;
+    case 3211: ret = 13211; break;
+    case 3212: ret = 13212; break;
+    case 3213: ret = 13213; break;
+    case 3214: ret = 13214; break;
+    case 3215: ret = 13215; break;
+    case 3216: ret = 13216; break;
+    case 3217: ret = 13217; break;
+    case 3218: ret = 13218; break;
+    case 3219: ret = 13219; break;
+    case 3220: ret = 13220; break;
+    case 3221: ret = 13221; break;
+    case 3222: ret = 13222; break;
+    case 3223: ret = 13223; break;
+    case 3224: ret = 13224; break;
+    case 3225: ret = 13225; break;
+    case 3226: ret = 13226; break;
+    case 3227: ret = 13227; break;
+    case 3228: ret = 13228; break;
+    case 3229: ret = 13229; break;
+    case 3230: ret = 13230; break;
+    case 3231: ret = 13231; break;
+    case 3232: ret = 13232; break;
+    case 3233: ret = 13233; break;
+    case 3234: ret = 13234; break;
+    case 3235: ret = 13235; break;
+    case 3236: ret = 13236; break;
+    case 3237: ret = 13237; break;
+    case 3238: ret = 13238; break;
+    case 3239: ret = 13239; break;
+    case 3240: ret = 13240; break;
+    case 3241: ret = 13241; break;
+    case 3242: ret = 13242; break;
+    case 3243: ret = 13243; break;
+    case 3244: ret = 13244; break;
+    case 3245: ret = 13245; break;
+    case 3246: ret = 13246; break;
+    case 3247: ret = 13247; break;
+    case 3248: ret = 13248; break;
+    case 3249: ret = 13249; break;
+    case 3250: ret = 13250; break;
+    case 3251: ret = 13251; break;
+    case 3252: ret = 13252; break;
+    case 3253: ret = 13253; break;
+    case 3254: ret = 13254; break;
+    case 3255: ret = 13255; break;
+    case 3256: ret = 13256; break;
+    case 3257: ret = 13257; break;
+    case 3258: ret = 13258; break;
+    case 3259: ret = 13259; break;
+    case 3260: ret = 13260; break;
+    case 3261: ret = 13261; break;
+    case 3262: ret = 13262; break;
+    case 3263: ret = 13263; break;
+    case 3264: ret = 13264; break;
+    case 3265: ret = 13265; break;
+    case 3266: ret = 13266; break;
+    case 3267: ret = 13267; break;
+    case 3268: ret = 13268; break;
+    case 3269: ret = 13269; break;
+    case 3270: ret = 13270; break;
+    case 3271: ret = 13271; break;
+    case 3272: ret = 13272; break;
+    case 3273: ret = 13273; break;
+    case 3274: ret = 13274; break;
+    case 3275: ret = 13275; break;
+    case 3276: ret = 13276; break;
+    case 3277: ret = 13277; break;
+    case 3278: ret = 13278; break;
+    case 3279: ret = 13279; break;
+    case 3280: ret = 13280; break;
+    case 3281: ret = 13281; break;
+    case 3282: ret = 13282; break;
+    case 3283: ret = 13283; break;
+    case 3284: ret = 13284; break;
+    case 3285: ret = 13285; break;
+    case 3286: ret = 13286; break;
+    case 3287: ret = 13287; break;
+    case 3288: ret = 13288; break;
+    case 3289: ret = 13289; break;
+    case 3290: ret = 13290; break;
+    case 3291: ret = 13291; break;
+    case 3292: ret = 13292; break;
+    case 3293: ret = 13293; break;
+    case 3294: ret = 13294; break;
+    case 3295: ret = 13295; break;
+    case 3296: ret = 13296; break;
+    case 3297: ret = 13297; break;
+    case 3298: ret = 13298; break;
+    case 3299: ret = 13299; break;
+    case 3300: ret = 13300; break;
+    case 3301: ret = 13301; break;
+    case 3302: ret = 13302; break;
+    case 3303: ret = 13303; break;
+    case 3304: ret = 13304; break;
+    case 3305: ret = 13305; break;
+    case 3306: ret = 13306; break;
+    case 3307: ret = 13307; break;
+    case 3308: ret = 13308; break;
+    case 3309: ret = 13309; break;
+    case 3310: ret = 13310; break;
+    case 3311: ret = 13311; break;
+    case 3312: ret = 13312; break;
+    case 3313: ret = 13313; break;
+    case 3314: ret = 13314; break;
+    case 3315: ret = 13315; break;
+    case 3316: ret = 13316; break;
+    case 3317: ret = 13317; break;
+    case 3318: ret = 13318; break;
+    case 3319: ret = 13319; break;
+    case 3320: ret = 13320; break;
+    case 3321: ret = 13321; break;
+    case 3322: ret = 13322; break;
+    case 3323: ret = 13323; break;
+    case 3324: ret = 13324; break;
+    case 3325: ret = 13325; break;
+    case 3326: ret = 13326; break;
+    case 3327: ret = 13327; break;
+    case 3328: ret = 13328; break;
+    case 3329: ret = 13329; break;
+    case 3330: ret = 13330; break;
+    case 3331: ret = 13331; break;
+    case 3332: ret = 13332; break;
+    case 3333: ret = 13333; break;
+    case 3334: ret = 13334; break;
+    case 3335: ret = 13335; break;
+    case 3336: ret = 13336; break;
+    case 3337: ret = 13337; break;
+    case 3338: ret = 13338; break;
+    case 3339: ret = 13339; break;
+    case 3340: ret = 13340; break;
+    case 3341: ret = 13341; break;
+    case 3342: ret = 13342; break;
+    case 3343: ret = 13343; break;
+    case 3344: ret = 13344; break;
+    case 3345: ret = 13345; break;
+    case 3346: ret = 13346; break;
+    case 3347: ret = 13347; break;
+    case 3348: ret = 13348; break;
+    case 3349: ret = 13349; break;
+    case 3350: ret = 13350; break;
+    case 3351: ret = 13351; break;
+    case 3352: ret = 13352; break;
+    case 3353: ret = 13353; break;
+    case 3354: ret = 13354; break;
+    case 3355: ret = 13355; break;
+    case 3356: ret = 13356; break;
+    case 3357: ret = 13357; break;
+    case 3358: ret = 13358; break;
+    case 3359: ret = 13359; break;
+    case 3360: ret = 13360; break;
+    case 3361: ret = 13361; break;
+    case 3362: ret = 13362; break;
+    case 3363: ret = 13363; break;
+    case 3364: ret = 13364; break;
+    case 3365: ret = 13365; break;
+    case 3366: ret = 13366; break;
+    case 3367: ret = 13367; break;
+    case 3368: ret = 13368; break;
+    case 3369: ret = 13369; break;
+    case 3370: ret = 13370; break;
+    case 3371: ret = 13371; break;
+    case 3372: ret = 13372; break;
+    case 3373: ret = 13373; break;
+    case 3374: ret = 13374; break;
+    case 3375: ret = 13375; break;
+    case 3376: ret = 13376; break;
+    case 3377: ret = 13377; break;
+    case 3378: ret = 13378; break;
+    case 3379: ret = 13379; break;
+    case 3380: ret = 13380; break;
+    case 3381: ret = 13381; break;
+    case 3382: ret = 13382; break;
+    case 3383: ret = 13383; break;
+    case 3384: ret = 13384; break;
+    case 3385: ret = 13385; break;
+    case 3386: ret = 13386; break;
+    case 3387: ret = 13387; break;
+    case 3388: ret = 13388; break;
+    case 3389: ret = 13389; break;
+    case 3390: ret = 13390; break;
+    case 3391: ret = 13391; break;
+    case 3392: ret = 13392; break;
+    case 3393: ret = 13393; break;
+    case 3394: ret = 13394; break;
+    case 3395: ret = 13395; break;
+    case 3396: ret = 13396; break;
+    case 3397: ret = 13397; break;
+    case 3398: ret = 13398; break;
+    case 3399: ret = 13399; break;
+    case 3400: ret = 13400; break;
+    case 3401: ret = 13401; break;
+    case 3402: ret = 13402; break;
+    case 3403: ret = 13403; break;
+    case 3404: ret = 13404; break;
+    case 3405: ret = 13405; break;
+    case 3406: ret = 13406; break;
+    case 3407: ret = 13407; break;
+    case 3408: ret = 13408; break;
+    case 3409: ret = 13409; break;
+    case 3410: ret = 13410; break;
+    case 3411: ret = 13411; break;
+    case 3412: ret = 13412; break;
+    case 3413: ret = 13413; break;
+    case 3414: ret = 13414; break;
+    case 3415: ret = 13415; break;
+    case 3416: ret = 13416; break;
+    case 3417: ret = 13417; break;
+    case 3418: ret = 13418; break;
+    case 3419: ret = 13419; break;
+    case 3420: ret = 13420; break;
+    case 3421: ret = 13421; break;
+    case 3422: ret = 13422; break;
+    case 3423: ret = 13423; break;
+    case 3424: ret = 13424; break;
+    case 3425: ret = 13425; break;
+    case 3426: ret = 13426; break;
+    case 3427: ret = 13427; break;
+    case 3428: ret = 13428; break;
+    case 3429: ret = 13429; break;
+    case 3430: ret = 13430; break;
+    case 3431: ret = 13431; break;
+    case 3432: ret = 13432; break;
+    case 3433: ret = 13433; break;
+    case 3434: ret = 13434; break;
+    case 3435: ret = 13435; break;
+    case 3436: ret = 13436; break;
+    case 3437: ret = 13437; break;
+    case 3438: ret = 13438; break;
+    case 3439: ret = 13439; break;
+    case 3440: ret = 13440; break;
+    case 3441: ret = 13441; break;
+    case 3442: ret = 13442; break;
+    case 3443: ret = 13443; break;
+    case 3444: ret = 13444; break;
+    case 3445: ret = 13445; break;
+    case 3446: ret = 13446; break;
+    case 3447: ret = 13447; break;
+    case 3448: ret = 13448; break;
+    case 3449: ret = 13449; break;
+    case 3450: ret = 13450; break;
+    case 3451: ret = 13451; break;
+    case 3452: ret = 13452; break;
+    case 3453: ret = 13453; break;
+    case 3454: ret = 13454; break;
+    case 3455: ret = 13455; break;
+    case 3456: ret = 13456; break;
+    case 3457: ret = 13457; break;
+    case 3458: ret = 13458; break;
+    case 3459: ret = 13459; break;
+    case 3460: ret = 13460; break;
+    case 3461: ret = 13461; break;
+    case 3462: ret = 13462; break;
+    case 3463: ret = 13463; break;
+    case 3464: ret = 13464; break;
+    case 3465: ret = 13465; break;
+    case 3466: ret = 13466; break;
+    case 3467: ret = 13467; break;
+    case 3468: ret = 13468; break;
+    case 3469: ret = 13469; break;
+    case 3470: ret = 13470; break;
+    case 3471: ret = 13471; break;
+    case 3472: ret = 13472; break;
+    case 3473: ret = 13473; break;
+    case 3474: ret = 13474; break;
+    case 3475: ret = 13475; break;
+    case 3476: ret = 13476; break;
+    case 3477: ret = 13477; break;
+    case 3478: ret = 13478; break;
+    case 3479: ret = 13479; break;
+    case 3480: ret = 13480; break;
+    case 3481: ret = 13481; break;
+    case 3482: ret = 13482; break;
+    case 3483: ret = 13483; break;
+    case 3484: ret = 13484; break;
+    case 3485: ret = 13485; break;
+    case 3486: ret = 13486; break;
+    case 3487: ret = 13487; break;
+    case 3488: ret = 13488; break;
+    case 3489: ret = 13489; break;
+    case 3490: ret = 13490; break;
+    case 3491: ret = 13491; break;
+    case 3492: ret = 13492; break;
+    case 3493: ret = 13493; break;
+    case 3494: ret = 13494; break;
+    case 3495: ret = 13495; break;
+    case 3496: ret = 13496; break;
+    case 3497: ret = 13497; break;
+    case 3498: ret = 13498; break;
+    case 3499: ret = 13499; break;
+    case 3500: ret = 13500; break;
+    case 3501: ret = 13501; break;
+    case 3502: ret = 13502; break;
+    case 3503: ret = 13503; break;
+    case 3504: ret = 13504; break;
+    case 3505: ret = 13505; break;
+    case 3506: ret = 13506; break;
+    case 3507: ret = 13507; break;
+    case 3508: ret = 13508; break;
+    case 3509: ret = 13509; break;
+    case 3510: ret = 13510; break;
+    case 3511: ret = 13511; break;
+    case 3512: ret = 13512; break;
+    case 3513: ret = 13513; break;
+    case 3514: ret = 13514; break;
+    case 3515: ret = 13515; break;
+    case 3516: ret = 13516; break;
+    case 3517: ret = 13517; break;
+    case 3518: ret = 13518; break;
+    case 3519: ret = 13519; break;
+    case 3520: ret = 13520; break;
+    case 3521: ret = 13521; break;
+    case 3522: ret = 13522; break;
+    case 3523: ret = 13523; break;
+    case 3524: ret = 13524; break;
+    case 3525: ret = 13525; break;
+    case 3526: ret = 13526; break;
+    case 3527: ret = 13527; break;
+    case 3528: ret = 13528; break;
+    case 3529: ret = 13529; break;
+    case 3530: ret = 13530; break;
+    case 3531: ret = 13531; break;
+    case 3532: ret = 13532; break;
+    case 3533: ret = 13533; break;
+    case 3534: ret = 13534; break;
+    case 3535: ret = 13535; break;
+    case 3536: ret = 13536; break;
+    case 3537: ret = 13537; break;
+    case 3538: ret = 13538; break;
+    case 3539: ret = 13539; break;
+    case 3540: ret = 13540; break;
+    case 3541: ret = 13541; break;
+    case 3542: ret = 13542; break;
+    case 3543: ret = 13543; break;
+    case 3544: ret = 13544; break;
+    case 3545: ret = 13545; break;
+    case 3546: ret = 13546; break;
+    case 3547: ret = 13547; break;
+    case 3548: ret = 13548; break;
+    case 3549: ret = 13549; break;
+    case 3550: ret = 13550; break;
+    case 3551: ret = 13551; break;
+    case 3552: ret = 13552; break;
+    case 3553: ret = 13553; break;
+    case 3554: ret = 13554; break;
+    case 3555: ret = 13555; break;
+    case 3556: ret = 13556; break;
+    case 3557: ret = 13557; break;
+    case 3558: ret = 13558; break;
+    case 3559: ret = 13559; break;
+    case 3560: ret = 13560; break;
+    case 3561: ret = 13561; break;
+    case 3562: ret = 13562; break;
+    case 3563: ret = 13563; break;
+    case 3564: ret = 13564; break;
+    case 3565: ret = 13565; break;
+    case 3566: ret = 13566; break;
+    case 3567: ret = 13567; break;
+    case 3568: ret = 13568; break;
+    case 3569: ret = 13569; break;
+    case 3570: ret = 13570; break;
+    case 3571: ret = 13571; break;
+    case 3572: ret = 13572; break;
+    case 3573: ret = 13573; break;
+    case 3574: ret = 13574; break;
+    case 3575: ret = 13575; break;
+    case 3576: ret = 13576; break;
+    case 3577: ret = 13577; break;
+    case 3578: ret = 13578; break;
+    case 3579: ret = 13579; break;
+    case 3580: ret = 13580; break;
+    case 3581: ret = 13581; break;
+    case 3582: ret = 13582; break;
+    case 3583: ret = 13583; break;
+    case 3584: ret = 13584; break;
+    case 3585: ret = 13585; break;
+    case 3586: ret = 13586; break;
+    case 3587: ret = 13587; break;
+    case 3588: ret = 13588; break;
+    case 3589: ret = 13589; break;
+    case 3590: ret = 13590; break;
+    case 3591: ret = 13591; break;
+    case 3592: ret = 13592; break;
+    case 3593: ret = 13593; break;
+    case 3594: ret = 13594; break;
+    case 3595: ret = 13595; break;
+    case 3596: ret = 13596; break;
+    case 3597: ret = 13597; break;
+    case 3598: ret = 13598; break;
+    case 3599: ret = 13599; break;
+    case 3600: ret = 13600; break;
+    case 3601: ret = 13601; break;
+    case 3602: ret = 13602; break;
+    case 3603: ret = 13603; break;
+    case 3604: ret = 13604; break;
+    case 3605: ret = 13605; break;
+    case 3606: ret = 13606; break;
+    case 3607: ret = 13607; break;
+    case 3608: ret = 13608; break;
+    case 3609: ret = 13609; break;
+    case 3610: ret = 13610; break;
+    case 3611: ret = 13611; break;
+    case 3612: ret = 13612; break;
+    case 3613: ret = 13613; break;
+    case 3614: ret = 13614; break;
+    case 3615: ret = 13615; break;
+    case 3616: ret = 13616; break;
+    case 3617: ret = 13617; break;
+    case 3618: ret = 13618; break;
+    case 3619: ret = 13619; break;
+    case 3620: ret = 13620; break;
+    case 3621: ret = 13621; break;
+    case 3622: ret = 13622; break;
+    case 3623: ret = 13623; break;
+    case 3624: ret = 13624; break;
+    case 3625: ret = 13625; break;
+    case 3626: ret = 13626; break;
+    case 3627: ret = 13627; break;
+    case 3628: ret = 13628; break;
+    case 3629: ret = 13629; break;
+    case 3630: ret = 13630; break;
+    case 3631: ret = 13631; break;
+    case 3632: ret = 13632; break;
+    case 3633: ret = 13633; break;
+    case 3634: ret = 13634; break;
+    case 3635: ret = 13635; break;
+    case 3636: ret = 13636; break;
+    case 3637: ret = 13637; break;
+    case 3638: ret = 13638; break;
+    case 3639: ret = 13639; break;
+    case 3640: ret = 13640; break;
+    case 3641: ret = 13641; break;
+    case 3642: ret = 13642; break;
+    case 3643: ret = 13643; break;
+    case 3644: ret = 13644; break;
+    case 3645: ret = 13645; break;
+    case 3646: ret = 13646; break;
+    case 3647: ret = 13647; break;
+    case 3648: ret = 13648; break;
+    case 3649: ret = 13649; break;
+    case 3650: ret = 13650; break;
+    case 3651: ret = 13651; break;
+    case 3652: ret = 13652; break;
+    case 3653: ret = 13653; break;
+    case 3654: ret = 13654; break;
+    case 3655: ret = 13655; break;
+    case 3656: ret = 13656; break;
+    case 3657: ret = 13657; break;
+    case 3658: ret = 13658; break;
+    case 3659: ret = 13659; break;
+    case 3660: ret = 13660; break;
+    case 3661: ret = 13661; break;
+    case 3662: ret = 13662; break;
+    case 3663: ret = 13663; break;
+    case 3664: ret = 13664; break;
+    case 3665: ret = 13665; break;
+    case 3666: ret = 13666; break;
+    case 3667: ret = 13667; break;
+    case 3668: ret = 13668; break;
+    case 3669: ret = 13669; break;
+    case 3670: ret = 13670; break;
+    case 3671: ret = 13671; break;
+    case 3672: ret = 13672; break;
+    case 3673: ret = 13673; break;
+    case 3674: ret = 13674; break;
+    case 3675: ret = 13675; break;
+    case 3676: ret = 13676; break;
+    case 3677: ret = 13677; break;
+    case 3678: ret = 13678; break;
+    case 3679: ret = 13679; break;
+    case 3680: ret = 13680; break;
+    case 3681: ret = 13681; break;
+    case 3682: ret = 13682; break;
+    case 3683: ret = 13683; break;
+    case 3684: ret = 13684; break;
+    case 3685: ret = 13685; break;
+    case 3686: ret = 13686; break;
+    case 3687: ret = 13687; break;
+    case 3688: ret = 13688; break;
+    case 3689: ret = 13689; break;
+    case 3690: ret = 13690; break;
+    case 3691: ret = 13691; break;
+    case 3692: ret = 13692; break;
+    case 3693: ret = 13693; break;
+    case 3694: ret = 13694; break;
+    case 3695: ret = 13695; break;
+    case 3696: ret = 13696; break;
+    case 3697: ret = 13697; break;
+    case 3698: ret = 13698; break;
+    case 3699: ret = 13699; break;
+    case 3700: ret = 13700; break;
+    case 3701: ret = 13701; break;
+    case 3702: ret = 13702; break;
+    case 3703: ret = 13703; break;
+    case 3704: ret = 13704; break;
+    case 3705: ret = 13705; break;
+    case 3706: ret = 13706; break;
+    case 3707: ret = 13707; break;
+    case 3708: ret = 13708; break;
+    case 3709: ret = 13709; break;
+    case 3710: ret = 13710; break;
+    case 3711: ret = 13711; break;
+    case 3712: ret = 13712; break;
+    case 3713: ret = 13713; break;
+    case 3714: ret = 13714; break;
+    case 3715: ret = 13715; break;
+    case 3716: ret = 13716; break;
+    case 3717: ret = 13717; break;
+    case 3718: ret = 13718; break;
+    case 3719: ret = 13719; break;
+    case 3720: ret = 13720; break;
+    case 3721: ret = 13721; break;
+    case 3722: ret = 13722; break;
+    case 3723: ret = 13723; break;
+    case 3724: ret = 13724; break;
+    case 3725: ret = 13725; break;
+    case 3726: ret = 13726; break;
+    case 3727: ret = 13727; break;
+    case 3728: ret = 13728; break;
+    case 3729: ret = 13729; break;
+    case 3730: ret = 13730; break;
+    case 3731: ret = 13731; break;
+    case 3732: ret = 13732; break;
+    case 3733: ret = 13733; break;
+    case 3734: ret = 13734; break;
+    case 3735: ret = 13735; break;
+    case 3736: ret = 13736; break;
+    case 3737: ret = 13737; break;
+    case 3738: ret = 13738; break;
+    case 3739: ret = 13739; break;
+    case 3740: ret = 13740; break;
+    case 3741: ret = 13741; break;
+    case 3742: ret = 13742; break;
+    case 3743: ret = 13743; break;
+    case 3744: ret = 13744; break;
+    case 3745: ret = 13745; break;
+    case 3746: ret = 13746; break;
+    case 3747: ret = 13747; break;
+    case 3748: ret = 13748; break;
+    case 3749: ret = 13749; break;
+    case 3750: ret = 13750; break;
+    case 3751: ret = 13751; break;
+    case 3752: ret = 13752; break;
+    case 3753: ret = 13753; break;
+    case 3754: ret = 13754; break;
+    case 3755: ret = 13755; break;
+    case 3756: ret = 13756; break;
+    case 3757: ret = 13757; break;
+    case 3758: ret = 13758; break;
+    case 3759: ret = 13759; break;
+    case 3760: ret = 13760; break;
+    case 3761: ret = 13761; break;
+    case 3762: ret = 13762; break;
+    case 3763: ret = 13763; break;
+    case 3764: ret = 13764; break;
+    case 3765: ret = 13765; break;
+    case 3766: ret = 13766; break;
+    case 3767: ret = 13767; break;
+    case 3768: ret = 13768; break;
+    case 3769: ret = 13769; break;
+    case 3770: ret = 13770; break;
+    case 3771: ret = 13771; break;
+    case 3772: ret = 13772; break;
+    case 3773: ret = 13773; break;
+    case 3774: ret = 13774; break;
+    case 3775: ret = 13775; break;
+    case 3776: ret = 13776; break;
+    case 3777: ret = 13777; break;
+    case 3778: ret = 13778; break;
+    case 3779: ret = 13779; break;
+    case 3780: ret = 13780; break;
+    case 3781: ret = 13781; break;
+    case 3782: ret = 13782; break;
+    case 3783: ret = 13783; break;
+    case 3784: ret = 13784; break;
+    case 3785: ret = 13785; break;
+    case 3786: ret = 13786; break;
+    case 3787: ret = 13787; break;
+    case 3788: ret = 13788; break;
+    case 3789: ret = 13789; break;
+    case 3790: ret = 13790; break;
+    case 3791: ret = 13791; break;
+    case 3792: ret = 13792; break;
+    case 3793: ret = 13793; break;
+    case 3794: ret = 13794; break;
+    case 3795: ret = 13795; break;
+    case 3796: ret = 13796; break;
+    case 3797: ret = 13797; break;
+    case 3798: ret = 13798; break;
+    case 3799: ret = 13799; break;
+    case 3800: ret = 13800; break;
+    case 3801: ret = 13801; break;
+    case 3802: ret = 13802; break;
+    case 3803: ret = 13803; break;
+    case 3804: ret = 13804; break;
+    case 3805: ret = 13805; break;
+    case 3806: ret = 13806; break;
+    case 3807: ret = 13807; break;
+    case 3808: ret = 13808; break;
+    case 3809: ret = 13809; break;
+    case 3810: ret = 13810; break;
+    case 3811: ret = 13811; break;
+    case 3812: ret = 13812; break;
+    case 3813: ret = 13813; break;
+    case 3814: ret = 13814; break;
+    case 3815: ret = 13815; break;
+    case 3816: ret = 13816; break;
+    case 3817: ret = 13817; break;
+    case 3818: ret = 13818; break;
+    case 3819: ret = 13819; break;
+    case 3820: ret = 13820; break;
+    case 3821: ret = 13821; break;
+    case 3822: ret = 13822; break;
+    case 3823: ret = 13823; break;
+    case 3824: ret = 13824; break;
+    case 3825: ret = 13825; break;
+    case 3826: ret = 13826; break;
+    case 3827: ret = 13827; break;
+    case 3828: ret = 13828; break;
+    case 3829: ret = 13829; break;
+    case 3830: ret = 13830; break;
+    case 3831: ret = 13831; break;
+    case 3832: ret = 13832; break;
+    case 3833: ret = 13833; break;
+    case 3834: ret = 13834; break;
+    case 3835: ret = 13835; break;
+    case 3836: ret = 13836; break;
+    case 3837: ret = 13837; break;
+    case 3838: ret = 13838; break;
+    case 3839: ret = 13839; break;
+    case 3840: ret = 13840; break;
+    case 3841: ret = 13841; break;
+    case 3842: ret = 13842; break;
+    case 3843: ret = 13843; break;
+    case 3844: ret = 13844; break;
+    case 3845: ret = 13845; break;
+    case 3846: ret = 13846; break;
+    case 3847: ret = 13847; break;
+    case 3848: ret = 13848; break;
+    case 3849: ret = 13849; break;
+    case 3850: ret = 13850; break;
+    case 3851: ret = 13851; break;
+    case 3852: ret = 13852; break;
+    case 3853: ret = 13853; break;
+    case 3854: ret = 13854; break;
+    case 3855: ret = 13855; break;
+    case 3856: ret = 13856; break;
+    case 3857: ret = 13857; break;
+    case 3858: ret = 13858; break;
+    case 3859: ret = 13859; break;
+    case 3860: ret = 13860; break;
+    case 3861: ret = 13861; break;
+    case 3862: ret = 13862; break;
+    case 3863: ret = 13863; break;
+    case 3864: ret = 13864; break;
+    case 3865: ret = 13865; break;
+    case 3866: ret = 13866; break;
+    case 3867: ret = 13867; break;
+    case 3868: ret = 13868; break;
+    case 3869: ret = 13869; break;
+    case 3870: ret = 13870; break;
+    case 3871: ret = 13871; break;
+    case 3872: ret = 13872; break;
+    case 3873: ret = 13873; break;
+    case 3874: ret = 13874; break;
+    case 3875: ret = 13875; break;
+    case 3876: ret = 13876; break;
+    case 3877: ret = 13877; break;
+    case 3878: ret = 13878; break;
+    case 3879: ret = 13879; break;
+    case 3880: ret = 13880; break;
+    case 3881: ret = 13881; break;
+    case 3882: ret = 13882; break;
+    case 3883: ret = 13883; break;
+    case 3884: ret = 13884; break;
+    case 3885: ret = 13885; break;
+    case 3886: ret = 13886; break;
+    case 3887: ret = 13887; break;
+    case 3888: ret = 13888; break;
+    case 3889: ret = 13889; break;
+    case 3890: ret = 13890; break;
+    case 3891: ret = 13891; break;
+    case 3892: ret = 13892; break;
+    case 3893: ret = 13893; break;
+    case 3894: ret = 13894; break;
+    case 3895: ret = 13895; break;
+    case 3896: ret = 13896; break;
+    case 3897: ret = 13897; break;
+    case 3898: ret = 13898; break;
+    case 3899: ret = 13899; break;
+    case 3900: ret = 13900; break;
+    case 3901: ret = 13901; break;
+    case 3902: ret = 13902; break;
+    case 3903: ret = 13903; break;
+    case 3904: ret = 13904; break;
+    case 3905: ret = 13905; break;
+    case 3906: ret = 13906; break;
+    case 3907: ret = 13907; break;
+    case 3908: ret = 13908; break;
+    case 3909: ret = 13909; break;
+    case 3910: ret = 13910; break;
+    case 3911: ret = 13911; break;
+    case 3912: ret = 13912; break;
+    case 3913: ret = 13913; break;
+    case 3914: ret = 13914; break;
+    case 3915: ret = 13915; break;
+    case 3916: ret = 13916; break;
+    case 3917: ret = 13917; break;
+    case 3918: ret = 13918; break;
+    case 3919: ret = 13919; break;
+    case 3920: ret = 13920; break;
+    case 3921: ret = 13921; break;
+    case 3922: ret = 13922; break;
+    case 3923: ret = 13923; break;
+    case 3924: ret = 13924; break;
+    case 3925: ret = 13925; break;
+    case 3926: ret = 13926; break;
+    case 3927: ret = 13927; break;
+    case 3928: ret = 13928; break;
+    case 3929: ret = 13929; break;
+    case 3930: ret = 13930; break;
+    case 3931: ret = 13931; break;
+    case 3932: ret = 13932; break;
+    case 3933: ret = 13933; break;
+    case 3934: ret = 13934; break;
+    case 3935: ret = 13935; break;
+    case 3936: ret = 13936; break;
+    case 3937: ret = 13937; break;
+    case 3938: ret = 13938; break;
+    case 3939: ret = 13939; break;
+    case 3940: ret = 13940; break;
+    case 3941: ret = 13941; break;
+    case 3942: ret = 13942; break;
+    case 3943: ret = 13943; break;
+    case 3944: ret = 13944; break;
+    case 3945: ret = 13945; break;
+    case 3946: ret = 13946; break;
+    case 3947: ret = 13947; break;
+    case 3948: ret = 13948; break;
+    case 3949: ret = 13949; break;
+    case 3950: ret = 13950; break;
+    case 3951: ret = 13951; break;
+    case 3952: ret = 13952; break;
+    case 3953: ret = 13953; break;
+    case 3954: ret = 13954; break;
+    case 3955: ret = 13955; break;
+    case 3956: ret = 13956; break;
+    case 3957: ret = 13957; break;
+    case 3958: ret = 13958; break;
+    case 3959: ret = 13959; break;
+    case 3960: ret = 13960; break;
+    case 3961: ret = 13961; break;
+    case 3962: ret = 13962; break;
+    case 3963: ret = 13963; break;
+    case 3964: ret = 13964; break;
+    case 3965: ret = 13965; break;
+    case 3966: ret = 13966; break;
+    case 3967: ret = 13967; break;
+    case 3968: ret = 13968; break;
+    case 3969: ret = 13969; break;
+    case 3970: ret = 13970; break;
+    case 3971: ret = 13971; break;
+    case 3972: ret = 13972; break;
+    case 3973: ret = 13973; break;
+    case 3974: ret = 13974; break;
+    case 3975: ret = 13975; break;
+    case 3976: ret = 13976; break;
+    case 3977: ret = 13977; break;
+    case 3978: ret = 13978; break;
+    case 3979: ret = 13979; break;
+    case 3980: ret = 13980; break;
+    case 3981: ret = 13981; break;
+    case 3982: ret = 13982; break;
+    case 3983: ret = 13983; break;
+    case 3984: ret = 13984; break;
+    case 3985: ret = 13985; break;
+    case 3986: ret = 13986; break;
+    case 3987: ret = 13987; break;
+    case 3988: ret = 13988; break;
+    case 3989: ret = 13989; break;
+    case 3990: ret = 13990; break;
+    case 3991: ret = 13991; break;
+    case 3992: ret = 13992; break;
+    case 3993: ret = 13993; break;
+    case 3994: ret = 13994; break;
+    case 3995: ret = 13995; break;
+    case 3996: ret = 13996; break;
+    case 3997: ret = 13997; break;
+    case 3998: ret = 13998; break;
+    case 3999: ret = 13999; break;
+    case 4000: ret = 14000; break;
+    case 4001: ret = 14001; break;
+    case 4002: ret = 14002; break;
+    case 4003: ret = 14003; break;
+    case 4004: ret = 14004; break;
+    case 4005: ret = 14005; break;
+    case 4006: ret = 14006; break;
+    case 4007: ret = 14007; break;
+    case 4008: ret = 14008; break;
+    case 4009: ret = 14009; break;
+    case 4010: ret = 14010; break;
+    case 4011: ret = 14011; break;
+    case 4012: ret = 14012; break;
+    case 4013: ret = 14013; break;
+    case 4014: ret = 14014; break;
+    case 4015: ret = 14015; break;
+    case 4016: ret = 14016; break;
+    case 4017: ret = 14017; break;
+    case 4018: ret = 14018; break;
+    case 4019: ret = 14019; break;
+    case 4020: ret = 14020; break;
+    case 4021: ret = 14021; break;
+    case 4022: ret = 14022; break;
+    case 4023: ret = 14023; break;
+    case 4024: ret = 14024; break;
+    case 4025: ret = 14025; break;
+    case 4026: ret = 14026; break;
+    case 4027: ret = 14027; break;
+    case 4028: ret = 14028; break;
+    case 4029: ret = 14029; break;
+    case 4030: ret = 14030; break;
+    case 4031: ret = 14031; break;
+    case 4032: ret = 14032; break;
+    case 4033: ret = 14033; break;
+    case 4034: ret = 14034; break;
+    case 4035: ret = 14035; break;
+    case 4036: ret = 14036; break;
+    case 4037: ret = 14037; break;
+    case 4038: ret = 14038; break;
+    case 4039: ret = 14039; break;
+    case 4040: ret = 14040; break;
+    case 4041: ret = 14041; break;
+    case 4042: ret = 14042; break;
+    case 4043: ret = 14043; break;
+    case 4044: ret = 14044; break;
+    case 4045: ret = 14045; break;
+    case 4046: ret = 14046; break;
+    case 4047: ret = 14047; break;
+    case 4048: ret = 14048; break;
+    case 4049: ret = 14049; break;
+    case 4050: ret = 14050; break;
+    case 4051: ret = 14051; break;
+    case 4052: ret = 14052; break;
+    case 4053: ret = 14053; break;
+    case 4054: ret = 14054; break;
+    case 4055: ret = 14055; break;
+    case 4056: ret = 14056; break;
+    case 4057: ret = 14057; break;
+    case 4058: ret = 14058; break;
+    case 4059: ret = 14059; break;
+    case 4060: ret = 14060; break;
+    case 4061: ret = 14061; break;
+    case 4062: ret = 14062; break;
+    case 4063: ret = 14063; break;
+    case 4064: ret = 14064; break;
+    case 4065: ret = 14065; break;
+    case 4066: ret = 14066; break;
+    case 4067: ret = 14067; break;
+    case 4068: ret = 14068; break;
+    case 4069: ret = 14069; break;
+    case 4070: ret = 14070; break;
+    case 4071: ret = 14071; break;
+    case 4072: ret = 14072; break;
+    case 4073: ret = 14073; break;
+    case 4074: ret = 14074; break;
+    case 4075: ret = 14075; break;
+    case 4076: ret = 14076; break;
+    case 4077: ret = 14077; break;
+    case 4078: ret = 14078; break;
+    case 4079: ret = 14079; break;
+    case 4080: ret = 14080; break;
+    case 4081: ret = 14081; break;
+    case 4082: ret = 14082; break;
+    case 4083: ret = 14083; break;
+    case 4084: ret = 14084; break;
+    case 4085: ret = 14085; break;
+    case 4086: ret = 14086; break;
+    case 4087: ret = 14087; break;
+    case 4088: ret = 14088; break;
+    case 4089: ret = 14089; break;
+    case 4090: ret = 14090; break;
+    case 4091: ret = 14091; break;
+    case 4092: ret = 14092; break;
+    case 4093: ret = 14093; break;
+    case 4094: ret = 14094; break;
+    case 4095: ret = 14095; break;
+    case 4096: ret = 14096; break;
+    case 4097: ret = 14097; break;
+    case 4098: ret = 14098; break;
+    case 4099: ret = 14099; break;
+    case 4100: ret = 14100; break;
+    case 4101: ret = 14101; break;
+    case 4102: ret = 14102; break;
+    case 4103: ret = 14103; break;
+    case 4104: ret = 14104; break;
+    case 4105: ret = 14105; break;
+    case 4106: ret = 14106; break;
+    case 4107: ret = 14107; break;
+    case 4108: ret = 14108; break;
+    case 4109: ret = 14109; break;
+    case 4110: ret = 14110; break;
+    case 4111: ret = 14111; break;
+    case 4112: ret = 14112; break;
+    case 4113: ret = 14113; break;
+    case 4114: ret = 14114; break;
+    case 4115: ret = 14115; break;
+    case 4116: ret = 14116; break;
+    case 4117: ret = 14117; break;
+    case 4118: ret = 14118; break;
+    case 4119: ret = 14119; break;
+    case 4120: ret = 14120; break;
+    case 4121: ret = 14121; break;
+    case 4122: ret = 14122; break;
+    case 4123: ret = 14123; break;
+    case 4124: ret = 14124; break;
+    case 4125: ret = 14125; break;
+    case 4126: ret = 14126; break;
+    case 4127: ret = 14127; break;
+    case 4128: ret = 14128; break;
+    case 4129: ret = 14129; break;
+    case 4130: ret = 14130; break;
+    case 4131: ret = 14131; break;
+    case 4132: ret = 14132; break;
+    case 4133: ret = 14133; break;
+    case 4134: ret = 14134; break;
+    case 4135: ret = 14135; break;
+    case 4136: ret = 14136; break;
+    case 4137: ret = 14137; break;
+    case 4138: ret = 14138; break;
+    case 4139: ret = 14139; break;
+    case 4140: ret = 14140; break;
+    case 4141: ret = 14141; break;
+    case 4142: ret = 14142; break;
+    case 4143: ret = 14143; break;
+    case 4144: ret = 14144; break;
+    case 4145: ret = 14145; break;
+    case 4146: ret = 14146; break;
+    case 4147: ret = 14147; break;
+    case 4148: ret = 14148; break;
+    case 4149: ret = 14149; break;
+    case 4150: ret = 14150; break;
+    case 4151: ret = 14151; break;
+    case 4152: ret = 14152; break;
+    case 4153: ret = 14153; break;
+    case 4154: ret = 14154; break;
+    case 4155: ret = 14155; break;
+    case 4156: ret = 14156; break;
+    case 4157: ret = 14157; break;
+    case 4158: ret = 14158; break;
+    case 4159: ret = 14159; break;
+    case 4160: ret = 14160; break;
+    case 4161: ret = 14161; break;
+    case 4162: ret = 14162; break;
+    case 4163: ret = 14163; break;
+    case 4164: ret = 14164; break;
+    case 4165: ret = 14165; break;
+    case 4166: ret = 14166; break;
+    case 4167: ret = 14167; break;
+    case 4168: ret = 14168; break;
+    case 4169: ret = 14169; break;
+    case 4170: ret = 14170; break;
+    case 4171: ret = 14171; break;
+    case 4172: ret = 14172; break;
+    case 4173: ret = 14173; break;
+    case 4174: ret = 14174; break;
+    case 4175: ret = 14175; break;
+    case 4176: ret = 14176; break;
+    case 4177: ret = 14177; break;
+    case 4178: ret = 14178; break;
+    case 4179: ret = 14179; break;
+    case 4180: ret = 14180; break;
+    case 4181: ret = 14181; break;
+    case 4182: ret = 14182; break;
+    case 4183: ret = 14183; break;
+    case 4184: ret = 14184; break;
+    case 4185: ret = 14185; break;
+    case 4186: ret = 14186; break;
+    case 4187: ret = 14187; break;
+    case 4188: ret = 14188; break;
+    case 4189: ret = 14189; break;
+    case 4190: ret = 14190; break;
+    case 4191: ret = 14191; break;
+    case 4192: ret = 14192; break;
+    case 4193: ret = 14193; break;
+    case 4194: ret = 14194; break;
+    case 4195: ret = 14195; break;
+    case 4196: ret = 14196; break;
+    case 4197: ret = 14197; break;
+    case 4198: ret = 14198; break;
+    case 4199: ret = 14199; break;
+    case 4200: ret = 14200; break;
+    case 4201: ret = 14201; break;
+    case 4202: ret = 14202; break;
+    case 4203: ret = 14203; break;
+    case 4204: ret = 14204; break;
+    case 4205: ret = 14205; break;
+    case 4206: ret = 14206; break;
+    case 4207: ret = 14207; break;
+    case 4208: ret = 14208; break;
+    case 4209: ret = 14209; break;
+    case 4210: ret = 14210; break;
+    case 4211: ret = 14211; break;
+    case 4212: ret = 14212; break;
+    case 4213: ret = 14213; break;
+    case 4214: ret = 14214; break;
+    case 4215: ret = 14215; break;
+    case 4216: ret = 14216; break;
+    case 4217: ret = 14217; break;
+    case 4218: ret = 14218; break;
+    case 4219: ret = 14219; break;
+    case 4220: ret = 14220; break;
+    case 4221: ret = 14221; break;
+    case 4222: ret = 14222; break;
+    case 4223: ret = 14223; break;
+    case 4224: ret = 14224; break;
+    case 4225: ret = 14225; break;
+    case 4226: ret = 14226; break;
+    case 4227: ret = 14227; break;
+    case 4228: ret = 14228; break;
+    case 4229: ret = 14229; break;
+    case 4230: ret = 14230; break;
+    case 4231: ret = 14231; break;
+    case 4232: ret = 14232; break;
+    case 4233: ret = 14233; break;
+    case 4234: ret = 14234; break;
+    case 4235: ret = 14235; break;
+    case 4236: ret = 14236; break;
+    case 4237: ret = 14237; break;
+    case 4238: ret = 14238; break;
+    case 4239: ret = 14239; break;
+    case 4240: ret = 14240; break;
+    case 4241: ret = 14241; break;
+    case 4242: ret = 14242; break;
+    case 4243: ret = 14243; break;
+    case 4244: ret = 14244; break;
+    case 4245: ret = 14245; break;
+    case 4246: ret = 14246; break;
+    case 4247: ret = 14247; break;
+    case 4248: ret = 14248; break;
+    case 4249: ret = 14249; break;
+    case 4250: ret = 14250; break;
+    case 4251: ret = 14251; break;
+    case 4252: ret = 14252; break;
+    case 4253: ret = 14253; break;
+    case 4254: ret = 14254; break;
+    case 4255: ret = 14255; break;
+    case 4256: ret = 14256; break;
+    case 4257: ret = 14257; break;
+    case 4258: ret = 14258; break;
+    case 4259: ret = 14259; break;
+    case 4260: ret = 14260; break;
+    case 4261: ret = 14261; break;
+    case 4262: ret = 14262; break;
+    case 4263: ret = 14263; break;
+    case 4264: ret = 14264; break;
+    case 4265: ret = 14265; break;
+    case 4266: ret = 14266; break;
+    case 4267: ret = 14267; break;
+    case 4268: ret = 14268; break;
+    case 4269: ret = 14269; break;
+    case 4270: ret = 14270; break;
+    case 4271: ret = 14271; break;
+    case 4272: ret = 14272; break;
+    case 4273: ret = 14273; break;
+    case 4274: ret = 14274; break;
+    case 4275: ret = 14275; break;
+    case 4276: ret = 14276; break;
+    case 4277: ret = 14277; break;
+    case 4278: ret = 14278; break;
+    case 4279: ret = 14279; break;
+    case 4280: ret = 14280; break;
+    case 4281: ret = 14281; break;
+    case 4282: ret = 14282; break;
+    case 4283: ret = 14283; break;
+    case 4284: ret = 14284; break;
+    case 4285: ret = 14285; break;
+    case 4286: ret = 14286; break;
+    case 4287: ret = 14287; break;
+    case 4288: ret = 14288; break;
+    case 4289: ret = 14289; break;
+    case 4290: ret = 14290; break;
+    case 4291: ret = 14291; break;
+    case 4292: ret = 14292; break;
+    case 4293: ret = 14293; break;
+    case 4294: ret = 14294; break;
+    case 4295: ret = 14295; break;
+    case 4296: ret = 14296; break;
+    case 4297: ret = 14297; break;
+    case 4298: ret = 14298; break;
+    case 4299: ret = 14299; break;
+    case 4300: ret = 14300; break;
+    case 4301: ret = 14301; break;
+    case 4302: ret = 14302; break;
+    case 4303: ret = 14303; break;
+    case 4304: ret = 14304; break;
+    case 4305: ret = 14305; break;
+    case 4306: ret = 14306; break;
+    case 4307: ret = 14307; break;
+    case 4308: ret = 14308; break;
+    case 4309: ret = 14309; break;
+    case 4310: ret = 14310; break;
+    case 4311: ret = 14311; break;
+    case 4312: ret = 14312; break;
+    case 4313: ret = 14313; break;
+    case 4314: ret = 14314; break;
+    case 4315: ret = 14315; break;
+    case 4316: ret = 14316; break;
+    case 4317: ret = 14317; break;
+    case 4318: ret = 14318; break;
+    case 4319: ret = 14319; break;
+    case 4320: ret = 14320; break;
+    case 4321: ret = 14321; break;
+    case 4322: ret = 14322; break;
+    case 4323: ret = 14323; break;
+    case 4324: ret = 14324; break;
+    case 4325: ret = 14325; break;
+    case 4326: ret = 14326; break;
+    case 4327: ret = 14327; break;
+    case 4328: ret = 14328; break;
+    case 4329: ret = 14329; break;
+    case 4330: ret = 14330; break;
+    case 4331: ret = 14331; break;
+    case 4332: ret = 14332; break;
+    case 4333: ret = 14333; break;
+    case 4334: ret = 14334; break;
+    case 4335: ret = 14335; break;
+    case 4336: ret = 14336; break;
+    case 4337: ret = 14337; break;
+    case 4338: ret = 14338; break;
+    case 4339: ret = 14339; break;
+    case 4340: ret = 14340; break;
+    case 4341: ret = 14341; break;
+    case 4342: ret = 14342; break;
+    case 4343: ret = 14343; break;
+    case 4344: ret = 14344; break;
+    case 4345: ret = 14345; break;
+    case 4346: ret = 14346; break;
+    case 4347: ret = 14347; break;
+    case 4348: ret = 14348; break;
+    case 4349: ret = 14349; break;
+    case 4350: ret = 14350; break;
+    case 4351: ret = 14351; break;
+    case 4352: ret = 14352; break;
+    case 4353: ret = 14353; break;
+    case 4354: ret = 14354; break;
+    case 4355: ret = 14355; break;
+    case 4356: ret = 14356; break;
+    case 4357: ret = 14357; break;
+    case 4358: ret = 14358; break;
+    case 4359: ret = 14359; break;
+    case 4360: ret = 14360; break;
+    case 4361: ret = 14361; break;
+    case 4362: ret = 14362; break;
+    case 4363: ret = 14363; break;
+    case 4364: ret = 14364; break;
+    case 4365: ret = 14365; break;
+    case 4366: ret = 14366; break;
+    case 4367: ret = 14367; break;
+    case 4368: ret = 14368; break;
+    case 4369: ret = 14369; break;
+    case 4370: ret = 14370; break;
+    case 4371: ret = 14371; break;
+    case 4372: ret = 14372; break;
+    case 4373: ret = 14373; break;
+    case 4374: ret = 14374; break;
+    case 4375: ret = 14375; break;
+    case 4376: ret = 14376; break;
+    case 4377: ret = 14377; break;
+    case 4378: ret = 14378; break;
+    case 4379: ret = 14379; break;
+    case 4380: ret = 14380; break;
+    case 4381: ret = 14381; break;
+    case 4382: ret = 14382; break;
+    case 4383: ret = 14383; break;
+    case 4384: ret = 14384; break;
+    case 4385: ret = 14385; break;
+    case 4386: ret = 14386; break;
+    case 4387: ret = 14387; break;
+    case 4388: ret = 14388; break;
+    case 4389: ret = 14389; break;
+    case 4390: ret = 14390; break;
+    case 4391: ret = 14391; break;
+    case 4392: ret = 14392; break;
+    case 4393: ret = 14393; break;
+    case 4394: ret = 14394; break;
+    case 4395: ret = 14395; break;
+    case 4396: ret = 14396; break;
+    case 4397: ret = 14397; break;
+    case 4398: ret = 14398; break;
+    case 4399: ret = 14399; break;
+    case 4400: ret = 14400; break;
+    case 4401: ret = 14401; break;
+    case 4402: ret = 14402; break;
+    case 4403: ret = 14403; break;
+    case 4404: ret = 14404; break;
+    case 4405: ret = 14405; break;
+    case 4406: ret = 14406; break;
+    case 4407: ret = 14407; break;
+    case 4408: ret = 14408; break;
+    case 4409: ret = 14409; break;
+    case 4410: ret = 14410; break;
+    case 4411: ret = 14411; break;
+    case 4412: ret = 14412; break;
+    case 4413: ret = 14413; break;
+    case 4414: ret = 14414; break;
+    case 4415: ret = 14415; break;
+    case 4416: ret = 14416; break;
+    case 4417: ret = 14417; break;
+    case 4418: ret = 14418; break;
+    case 4419: ret = 14419; break;
+    case 4420: ret = 14420; break;
+    case 4421: ret = 14421; break;
+    case 4422: ret = 14422; break;
+    case 4423: ret = 14423; break;
+    case 4424: ret = 14424; break;
+    case 4425: ret = 14425; break;
+    case 4426: ret = 14426; break;
+    case 4427: ret = 14427; break;
+    case 4428: ret = 14428; break;
+    case 4429: ret = 14429; break;
+    case 4430: ret = 14430; break;
+    case 4431: ret = 14431; break;
+    case 4432: ret = 14432; break;
+    case 4433: ret = 14433; break;
+    case 4434: ret = 14434; break;
+    case 4435: ret = 14435; break;
+    case 4436: ret = 14436; break;
+    case 4437: ret = 14437; break;
+    case 4438: ret = 14438; break;
+    case 4439: ret = 14439; break;
+    case 4440: ret = 14440; break;
+    case 4441: ret = 14441; break;
+    case 4442: ret = 14442; break;
+    case 4443: ret = 14443; break;
+    case 4444: ret = 14444; break;
+    case 4445: ret = 14445; break;
+    case 4446: ret = 14446; break;
+    case 4447: ret = 14447; break;
+    case 4448: ret = 14448; break;
+    case 4449: ret = 14449; break;
+    case 4450: ret = 14450; break;
+    case 4451: ret = 14451; break;
+    case 4452: ret = 14452; break;
+    case 4453: ret = 14453; break;
+    case 4454: ret = 14454; break;
+    case 4455: ret = 14455; break;
+    case 4456: ret = 14456; break;
+    case 4457: ret = 14457; break;
+    case 4458: ret = 14458; break;
+    case 4459: ret = 14459; break;
+    case 4460: ret = 14460; break;
+    case 4461: ret = 14461; break;
+    case 4462: ret = 14462; break;
+    case 4463: ret = 14463; break;
+    case 4464: ret = 14464; break;
+    case 4465: ret = 14465; break;
+    case 4466: ret = 14466; break;
+    case 4467: ret = 14467; break;
+    case 4468: ret = 14468; break;
+    case 4469: ret = 14469; break;
+    case 4470: ret = 14470; break;
+    case 4471: ret = 14471; break;
+    case 4472: ret = 14472; break;
+    case 4473: ret = 14473; break;
+    case 4474: ret = 14474; break;
+    case 4475: ret = 14475; break;
+    case 4476: ret = 14476; break;
+    case 4477: ret = 14477; break;
+    case 4478: ret = 14478; break;
+    case 4479: ret = 14479; break;
+    case 4480: ret = 14480; break;
+    case 4481: ret = 14481; break;
+    case 4482: ret = 14482; break;
+    case 4483: ret = 14483; break;
+    case 4484: ret = 14484; break;
+    case 4485: ret = 14485; break;
+    case 4486: ret = 14486; break;
+    case 4487: ret = 14487; break;
+    case 4488: ret = 14488; break;
+    case 4489: ret = 14489; break;
+    case 4490: ret = 14490; break;
+    case 4491: ret = 14491; break;
+    case 4492: ret = 14492; break;
+    case 4493: ret = 14493; break;
+    case 4494: ret = 14494; break;
+    case 4495: ret = 14495; break;
+    case 4496: ret = 14496; break;
+    case 4497: ret = 14497; break;
+    case 4498: ret = 14498; break;
+    case 4499: ret = 14499; break;
+    case 4500: ret = 14500; break;
+    case 4501: ret = 14501; break;
+    case 4502: ret = 14502; break;
+    case 4503: ret = 14503; break;
+    case 4504: ret = 14504; break;
+    case 4505: ret = 14505; break;
+    case 4506: ret = 14506; break;
+    case 4507: ret = 14507; break;
+    case 4508: ret = 14508; break;
+    case 4509: ret = 14509; break;
+    case 4510: ret = 14510; break;
+    case 4511: ret = 14511; break;
+    case 4512: ret = 14512; break;
+    case 4513: ret = 14513; break;
+    case 4514: ret = 14514; break;
+    case 4515: ret = 14515; break;
+    case 4516: ret = 14516; break;
+    case 4517: ret = 14517; break;
+    case 4518: ret = 14518; break;
+    case 4519: ret = 14519; break;
+    case 4520: ret = 14520; break;
+    case 4521: ret = 14521; break;
+    case 4522: ret = 14522; break;
+    case 4523: ret = 14523; break;
+    case 4524: ret = 14524; break;
+    case 4525: ret = 14525; break;
+    case 4526: ret = 14526; break;
+    case 4527: ret = 14527; break;
+    case 4528: ret = 14528; break;
+    case 4529: ret = 14529; break;
+    case 4530: ret = 14530; break;
+    case 4531: ret = 14531; break;
+    case 4532: ret = 14532; break;
+    case 4533: ret = 14533; break;
+    case 4534: ret = 14534; break;
+    case 4535: ret = 14535; break;
+    case 4536: ret = 14536; break;
+    case 4537: ret = 14537; break;
+    case 4538: ret = 14538; break;
+    case 4539: ret = 14539; break;
+    case 4540: ret = 14540; break;
+    case 4541: ret = 14541; break;
+    case 4542: ret = 14542; break;
+    case 4543: ret = 14543; break;
+    case 4544: ret = 14544; break;
+    case 4545: ret = 14545; break;
+    case 4546: ret = 14546; break;
+    case 4547: ret = 14547; break;
+    case 4548: ret = 14548; break;
+    case 4549: ret = 14549; break;
+    case 4550: ret = 14550; break;
+    case 4551: ret = 14551; break;
+    case 4552: ret = 14552; break;
+    case 4553: ret = 14553; break;
+    case 4554: ret = 14554; break;
+    case 4555: ret = 14555; break;
+    case 4556: ret = 14556; break;
+    case 4557: ret = 14557; break;
+    case 4558: ret = 14558; break;
+    case 4559: ret = 14559; break;
+    case 4560: ret = 14560; break;
+    case 4561: ret = 14561; break;
+    case 4562: ret = 14562; break;
+    case 4563: ret = 14563; break;
+    case 4564: ret = 14564; break;
+    case 4565: ret = 14565; break;
+    case 4566: ret = 14566; break;
+    case 4567: ret = 14567; break;
+    case 4568: ret = 14568; break;
+    case 4569: ret = 14569; break;
+    case 4570: ret = 14570; break;
+    case 4571: ret = 14571; break;
+    case 4572: ret = 14572; break;
+    case 4573: ret = 14573; break;
+    case 4574: ret = 14574; break;
+    case 4575: ret = 14575; break;
+    case 4576: ret = 14576; break;
+    case 4577: ret = 14577; break;
+    case 4578: ret = 14578; break;
+    case 4579: ret = 14579; break;
+    case 4580: ret = 14580; break;
+    case 4581: ret = 14581; break;
+    case 4582: ret = 14582; break;
+    case 4583: ret = 14583; break;
+    case 4584: ret = 14584; break;
+    case 4585: ret = 14585; break;
+    case 4586: ret = 14586; break;
+    case 4587: ret = 14587; break;
+    case 4588: ret = 14588; break;
+    case 4589: ret = 14589; break;
+    case 4590: ret = 14590; break;
+    case 4591: ret = 14591; break;
+    case 4592: ret = 14592; break;
+    case 4593: ret = 14593; break;
+    case 4594: ret = 14594; break;
+    case 4595: ret = 14595; break;
+    case 4596: ret = 14596; break;
+    case 4597: ret = 14597; break;
+    case 4598: ret = 14598; break;
+    case 4599: ret = 14599; break;
+    case 4600: ret = 14600; break;
+    case 4601: ret = 14601; break;
+    case 4602: ret = 14602; break;
+    case 4603: ret = 14603; break;
+    case 4604: ret = 14604; break;
+    case 4605: ret = 14605; break;
+    case 4606: ret = 14606; break;
+    case 4607: ret = 14607; break;
+    case 4608: ret = 14608; break;
+    case 4609: ret = 14609; break;
+    case 4610: ret = 14610; break;
+    case 4611: ret = 14611; break;
+    case 4612: ret = 14612; break;
+    case 4613: ret = 14613; break;
+    case 4614: ret = 14614; break;
+    case 4615: ret = 14615; break;
+    case 4616: ret = 14616; break;
+    case 4617: ret = 14617; break;
+    case 4618: ret = 14618; break;
+    case 4619: ret = 14619; break;
+    case 4620: ret = 14620; break;
+    case 4621: ret = 14621; break;
+    case 4622: ret = 14622; break;
+    case 4623: ret = 14623; break;
+    case 4624: ret = 14624; break;
+    case 4625: ret = 14625; break;
+    case 4626: ret = 14626; break;
+    case 4627: ret = 14627; break;
+    case 4628: ret = 14628; break;
+    case 4629: ret = 14629; break;
+    case 4630: ret = 14630; break;
+    case 4631: ret = 14631; break;
+    case 4632: ret = 14632; break;
+    case 4633: ret = 14633; break;
+    case 4634: ret = 14634; break;
+    case 4635: ret = 14635; break;
+    case 4636: ret = 14636; break;
+    case 4637: ret = 14637; break;
+    case 4638: ret = 14638; break;
+    case 4639: ret = 14639; break;
+    case 4640: ret = 14640; break;
+    case 4641: ret = 14641; break;
+    case 4642: ret = 14642; break;
+    case 4643: ret = 14643; break;
+    case 4644: ret = 14644; break;
+    case 4645: ret = 14645; break;
+    case 4646: ret = 14646; break;
+    case 4647: ret = 14647; break;
+    case 4648: ret = 14648; break;
+    case 4649: ret = 14649; break;
+    case 4650: ret = 14650; break;
+    case 4651: ret = 14651; break;
+    case 4652: ret = 14652; break;
+    case 4653: ret = 14653; break;
+    case 4654: ret = 14654; break;
+    case 4655: ret = 14655; break;
+    case 4656: ret = 14656; break;
+    case 4657: ret = 14657; break;
+    case 4658: ret = 14658; break;
+    case 4659: ret = 14659; break;
+    case 4660: ret = 14660; break;
+    case 4661: ret = 14661; break;
+    case 4662: ret = 14662; break;
+    case 4663: ret = 14663; break;
+    case 4664: ret = 14664; break;
+    case 4665: ret = 14665; break;
+    case 4666: ret = 14666; break;
+    case 4667: ret = 14667; break;
+    case 4668: ret = 14668; break;
+    case 4669: ret = 14669; break;
+    case 4670: ret = 14670; break;
+    case 4671: ret = 14671; break;
+    case 4672: ret = 14672; break;
+    case 4673: ret = 14673; break;
+    case 4674: ret = 14674; break;
+    case 4675: ret = 14675; break;
+    case 4676: ret = 14676; break;
+    case 4677: ret = 14677; break;
+    case 4678: ret = 14678; break;
+    case 4679: ret = 14679; break;
+    case 4680: ret = 14680; break;
+    case 4681: ret = 14681; break;
+    case 4682: ret = 14682; break;
+    case 4683: ret = 14683; break;
+    case 4684: ret = 14684; break;
+    case 4685: ret = 14685; break;
+    case 4686: ret = 14686; break;
+    case 4687: ret = 14687; break;
+    case 4688: ret = 14688; break;
+    case 4689: ret = 14689; break;
+    case 4690: ret = 14690; break;
+    case 4691: ret = 14691; break;
+    case 4692: ret = 14692; break;
+    case 4693: ret = 14693; break;
+    case 4694: ret = 14694; break;
+    case 4695: ret = 14695; break;
+    case 4696: ret = 14696; break;
+    case 4697: ret = 14697; break;
+    case 4698: ret = 14698; break;
+    case 4699: ret = 14699; break;
+    case 4700: ret = 14700; break;
+    case 4701: ret = 14701; break;
+    case 4702: ret = 14702; break;
+    case 4703: ret = 14703; break;
+    case 4704: ret = 14704; break;
+    case 4705: ret = 14705; break;
+    case 4706: ret = 14706; break;
+    case 4707: ret = 14707; break;
+    case 4708: ret = 14708; break;
+    case 4709: ret = 14709; break;
+    case 4710: ret = 14710; break;
+    case 4711: ret = 14711; break;
+    case 4712: ret = 14712; break;
+    case 4713: ret = 14713; break;
+    case 4714: ret = 14714; break;
+    case 4715: ret = 14715; break;
+    case 4716: ret = 14716; break;
+    case 4717: ret = 14717; break;
+    case 4718: ret = 14718; break;
+    case 4719: ret = 14719; break;
+    case 4720: ret = 14720; break;
+    case 4721: ret = 14721; break;
+    case 4722: ret = 14722; break;
+    case 4723: ret = 14723; break;
+    case 4724: ret = 14724; break;
+    case 4725: ret = 14725; break;
+    case 4726: ret = 14726; break;
+    case 4727: ret = 14727; break;
+    case 4728: ret = 14728; break;
+    case 4729: ret = 14729; break;
+    case 4730: ret = 14730; break;
+    case 4731: ret = 14731; break;
+    case 4732: ret = 14732; break;
+    case 4733: ret = 14733; break;
+    case 4734: ret = 14734; break;
+    case 4735: ret = 14735; break;
+    case 4736: ret = 14736; break;
+    case 4737: ret = 14737; break;
+    case 4738: ret = 14738; break;
+    case 4739: ret = 14739; break;
+    case 4740: ret = 14740; break;
+    case 4741: ret = 14741; break;
+    case 4742: ret = 14742; break;
+    case 4743: ret = 14743; break;
+    case 4744: ret = 14744; break;
+    case 4745: ret = 14745; break;
+    case 4746: ret = 14746; break;
+    case 4747: ret = 14747; break;
+    case 4748: ret = 14748; break;
+    case 4749: ret = 14749; break;
+    case 4750: ret = 14750; break;
+    case 4751: ret = 14751; break;
+    case 4752: ret = 14752; break;
+    case 4753: ret = 14753; break;
+    case 4754: ret = 14754; break;
+    case 4755: ret = 14755; break;
+    case 4756: ret = 14756; break;
+    case 4757: ret = 14757; break;
+    case 4758: ret = 14758; break;
+    case 4759: ret = 14759; break;
+    case 4760: ret = 14760; break;
+    case 4761: ret = 14761; break;
+    case 4762: ret = 14762; break;
+    case 4763: ret = 14763; break;
+    case 4764: ret = 14764; break;
+    case 4765: ret = 14765; break;
+    case 4766: ret = 14766; break;
+    case 4767: ret = 14767; break;
+    case 4768: ret = 14768; break;
+    case 4769: ret = 14769; break;
+    case 4770: ret = 14770; break;
+    case 4771: ret = 14771; break;
+    case 4772: ret = 14772; break;
+    case 4773: ret = 14773; break;
+    case 4774: ret = 14774; break;
+    case 4775: ret = 14775; break;
+    case 4776: ret = 14776; break;
+    case 4777: ret = 14777; break;
+    case 4778: ret = 14778; break;
+    case 4779: ret = 14779; break;
+    case 4780: ret = 14780; break;
+    case 4781: ret = 14781; break;
+    case 4782: ret = 14782; break;
+    case 4783: ret = 14783; break;
+    case 4784: ret = 14784; break;
+    case 4785: ret = 14785; break;
+    case 4786: ret = 14786; break;
+    case 4787: ret = 14787; break;
+    case 4788: ret = 14788; break;
+    case 4789: ret = 14789; break;
+    case 4790: ret = 14790; break;
+    case 4791: ret = 14791; break;
+    case 4792: ret = 14792; break;
+    case 4793: ret = 14793; break;
+    case 4794: ret = 14794; break;
+    case 4795: ret = 14795; break;
+    case 4796: ret = 14796; break;
+    case 4797: ret = 14797; break;
+    case 4798: ret = 14798; break;
+    case 4799: ret = 14799; break;
+    case 4800: ret = 14800; break;
+    case 4801: ret = 14801; break;
+    case 4802: ret = 14802; break;
+    case 4803: ret = 14803; break;
+    case 4804: ret = 14804; break;
+    case 4805: ret = 14805; break;
+    case 4806: ret = 14806; break;
+    case 4807: ret = 14807; break;
+    case 4808: ret = 14808; break;
+    case 4809: ret = 14809; break;
+    case 4810: ret = 14810; break;
+    case 4811: ret = 14811; break;
+    case 4812: ret = 14812; break;
+    case 4813: ret = 14813; break;
+    case 4814: ret = 14814; break;
+    case 4815: ret = 14815; break;
+    case 4816: ret = 14816; break;
+    case 4817: ret = 14817; break;
+    case 4818: ret = 14818; break;
+    case 4819: ret = 14819; break;
+    case 4820: ret = 14820; break;
+    case 4821: ret = 14821; break;
+    case 4822: ret = 14822; break;
+    case 4823: ret = 14823; break;
+    case 4824: ret = 14824; break;
+    case 4825: ret = 14825; break;
+    case 4826: ret = 14826; break;
+    case 4827: ret = 14827; break;
+    case 4828: ret = 14828; break;
+    case 4829: ret = 14829; break;
+    case 4830: ret = 14830; break;
+    case 4831: ret = 14831; break;
+    case 4832: ret = 14832; break;
+    case 4833: ret = 14833; break;
+    case 4834: ret = 14834; break;
+    case 4835: ret = 14835; break;
+    case 4836: ret = 14836; break;
+    case 4837: ret = 14837; break;
+    case 4838: ret = 14838; break;
+    case 4839: ret = 14839; break;
+    case 4840: ret = 14840; break;
+    case 4841: ret = 14841; break;
+    case 4842: ret = 14842; break;
+    case 4843: ret = 14843; break;
+    case 4844: ret = 14844; break;
+    case 4845: ret = 14845; break;
+    case 4846: ret = 14846; break;
+    case 4847: ret = 14847; break;
+    case 4848: ret = 14848; break;
+    case 4849: ret = 14849; break;
+    case 4850: ret = 14850; break;
+    case 4851: ret = 14851; break;
+    case 4852: ret = 14852; break;
+    case 4853: ret = 14853; break;
+    case 4854: ret = 14854; break;
+    case 4855: ret = 14855; break;
+    case 4856: ret = 14856; break;
+    case 4857: ret = 14857; break;
+    case 4858: ret = 14858; break;
+    case 4859: ret = 14859; break;
+    case 4860: ret = 14860; break;
+    case 4861: ret = 14861; break;
+    case 4862: ret = 14862; break;
+    case 4863: ret = 14863; break;
+    case 4864: ret = 14864; break;
+    case 4865: ret = 14865; break;
+    case 4866: ret = 14866; break;
+    case 4867: ret = 14867; break;
+    case 4868: ret = 14868; break;
+    case 4869: ret = 14869; break;
+    case 4870: ret = 14870; break;
+    case 4871: ret = 14871; break;
+    case 4872: ret = 14872; break;
+    case 4873: ret = 14873; break;
+    case 4874: ret = 14874; break;
+    case 4875: ret = 14875; break;
+    case 4876: ret = 14876; break;
+    case 4877: ret = 14877; break;
+    case 4878: ret = 14878; break;
+    case 4879: ret = 14879; break;
+    case 4880: ret = 14880; break;
+    case 4881: ret = 14881; break;
+    case 4882: ret = 14882; break;
+    case 4883: ret = 14883; break;
+    case 4884: ret = 14884; break;
+    case 4885: ret = 14885; break;
+    case 4886: ret = 14886; break;
+    case 4887: ret = 14887; break;
+    case 4888: ret = 14888; break;
+    case 4889: ret = 14889; break;
+    case 4890: ret = 14890; break;
+    case 4891: ret = 14891; break;
+    case 4892: ret = 14892; break;
+    case 4893: ret = 14893; break;
+    case 4894: ret = 14894; break;
+    case 4895: ret = 14895; break;
+    case 4896: ret = 14896; break;
+    case 4897: ret = 14897; break;
+    case 4898: ret = 14898; break;
+    case 4899: ret = 14899; break;
+    case 4900: ret = 14900; break;
+    case 4901: ret = 14901; break;
+    case 4902: ret = 14902; break;
+    case 4903: ret = 14903; break;
+    case 4904: ret = 14904; break;
+    case 4905: ret = 14905; break;
+    case 4906: ret = 14906; break;
+    case 4907: ret = 14907; break;
+    case 4908: ret = 14908; break;
+    case 4909: ret = 14909; break;
+    case 4910: ret = 14910; break;
+    case 4911: ret = 14911; break;
+    case 4912: ret = 14912; break;
+    case 4913: ret = 14913; break;
+    case 4914: ret = 14914; break;
+    case 4915: ret = 14915; break;
+    case 4916: ret = 14916; break;
+    case 4917: ret = 14917; break;
+    case 4918: ret = 14918; break;
+    case 4919: ret = 14919; break;
+    case 4920: ret = 14920; break;
+    case 4921: ret = 14921; break;
+    case 4922: ret = 14922; break;
+    case 4923: ret = 14923; break;
+    case 4924: ret = 14924; break;
+    case 4925: ret = 14925; break;
+    case 4926: ret = 14926; break;
+    case 4927: ret = 14927; break;
+    case 4928: ret = 14928; break;
+    case 4929: ret = 14929; break;
+    case 4930: ret = 14930; break;
+    case 4931: ret = 14931; break;
+    case 4932: ret = 14932; break;
+    case 4933: ret = 14933; break;
+    case 4934: ret = 14934; break;
+    case 4935: ret = 14935; break;
+    case 4936: ret = 14936; break;
+    case 4937: ret = 14937; break;
+    case 4938: ret = 14938; break;
+    case 4939: ret = 14939; break;
+    case 4940: ret = 14940; break;
+    case 4941: ret = 14941; break;
+    case 4942: ret = 14942; break;
+    case 4943: ret = 14943; break;
+    case 4944: ret = 14944; break;
+    case 4945: ret = 14945; break;
+    case 4946: ret = 14946; break;
+    case 4947: ret = 14947; break;
+    case 4948: ret = 14948; break;
+    case 4949: ret = 14949; break;
+    case 4950: ret = 14950; break;
+    case 4951: ret = 14951; break;
+    case 4952: ret = 14952; break;
+    case 4953: ret = 14953; break;
+    case 4954: ret = 14954; break;
+    case 4955: ret = 14955; break;
+    case 4956: ret = 14956; break;
+    case 4957: ret = 14957; break;
+    case 4958: ret = 14958; break;
+    case 4959: ret = 14959; break;
+    case 4960: ret = 14960; break;
+    case 4961: ret = 14961; break;
+    case 4962: ret = 14962; break;
+    case 4963: ret = 14963; break;
+    case 4964: ret = 14964; break;
+    case 4965: ret = 14965; break;
+    case 4966: ret = 14966; break;
+    case 4967: ret = 14967; break;
+    case 4968: ret = 14968; break;
+    case 4969: ret = 14969; break;
+    case 4970: ret = 14970; break;
+    case 4971: ret = 14971; break;
+    case 4972: ret = 14972; break;
+    case 4973: ret = 14973; break;
+    case 4974: ret = 14974; break;
+    case 4975: ret = 14975; break;
+    case 4976: ret = 14976; break;
+    case 4977: ret = 14977; break;
+    case 4978: ret = 14978; break;
+    case 4979: ret = 14979; break;
+    case 4980: ret = 14980; break;
+    case 4981: ret = 14981; break;
+    case 4982: ret = 14982; break;
+    case 4983: ret = 14983; break;
+    case 4984: ret = 14984; break;
+    case 4985: ret = 14985; break;
+    case 4986: ret = 14986; break;
+    case 4987: ret = 14987; break;
+    case 4988: ret = 14988; break;
+    case 4989: ret = 14989; break;
+    case 4990: ret = 14990; break;
+    case 4991: ret = 14991; break;
+    case 4992: ret = 14992; break;
+    case 4993: ret = 14993; break;
+    case 4994: ret = 14994; break;
+    case 4995: ret = 14995; break;
+    case 4996: ret = 14996; break;
+    case 4997: ret = 14997; break;
+    case 4998: ret = 14998; break;
+    case 4999: ret = 14999; break;
+    }
+    while (ret == 0) {
+      ret = 42;
+    }
+    return ret;
+  }
+  
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/MethodTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/MethodTest.java
new file mode 100644 (file)
index 0000000..76e114e
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+
+/**
+ * basic method invocation test
+ */
+
+
+interface TMI {
+  void gna();
+}
+
+class TestMethodBase extends TestJPF implements TMI {
+  
+  int baseData;
+
+  static int sData;
+  
+  static {
+    sData = -1;
+  }
+  
+  static void taz () {
+    sData = 24;
+  }
+  
+  TestMethodBase (int a) {
+    assert a == 42;
+    baseData = 42;
+  }
+
+  boolean baz (boolean p, byte b, char c, short s, int i, long l, float f, double d, Object o) {
+    assert p;
+    assert b == 4;
+    assert c == '?';
+    assert s == 42;
+    assert i == 4242;
+    assert l == 424242;
+    assert f == 4.2f;
+    assert d == 4.242;
+    assert o.equals(new Integer(42));
+
+    baseData = 44;
+
+    return p;
+  }
+  
+  void faz () {
+    gna();
+  }
+  
+  @Override
+  public void gna () {
+    baseData = 0;
+  }
+  
+  int har () {
+    return priv();
+  }
+  
+  private int priv () {
+    return 7;
+  }
+}
+
+public class MethodTest extends TestMethodBase {
+  
+  int data;
+  
+  static void taz () {
+    sData = 9;
+  }
+  
+  public MethodTest () {
+    super(42);
+    
+    data = 42;
+  }
+
+  MethodTest (int a) {
+    super(a);
+    
+    data = a;
+  }
+  
+  double foo (boolean p, byte b, char c, short s, int i, long l, float f, double d, Object o) {
+    assert p;
+    assert b == 4;
+    assert c == '?';
+    assert s == 42;
+    assert i == 4242;
+    assert l == 424242;
+    assert f == 4.2f;
+    assert d == 4.242;
+    assert o.equals(new Integer(42));
+
+    data = 43;
+
+    return d;
+  }
+
+  @Override
+  public void gna () {
+    baseData = 45;
+  }
+  
+  int priv () {
+    return 8;
+  }
+  
+  @Test
+  public void testCtor() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o1 = new MethodTest();
+      assert o1.data == 42;
+      assert o1.baseData == 42;
+
+      MethodTest o2 = new MethodTest(42);
+      assert o2.data == 42;
+      assert o2.baseData == 42;
+    }
+  }
+
+  @Test
+  public void testCall() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+      assert o.foo(true, (byte) 4, '?', (short) 42, 4242, 424242, 4.2f, 4.242, new Integer(42)) == 4.242;
+      assert o.data == 43;
+    }
+  }
+
+  @Test
+  public void testInheritedCall() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+      assert o.baz(true, (byte) 4, '?', (short) 42, 4242, 424242, 4.2f, 4.242, new Integer(42));
+      assert o.baseData == 44;
+    }
+  }
+
+  @Test
+  public void testVirtualCall() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+      TestMethodBase b = o;
+
+      b.faz();
+      assert o.baseData == 45;
+    }
+  }
+
+  @Test
+  public void testSpecialCall() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+      assert o.har() == 7;
+    }
+  }
+
+  @Test
+  public void testStaticCall() {
+    if (verifyNoPropertyViolation()) {
+      assert TestMethodBase.sData == -1;
+
+      MethodTest.taz();
+      assert TestMethodBase.sData == 9;
+
+      TestMethodBase.taz();
+      assert TestMethodBase.sData == 24;
+
+      // used to be:
+      //TestMethod o = new TestMethod();
+      //o.taz();
+      // statically equiv. to this: (no warnings) - pcd
+      new MethodTest();
+      MethodTest.taz();
+
+      assert TestMethodBase.sData == 9;
+    }
+  }
+
+  @Test
+  public void testInterfaceCall() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+      TMI ifc = o;
+
+      ifc.gna();
+      assert o.baseData == 45;
+    }
+  }
+  
+  static class A {
+    public int foo () {
+      assert false : "should bever be invoked";
+      return -1;
+    }
+  }
+  
+  static class A0 extends A {
+    @Override
+       public int foo () {
+      return 0;
+    }
+  }
+  
+  static class A1 extends A {
+    @Override
+       public int foo () {
+      return 1;
+    }
+  }
+    
+  /**
+   * this is tricky - both the allocations and the foo() calls have to be
+   * the same instructions, and we have to remove all 'a' references before
+   * forcing a GC. Otherwise we don't allocate 'a's with the same reference values 
+   *
+   */
+  @Test public void testCallAcrossGC () {
+
+    if (verifyNoPropertyViolation()){
+      System.out.println("testing virtual calls on GC boundaries");
+      A a;
+
+      a = new A0();
+      assert a.foo() == 0 : "wrong A.foo() called for A0";
+
+      // do a GC
+      Runtime.getRuntime().gc(); // this should recycle 'a'
+
+      a = new A1();
+      assert a.foo() == 1 : "wrong A.foo() called for A1";
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/OutOfMemoryErrorTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/OutOfMemoryErrorTest.java
new file mode 100644 (file)
index 0000000..085c469
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.TypeRef;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.ArrayList;
+
+import org.junit.Test;
+
+public class OutOfMemoryErrorTest extends TestJPF
+{
+   @Test
+   public void outOfMemoryErrorFails()
+   {
+      ArrayList<byte[]> hold;
+      byte hog[];
+      
+      if (verifyPropertyViolation(new TypeRef("gov.nasa.jpf.vm.NoOutOfMemoryErrorProperty")))
+      {
+         hold = new ArrayList<byte[]>();
+         
+         while (true)
+         {
+            hog = new byte[1024 * 1024 * 1024];
+            
+            hold.add(hog);
+         }
+      }
+   }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/basic/RecursiveClinitTest.java b/src/tests/gov/nasa/jpf/test/vm/basic/RecursiveClinitTest.java
new file mode 100644 (file)
index 0000000..f94eff2
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.basic;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import org.junit.Test;
+
+/**
+ * test automatic and recursive clinit invocation
+ */
+public class RecursiveClinitTest extends TestJPF {
+
+  static class Base {
+    static int d = 1;
+    static {
+      System.out.println("Base clinit");
+    }
+  }
+
+  static class Derived extends Base {
+    static int d = Base.d * 42;
+    static {
+      System.out.println("Derived clinit");
+    }
+    
+    public Derived (int i){
+      System.out.println("Derived(" + i + ')');
+    }
+    
+    public static void foo(){
+      System.out.println("Derived.foo()");
+    }
+  }
+
+  @Test 
+  public void testStaticField (){
+    if (verifyNoPropertyViolation()) {
+      System.out.println("main now referencing Derived.d");
+      int d = Derived.d;
+      System.out.println("back in main");
+      
+      assertTrue(d == 42);
+    }
+  }
+  
+  @Test
+  public void testNewInstance (){
+    if (verifyNoPropertyViolation()) {
+      System.out.println("main now calling Derived.class.newInstance()");
+      try {
+        Derived.class.newInstance();
+      } catch (Throwable t) {
+        fail("instantiation failed with " + t);
+      }
+      System.out.println("back in main");
+      
+      assertTrue(Derived.d == 42);
+    }
+  }
+  
+  @Test
+  public void testMethodReflection (){
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> clazz = Class.forName("gov.nasa.jpf.test.vm.basic.RecursiveClinitTest$Derived");
+        System.out.println("main now calling Derived.foo()");
+        Method m = clazz.getDeclaredMethod("foo", new Class[0]);
+        m.invoke(null);
+        
+        System.out.println("back in main");
+        assertTrue(Derived.d == 42);
+        
+      } catch (Throwable t){
+        fail("test failed with: " + t);
+      }
+    }    
+  }
+  
+  @Test
+  public void testCtorReflection (){
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> clazz = Class.forName("gov.nasa.jpf.test.vm.basic.RecursiveClinitTest$Derived");
+        System.out.println("main now creating Derived(-42)");
+        Constructor ctor = clazz.getConstructor(new Class[] {int.class});
+        Object o = ctor.newInstance( new Object[] {Integer.valueOf(-42)});
+        
+        System.out.println("back in main");
+        assertTrue( o instanceof Derived);
+        assertTrue(Derived.d == 42);
+        
+      } catch (Throwable t){
+        fail("test failed with: " + t);
+      }
+    }    
+  }
+  
+  // <2do> we also need the SerializatinConstructor
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/ArrayTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/ArrayTest.java
new file mode 100644 (file)
index 0000000..5f44584
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.lang.reflect.Array;
+
+import org.junit.Test;
+
+/**
+ * Test class for primitive getters and setters in the <code>java.lang.reflect.Array</code> class.
+ *  
+ * @author Mirko Stojmenovic (mirko.stojmenovic@gmail.com)
+ * @author Igor Andjelkovic (igor.andjelkovic@gmail.com)
+ */
+public class ArrayTest extends TestJPF {
+
+  int[] arrayInt = new int[]{42};
+  double[] arrayDouble = new double[]{42.0};
+
+  @Test public void testArrayInt () {
+    if (verifyNoPropertyViolation()){
+      try {
+        int i = Array.getInt(arrayInt, 0);
+        assert i == 42;
+
+        Array.setInt(arrayInt, 0, 43);
+        assert arrayInt[0] == 43;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+  @Test public void testArrayDouble () {
+    if (verifyNoPropertyViolation()){
+      try {
+        double d = Array.getDouble(arrayDouble, 0);
+        assert d == 42.0;
+
+        Array.setDouble(arrayDouble, 0, 43.0);
+        assert arrayDouble[0] == 43.0;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/ConstructorTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/ConstructorTest.java
new file mode 100644 (file)
index 0000000..eff4e3b
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Constructor;
+
+import org.junit.Test;
+
+/**
+ * regression test for constructor reflection
+ */
+public class ConstructorTest extends TestJPF {
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A {
+    String value();
+  }
+  
+  static class Y {
+    @A("this is a superclass ctor annotation")
+    protected Y(){}
+  }
+  
+  static class X extends Y {
+    private String a;
+
+    @A("this is a ctor annotation")
+    public X (@A("this is a parameter annotation") String x) {
+      this.a = x;
+      System.out.println(x);
+    }
+  }
+
+  @Test
+  public void testConstructorCall() {
+    if (verifyNoPropertyViolation()){
+      try {
+        Class<X> cls = X.class;
+        Constructor<X> ctor = cls.getDeclaredConstructor(new Class<?>[] { String.class });
+
+        X x = ctor.newInstance("I'm an X");
+        
+        assertNotNull(x); 
+      } catch (Throwable t){
+        fail("ctor invocation failed: " + t);
+      }
+    }
+  }
+
+  static class I {
+    private Integer i;
+
+    public I(Integer i) {
+      this.i = i;
+    }
+  }
+
+  @Test
+  public void testConstructorCallInteger() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<I> cls = I.class;
+        Constructor<I> ctor = cls.getDeclaredConstructor(new Class<?>[] {Integer.class });
+
+        I obj = ctor.newInstance(42);
+        assertNotNull(obj);
+        assertEquals(new Integer(42), obj.i);
+      } catch (Throwable t) {
+        fail("ctor invocation with Integer failed: " + t);
+      }
+    }
+  }
+
+  
+  
+  @Test
+  public void testAnnotations(){
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<X> cls = X.class;
+        Constructor<X> ctor = cls.getDeclaredConstructor(new Class<?>[] { String.class });
+
+        Annotation[] ai = ctor.getDeclaredAnnotations();
+        assertTrue("no declared ctor annotations found", ai.length == 1);
+        
+        assertTrue("wrong ctor annotation type", ai[0] instanceof A);
+        System.out.printf("ctor annotation: " + ai[0]);
+        
+      } catch (Throwable t) {
+        fail("ctor.getDeclaredAnnotations() failed: " + t);
+      }
+    }    
+  }
+  
+  @Test
+  public void testParameterAnnotations(){
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<X> cls = X.class;
+        Constructor<X> ctor = cls.getDeclaredConstructor(new Class<?>[] { String.class });
+
+        Annotation[][] pai = ctor.getParameterAnnotations();
+        assertTrue("no ctor parameter annotations found", pai.length == 1);
+        
+        Annotation[] ai = pai[0];
+        assertTrue("wrong number of annotations for first ctor argument", ai.length == 1);
+        
+        assertTrue("wrong parameter annotation type", ai[0] instanceof A);
+        System.out.printf("ctor parameter annotation: " + ai[0]);
+        
+      } catch (Throwable t) {
+        fail("ctor.getParameterAnnotations() failed: " + t);
+      }
+    }        
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/FieldTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/FieldTest.java
new file mode 100644 (file)
index 0000000..8daddbc
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.lang.reflect.Field;
+
+import org.junit.Test;
+
+public class FieldTest extends TestJPF {
+
+  int instInt = 42;
+  double instDouble = 42.0;
+  double primField = 42.0;
+  Object refField = new Integer(42);
+  int[] arrayField = new int[]{42};
+
+  static int statInt = 43;
+
+  @Test
+  public void testInstanceInt() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("instInt");
+
+        int i = f.getInt(this);
+        assert i == 42;
+
+        f.setInt(this, 43);
+        assert instInt == 43;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+  @Test
+  public void testStaticInt() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("statInt");
+
+        int i = f.getInt(this);
+        assert i == 43;
+        System.out.println("statInt = " + i);
+
+        f.setInt(null, 44);
+        assert statInt == 44;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+
+  @Test
+  public void testInstanceDouble() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("instDouble");
+
+        double d = f.getDouble(this);
+        assert d == 42.0;
+
+        f.setDouble(this, 43.0);
+        assert instDouble == 43.0;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+  @Test
+  public void testSetPrimitive() {
+    if (verifyNoPropertyViolation()) {
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("primField");
+
+        double d = ((Double) f.get(this)).doubleValue();
+        assert d == 42.0;
+
+        f.set(this, new Double(43.0));
+        assert primField == 43.0;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+  @Test
+  public void testSetReference() {
+    if (verifyNoPropertyViolation()) {
+
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("refField");
+
+        Object o = f.get(this);
+        assert o == refField;
+
+        Object ob = new Double(43.0);
+        f.set(this, ob);
+        assert ob == refField;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+
+  @Test
+  public void testSetArray() {
+    if (verifyNoPropertyViolation()) {
+
+      try {
+        Class<?> cls = FieldTest.class;
+        Field f = cls.getField("arrayField");
+
+        int[] a = (int[]) f.get(this);
+        assert a == arrayField;
+
+        int[] ar = new int[]{43};
+        f.set(this, ar);
+        assert ar == arrayField;
+
+      } catch (Throwable t) {
+        assert false : "unexpected exception: " + t;
+      }
+    }
+  }
+  
+  //--- field enumeration
+  interface I {
+    static int I_S = 42;
+  }
+  
+  static class X implements I {
+    public int pub_x_i;  // 1
+    protected int prot_x_i;
+    public static int pub_x_s;  // 2
+    static int prot_x_s;
+  }
+
+  static class Y extends X {
+    public static int pub_y_s; // 3
+    static int prot_y_s;
+    public int pub_y_i;  // 4
+    protected int prot_y_i;
+  }
+
+  @Test
+  public void testGetFields(){
+    if (verifyNoPropertyViolation()){
+      Field[] publicFields = Y.class.getFields();
+      String[] fnames = {"pub_x_i", "pub_x_s", "pub_y_s", "pub_y_i", "I_S"};
+      
+      assertTrue("wrong number of public fields", publicFields.length == fnames.length);
+      
+      for (Field f : publicFields){
+        String fname = f.getName();
+        System.out.println(fname);
+        
+        for (int i=0; i<fnames.length; i++){  
+          if (fname.equals(fnames[i])){
+            fnames[i] = null;
+          }
+        }
+      }
+      
+      for (int i=0; i<fnames.length; i++){
+        if (fnames[i] != null){
+          fail("unseen field: " + fnames[i]);
+        }
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/MethodTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/MethodTest.java
new file mode 100644 (file)
index 0000000..bc65abc
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.lang.annotation.Annotation;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.junit.Test;
+
+public class MethodTest extends TestJPF {
+
+  private double m_data = 42.0;
+  private Object m_arg;
+
+  static class Boo {
+
+    static int d = 42;
+  }
+
+  static class Faz {
+
+    static int d = 4200;
+    
+    static private int foo (int a){
+      return a + 42;
+    }
+  }
+
+  static class SupC {
+
+    private int privateMethod() {
+      return -42;
+    }
+  }
+
+  static class SubC extends SupC {
+
+    public int privateMethod() {
+      return 42;
+    }
+  }
+
+  public Boo getBoo() {
+    return null;
+  }
+
+  public double foo(int a, double d, String s) {
+    System.out.printf("in foo( %d, %f, %s)\n", a,d,s);
+    
+    assert m_data == 42.0 : "wrong object data";
+    assert a == 3 : "wrong int parameter value";
+    assert d == 3.33 : "wrong double parameter value";
+    assert "Blah".equals(s) : "wrong String parameter value";
+
+    return 123.456;
+  }
+
+  @Test
+  public void testInstanceMethodInvoke() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+
+      try {
+        Class<?> cls = o.getClass();
+        Method m = cls.getMethod("foo", int.class, double.class, String.class);
+
+        Object res = m.invoke(o, new Integer(3), new Double(3.33), "Blah");
+        double d = ((Double) res).doubleValue();
+        System.out.println("foo returned " + d);
+
+        assert d == 123.456 : "wrong return value";
+
+      } catch (Throwable t) {
+        t.printStackTrace();
+
+        assert false : " unexpected exception: " + t;
+      }
+    }
+  }
+
+  public static int harr (int a){
+    System.out.printf("in harr(%d)\n", a);
+    
+    return a+1;
+  }
+  
+  @Test
+  public void testStaticMethodInvoke() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+
+      try {
+        Class<?> cls = o.getClass();
+        Method m = cls.getMethod("harr", int.class);
+
+        Object res = m.invoke(null, new Integer(41));
+        int r = (Integer)res;
+        System.out.println("harr returned " + r);
+
+        assert r == 42 : "wrong return value";
+
+      } catch (Throwable t) {
+        t.printStackTrace();
+
+        assert false : " unexpected exception: " + t;
+      }
+    }
+  }
+  
+  public static void doAlmostNothing(){
+    System.out.println("in doAlmostNothing");
+  }
+  
+  @Test
+  public void testNoArgStaticMethodInvoke() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+
+      try {
+        Class<?> cls = o.getClass();
+        Method m = cls.getMethod("doAlmostNothing");
+
+        Object res = m.invoke(null, (Object[])null);
+        System.out.println("doAlmostNothing returned " + res);
+
+        assert res == null : "wrong return value";
+
+      } catch (Throwable t) {
+        t.printStackTrace();
+
+        assert false : " unexpected exception: " + t;
+      }
+    }
+  }
+  
+  
+  
+  @Test
+  public void getPrivateMethod() throws NoSuchMethodException {
+    if (verifyUnhandledException(NoSuchMethodException.class.getName())) {
+      Integer.class.getMethod("toUnsignedString0", int.class, int.class);   // Doesn't matter which class we use.  It just needs to be a different class and a private method.
+    }
+  }
+
+  private static void privateStaticMethod() {
+  }
+
+  @Test
+  public void invokePrivateSameClass() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    privateStaticMethod();   // Get rid of IDE warning
+
+    if (verifyNoPropertyViolation()) {
+      Method m = getClass().getDeclaredMethod("privateStaticMethod");
+
+      m.invoke(null);
+    }
+  }
+
+  @Test
+  public void invokePrivateOtherClass() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyUnhandledException(IllegalAccessException.class.getName())) {
+      Method m = Faz.class.getDeclaredMethod("foo", int.class);
+
+      int res = (Integer)m.invoke(null, 5);
+      fail("should never get here");
+    }
+  }
+
+  @Test
+  public void invokePrivateOtherClassAccessible() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyNoPropertyViolation()) {
+      Method m = Faz.class.getDeclaredMethod("foo", int.class);
+
+      m.setAccessible(true);
+      int res = (Integer)m.invoke(null, 5);
+      assertTrue( res == 47);
+    }
+  }
+
+  @Test
+  public void invokePrivateSuperclass() throws SecurityException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyNoPropertyViolation()) {
+      Method aMethod = SupC.class.getDeclaredMethod("privateMethod");
+      aMethod.setAccessible(true);
+      assert ((Integer) aMethod.invoke(new SubC()) == -42) : "must call method from superclass";
+    }
+  }
+
+  @Test
+  public void getMethodCanFindNotify() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()) {
+      Integer.class.getMethod("notify");
+    }
+  }
+
+  @Test
+  public void getDeclaredMethodCantFindNotify() throws NoSuchMethodException {
+    if (verifyUnhandledException(NoSuchMethodException.class.getName())) {
+      Integer.class.getDeclaredMethod("notify");
+    }
+  }
+
+  public void publicMethod() {
+  }
+
+  @Test
+  public void invokeWrongThisType() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyUnhandledException(IllegalArgumentException.class.getName())) {
+      Method m = getClass().getMethod("publicMethod");
+
+      m.invoke(new Object());
+    }
+  }
+
+  @Test
+  public void invokeNullObject() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyUnhandledException(NullPointerException.class.getName())) {
+      Method m = getClass().getMethod("publicMethod");
+
+      m.invoke(null);
+    }
+  }
+
+  @Test
+  public void invokeWrongNumberOfArguments() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyUnhandledException(IllegalArgumentException.class.getName())) {
+      Method m = getClass().getMethod("publicMethod");
+
+      m.invoke(this, 5);
+    }
+  }
+
+  @Test
+  public void invokeWrongArgumentTypeReference() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    if (verifyUnhandledException(IllegalArgumentException.class.getName())) {
+      Method m = getClass().getMethod("boofaz", Boo.class, Faz.class);
+
+      m.invoke(this, new Faz(), new Boo());
+    }
+  }
+
+  public void throwThrowable() throws Throwable {
+    throw new Throwable("purposeful exception");
+  }
+
+  @Test
+  public void invokeInvocationTargetException() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+    Class<?> clazz;
+    Method method;
+
+    if (verifyUnhandledException(InvocationTargetException.class.getName())) {
+      clazz = getClass();
+      method = clazz.getMethod("throwThrowable");
+
+      method.invoke(this);
+    }
+  }
+
+  @Test
+  public void testReturnType() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+
+      try {
+        Class<?> cls = o.getClass();
+        Method m = cls.getMethod("getBoo");
+        Class<?> rt = m.getReturnType();
+        String s = rt.getName();
+
+        assert Boo.class.getName().equals(s) : "wrong return type: " + s;
+
+      } catch (Throwable t) {
+        t.printStackTrace();
+
+        assert false : " unexpected exception in Method.getReturnType(): " + t;
+      }
+    }
+  }
+
+  public void boofaz(Boo b, Faz f) {
+    b = null; // Get rid of IDE warning
+    f = null;
+  }
+
+  @Test
+  public void testParameterTypes() {
+    if (verifyNoPropertyViolation()) {
+      MethodTest o = new MethodTest();
+
+      try {
+        Class<?> cls = o.getClass();
+
+        for (Method m : cls.getMethods()) {
+          if (m.getName().equals("boofaz")) {
+            Class<?>[] pt = m.getParameterTypes();
+
+            assert Boo.class.getName().equals(pt[0].getName()) : "wrong parameter type 0: " + pt[0].getName();
+            assert Faz.class.getName().equals(pt[1].getName()) : "wrong parameter type 1: " + pt[1].getName();
+          }
+        }
+
+      } catch (Throwable t) {
+        t.printStackTrace();
+
+        assert false : " unexpected exception in Method.getParameterTypes(): " + t;
+      }
+    }
+  }
+
+  //--- argument value conversion tests
+  
+  static final Object[] testArgValues = {
+    Byte.valueOf((byte) 7),
+    Short.valueOf((short) 8),
+    Integer.valueOf(9),
+    Long.valueOf(10),
+    Float.valueOf(3.1415f),
+    Double.valueOf(3.14159),
+    Boolean.TRUE,
+    Character.valueOf('w'),
+    "hello",
+    null
+  };
+  
+  static final Object ILLEGAL = new Object(); // we use this to flag an IllegalArgumentException
+  
+  private void invokeTest (Method m, Object argValue, Object expected){
+    System.out.print(argValue);
+    System.out.print("=>");
+    try {
+      Object ret = m.invoke(this, argValue);
+      System.out.println(ret);
+      if (isJPFRun()) {
+        assertTrue( ((ret == null) && (expected == null)) || ret.equals(expected));
+      }
+      
+    } catch (IllegalArgumentException ix){
+      System.out.println("ILLEGAL");
+      if (isJPFRun()) {
+        assertTrue( expected == ILLEGAL);
+      }
+      
+    } catch (Throwable t){
+      fail("_test invocation failed for value = " + argValue + " with " + t);
+    }    
+  }
+
+  //--- boolean argument
+  
+  boolean _test (boolean v){
+    //System.out.println("-- test(boolean) got " + v);
+    return v;    
+  }
+  
+  @Test
+  public void argTestBoolean() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", boolean.class);
+      Object[] expected = { // all but Boolean throws
+          ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, Boolean.TRUE, ILLEGAL, ILLEGAL, ILLEGAL
+      };
+
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+  
+  //--- byte argument
+  byte _test(byte v){
+    //System.out.println("-- test(long) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestByte() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", byte.class);
+      Object[] expected = { // all but byte throws
+          Byte.valueOf((byte)7), ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }    
+  }
+  
+  //--- short argument
+  short _test(short v){
+    //System.out.println("-- test(short) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestShort() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", short.class);
+      Object[] expected = { // all but byte and short throws
+          Short.valueOf((short)7), Short.valueOf((short)8), ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }    
+  }
+
+  //--- char argument
+  char _test(char v){
+    //System.out.println("-- test(char) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestChar() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", char.class);
+      Object[] expected = { // all but char throws
+          ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, Character.valueOf('w'), ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }    
+  }
+
+
+  //--- int argument
+  int _test(int v){
+    //System.out.println("-- test(int) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestInt() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", int.class);
+      Object[] expected = { // all but byte, short, int and char throws
+          Integer.valueOf(7), Integer.valueOf(8), Integer.valueOf(9), ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL,
+          Integer.valueOf('w'), ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }    
+  }
+
+  
+  //--- long argument
+  long _test(long v){
+    //System.out.println("-- test(long) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestLong() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", long.class);
+      Object[] expected = {
+          Long.valueOf(7L),Long.valueOf(8L), Long.valueOf(9L), Long.valueOf(10L),
+          ILLEGAL, ILLEGAL, ILLEGAL, Long.valueOf('w'), ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+
+  //--- float argument
+  float _test(float v){
+    //System.out.println("-- test(float) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestFloat() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", float.class);
+      Object[] expected = {
+          Float.valueOf(7f), Float.valueOf(8f), Float.valueOf(9f), 
+          Float.valueOf(10f), Float.valueOf(3.1415f), ILLEGAL, ILLEGAL, 
+          Float.valueOf('w'), ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+
+  //--- double argument
+  double _test(double v){
+    //System.out.println("-- test(double) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestDouble() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", double.class);
+      Object[] expected = {
+          Double.valueOf(7.0), Double.valueOf(8.0), Double.valueOf(9.0), 
+          Double.valueOf(10.0), Double.valueOf(3.1415f), Double.valueOf(3.14159),
+          ILLEGAL, Double.valueOf('w'), ILLEGAL, ILLEGAL
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+
+  //--- String argument
+  String _test(String v){
+    //System.out.println("-- test(String) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestString() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", String.class);
+      Object[] expected = {
+          ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, ILLEGAL, "hello", null
+      };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+  
+  //--- Object argument
+  Object _test(Object v){
+    //System.out.println("-- test(String) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestObject() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", Object.class);
+      Object[] expected = testArgValues;
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+
+  //--- Number argument
+  Number _test(Number v){
+    //System.out.println("-- test(Number) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestNumber() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", Number.class);
+      Object[] expected = {
+          Byte.valueOf((byte) 7),
+          Short.valueOf((short) 8),
+          Integer.valueOf(9),
+          Long.valueOf(10),
+          Float.valueOf(3.1415f),
+          Double.valueOf(3.14159),
+          ILLEGAL,
+          ILLEGAL,
+          ILLEGAL,
+          null  // we already used the real null to flag an IllegalArgumentException
+       };
+      
+      for (int i=0; i<testArgValues.length; i++){
+        invokeTest( m, testArgValues[i], expected[i]);
+      }
+    }
+  }
+
+  //--- array argument
+  int[] _test(int[] v){
+    //System.out.println("-- test(int[]) got " + v);
+    return v;
+  }
+  
+  @Test
+  public void argTestIntArray() throws NoSuchMethodException {
+    if (verifyNoPropertyViolation()){
+      Method m = MethodTest.class.getDeclaredMethod("_test", int[].class);
+      Object[] testVals = {
+        new int[0],
+        new float[0],
+        "blah",
+        null
+      };
+      Object[] expected = {
+          testVals[0],
+          ILLEGAL,
+          ILLEGAL,
+          null
+       };
+      
+      for (int i=0; i<testVals.length; i++){
+        invokeTest( m, testVals[i], expected[i]);
+      }
+    }
+  }
+
+  
+  //--- parameter annotation reflection
+  
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface A {
+    String value();
+  }
+  
+  void noFoo() {}
+  void noFoo(int a) {}
+  void oneFoo (@A("arg 1")int a){}
+  void twoFoo (int a, @A("arg 2") int b){}
+  
+  @Test
+  public void testParameterAnnotations(){
+    if (verifyNoPropertyViolation()){
+      try {
+        Method mth;
+        Annotation[][] pai;
+        Class<MethodTest> cls = MethodTest.class;
+/**
+        mth = cls.getDeclaredMethod("noFoo");
+        pai = mth.getParameterAnnotations();
+        assertTrue("should return Annotation[0][] for noFoo()", pai != null && pai.length == 0);
+        
+        mth = cls.getDeclaredMethod("noFoo", int.class );
+        pai = mth.getParameterAnnotations();
+        assertTrue("should return Annotation[1][{}] for noFoo(int)", pai != null && pai.length == 1 
+            && ((pai[0] != null) && (pai[0].length == 0)));
+        System.out.println("noFoo(int) : " + pai[0]);
+**/
+        mth = cls.getDeclaredMethod("oneFoo", int.class);
+        pai = mth.getParameterAnnotations();
+        assertTrue("should return Annotation[1][{@A}] for oneFoo(int)", pai != null && pai.length == 1 
+            && ((pai[0] != null) && (pai[0].length == 1) && (pai[0][0] instanceof A)));
+        System.out.println("oneFoo(@A int) : " + pai[0][0]);
+
+        mth = cls.getDeclaredMethod("twoFoo", int.class, int.class);
+        pai = mth.getParameterAnnotations();
+        assertTrue("should return Annotation[1][{@A}{}] for twoFoo(int,int)", pai != null && pai.length == 2 
+            && ((pai[0] != null) && (pai[0].length == 0))
+            && ((pai[1] != null) && (pai[1].length == 1)  && (pai[1][0] instanceof A)));
+        System.out.println("twoFoo(int, @A int)  : " + pai[0] + ',' +  pai[1][0]);
+        
+        
+      } catch (Throwable t){
+        t.printStackTrace();
+        fail("retrieving parameter annotation failed: " + t);
+      }
+
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/ProxyTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/ProxyTest.java
new file mode 100644 (file)
index 0000000..f53af2b
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import org.junit.Test;
+
+/**
+ * regression test for java.lang.reflect.Proxy
+ */
+public class ProxyTest extends TestJPF {
+  
+  interface Ifc {
+    int foo (int a);
+  }
+  
+  public static class MyHandler implements InvocationHandler {
+    int data;
+    
+    MyHandler (int d){
+      data = d;
+    }
+    
+    @Override
+       public Object invoke (Object proxy, Method mth, Object[] args){
+      int a = (Integer)args[0];
+      System.out.println("proxy invoke of " + mth);
+      //System.out.println(" on " + proxy);
+      System.out.println(" with " + a);
+
+      return Integer.valueOf(data + a);
+    }
+  }
+
+  @Test
+  public void testBasicProxy (){
+    if (verifyNoPropertyViolation()){
+      MyHandler handler = new MyHandler(42);
+      Ifc proxy = (Ifc)Proxy.newProxyInstance( Ifc.class.getClassLoader(),
+                                               new Class[] { Ifc.class },
+                                               handler);
+
+      int res = proxy.foo(1);
+      System.out.println(res);
+      assertTrue( res == 43);
+    }
+  }
+  
+  //--------------- proxy for annotation
+  @Retention(RetentionPolicy.RUNTIME)
+  @interface AnnoIfc {
+    int baz();
+  }
+  
+  class AnnoHandler implements InvocationHandler {
+    @Override
+       public Object invoke (Object proxy, Method mth, Object[] args){
+      System.out.println("proxy invoke of " + mth);
+      return Integer.valueOf(42);
+    }
+  }
+  
+  @Test
+  public void testAnnoProxy (){
+    if (verifyNoPropertyViolation()){
+      InvocationHandler handler = new AnnoHandler();
+      AnnoIfc proxy = (AnnoIfc)Proxy.newProxyInstance( AnnoIfc.class.getClassLoader(),
+                                               new Class[] { AnnoIfc.class },
+                                               handler);
+
+      int res = proxy.baz();
+      System.out.println(res);
+      assertTrue( res == 42);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/reflection/ReflectionTest.java b/src/tests/gov/nasa/jpf/test/vm/reflection/ReflectionTest.java
new file mode 100644 (file)
index 0000000..b34a1f4
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.reflection;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+public class ReflectionTest extends TestJPF {
+
+  static class MyClass {
+    void bar(){
+      foo();
+    }
+
+    // compilation will cause a warning about internal proprietary API that cannot be suppressed, but we have to test this
+    // since it is still used by standard libs
+    void foo (){
+      Class<?> callerCls = sun.reflect.Reflection.getCallerClass(0); // that would be getCallerClass()
+      System.out.println("-- getCallerClass(0) = " + callerCls);
+      assertTrue(callerCls.getName().equals("sun.reflect.Reflection"));
+      
+      callerCls = sun.reflect.Reflection.getCallerClass(1); // foo()
+      System.out.println("-- getCallerClass(1) = " + callerCls);
+      assertTrue(callerCls.getName().equals("gov.nasa.jpf.test.vm.reflection.ReflectionTest$MyClass"));
+      
+      callerCls = sun.reflect.Reflection.getCallerClass(2); // bar()
+      System.out.println("-- getCallerClass(2) = " + callerCls);
+      assertTrue(callerCls.getName().equals("gov.nasa.jpf.test.vm.reflection.ReflectionTest$MyClass"));
+
+      callerCls = sun.reflect.Reflection.getCallerClass(3); // callIt()
+      System.out.println("-- getCallerClass(3) = " + callerCls);
+      assertTrue(callerCls.getName().equals("gov.nasa.jpf.test.vm.reflection.ReflectionTest"));
+      
+      // <2do> should also test Method.invoke skipping
+    }
+  }
+  
+  void callIt(){
+    MyClass o = new MyClass();
+    o.bar();
+  }
+  
+  @Test
+  public void testCallerClass() {
+    if (verifyNoPropertyViolation()){
+      callIt();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/InterruptTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/InterruptTest.java
new file mode 100644 (file)
index 0000000..7f134ff
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.concurrent.locks.LockSupport;
+
+import org.junit.Test;
+
+/**
+ * raw test for Thread.interrupt conformance
+ */
+public class InterruptTest extends TestJPF {
+
+  @Test public void testInterruptStatus () {
+    if (verifyNoPropertyViolation()) {
+      Thread t = Thread.currentThread();
+
+      assert !t.isInterrupted() : "initial interrupt status is set";
+
+      System.out.println("setting interrupt status");
+      t.interrupt();
+
+      assert t.isInterrupted() : "interrupt status not set";
+
+      System.out.println("query and clear interrupt status");
+      assert Thread.interrupted() : "interrupt status prematurely reset";
+
+      assert !Thread.interrupted() : "interrupt status wasn't reset";
+    }
+  }
+
+  @Test public void testWaitSyncInterrupt() {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+
+          // doesn't matter from where we interrupt (thread can interrupt itself)
+          Thread t = Thread.currentThread();
+          t.interrupt();
+
+          synchronized (this) {
+            // if we don't enter here, this should be reported as a deadlock
+
+            System.out.println("T waiting");
+            try {
+              wait(); // this should immediately throw, i.e. return
+              assert false : "wait() did not throw InterruptedException";
+            } catch (InterruptedException ix) {
+              ix.printStackTrace(); // should include Object.wait()
+              System.out.println("T interrupted, terminating");
+              assert !t.isInterrupted() : "throw didn't reset interrupt status";
+              return;
+            } catch (Throwable x) {
+              assert false : "wait did throw wrong exception: " + x;
+              return;
+            }
+          }
+          assert false : "should never get here";
+        }
+      };
+
+      Thread t1 = new Thread(r);
+
+      t1.interrupt(); // should have no effect - it's not runnable yet
+      assert !t1.isInterrupted() : "non-started thread has interrupt status set";
+
+      t1.start();
+      System.out.println("main terminated");
+    }
+  }
+
+  @Test public void testWaitAsyncInterrupt() {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (this) {
+            // if we don't enter here, this should be reported as a deadlock
+            try {
+              System.out.println("T waiting");
+              wait();
+              assert false : "wait() did not throw InterruptedException";
+            } catch (InterruptedException ix) {
+              ix.printStackTrace(); // should include Object.wait()
+              System.out.println("T interrupted, terminating");
+              assert !Thread.currentThread().isInterrupted() : "throw didn't reset interrupt status";
+              return;
+            } catch (Throwable x) {
+              assert false : "wait did throw wrong exception: " + x;
+              return;
+            }
+          }
+          assert false : "should never get here";
+        }
+      };
+
+      Thread t1 = new Thread(r);
+      t1.start();
+
+      // no matter if this is executed before or after t1 enters Object.wait()
+      // it should terminate it by throwing. In fact, JPF should explore both
+      // paths
+      System.out.println("main interrupting t1");
+      t1.interrupt();
+
+      System.out.println("main terminated");
+    }
+  }
+
+  boolean interrupted;
+  boolean waiting;
+
+  @Test public void testBlockedWaitAsyncInterrupt() {
+    if (verifyNoPropertyViolation()) {
+
+      interrupted = false;
+      waiting = false;
+
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (this) {
+            // if we don't enter here, this should be reported as a deadlock
+            try {
+              System.out.println("T waiting");
+              waiting = true;
+              wait();
+              assert false : "wait() did not throw InterruptedException";
+            } catch (InterruptedException ix) {
+              ix.printStackTrace(); // should include Object.wait()
+              assert !Thread.currentThread().isInterrupted() : "throw didn't reset interrupt status";
+              System.out.println("T interrupted, terminating");
+              interrupted = true;
+              return;
+            } catch (Throwable x) {
+              assert false : "wait did throw wrong exception: " + x;
+              return;
+            }
+          }
+          assert false : "should never get here";
+        }
+      };
+
+      Thread t1 = new Thread(r);
+      t1.start();
+
+      while (!waiting) {
+        Thread.yield();
+      }
+
+      synchronized (r) {
+        System.out.println("main interrupting t1");
+        t1.interrupt();
+
+        // t1 can't run before we release the lock
+        Thread.yield(); // this shouldn't reschedule, t1 is blocked
+        assert !interrupted : "t1 prematurely scheduled w/o acquiring the lock";
+        System.out.println("main terminated, t1 runnable again");
+      }
+    }
+  }
+
+  @Test public void testPark() {
+    if (verifyNoPropertyViolation()) {
+
+      interrupted = false;
+
+      Thread t1 = new Thread(new Runnable() {
+
+        @Override
+               public void run() {
+          System.out.println("T parking..");
+          LockSupport.park();
+          interrupted = true;
+          System.out.println("T terminated");
+        }
+      });
+
+      t1.start();
+
+      System.out.println("main interrupting");
+      t1.interrupt();
+
+      try {
+        System.out.println("main joining t1..");
+        t1.join();
+        System.out.println("main joined t1");
+      } catch (InterruptedException e) {
+        assert false : "t1.join() interrupted in main";
+      }
+
+      assert interrupted : "LockSupport.park() didn't get interrupted";
+      System.out.println("main terminated");
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/JoinTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/JoinTest.java
new file mode 100644 (file)
index 0000000..954d9b1
--- /dev/null
@@ -0,0 +1,716 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+/**
+ * regression test for various Thread.join scenarios
+ */
+public class JoinTest extends TestJPF {
+
+  static final String[] JPF_ARGS = { "+cg.threads.break_start=true",
+                                     "+cg.threads.break_yield=true",
+                                     "+vm.tree_output=false",
+                                     "+vm.path_output=true"};
+
+  @Test public void testSimpleJoin(){
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      try {
+        t.join();
+        assert !t.isAlive();
+        System.out.println("main returned from join");
+      } catch (InterruptedException x) {
+        fail("join() did throw InterruptedException");
+      }
+    }
+  }
+
+  @Test public void testNoRunnableSimpleJoin() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Thread t = new Thread() {
+        @Override
+               public synchronized void run() {
+          System.out.println("thread-0 run");
+        }
+      };
+
+      t.start();
+
+      try {
+        t.join();
+        assert !t.isAlive();
+        System.out.println("main returned from join");
+      } catch (InterruptedException x) {
+        fail("join() did throw InterruptedException");
+      }
+    }
+  }
+
+  static class SomeThread extends Thread {
+    Object o;
+
+    @Override
+       public void run() {
+      synchronized (this){
+        // this causes a transition break - write on a shared object while
+        // we still hold the lock
+        o = new Object();
+      }
+      System.out.println("thread-0 done");
+    }
+  }
+
+  @Test public void testBlockedJoin() {
+    if (verifyNoPropertyViolation("+cg.threads.break_start=true",
+                                  "+vm.storage.class=null")) {
+      Thread t = new SomeThread();
+
+      t.start();
+      System.out.println("main started thread-0");
+
+      try {
+        t.join();
+        assert !t.isAlive();
+        System.out.println("main returned from join");
+      } catch (InterruptedException x) {
+        fail("join() did throw InterruptedException");
+      }
+    }
+  }
+
+  @Test public void testJoinHoldingLock(){
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      try {
+        synchronized(t){
+          t.join();
+        }
+        System.out.println("main returned from join");
+      } catch (InterruptedException x) {
+        fail("join() did throw InterruptedException");
+      }
+    }
+  }
+
+
+  @Test public void testNotAliveJoin(){
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      // poor man's join
+      while (t.isAlive()){
+        Thread.yield();
+      }
+
+      try {
+        t.join();
+        System.out.println("main returned from join");
+      } catch (InterruptedException x) {
+        fail("join() did throw InterruptedException");
+      }
+    }
+  }
+
+  @Test public void testPreJoinInterrupt() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+        }
+      };
+
+      Thread.currentThread().interrupt();
+
+      Thread t = new Thread(r);
+      t.start();
+
+      try {
+        t.join();
+        fail("join() didn't throw InterruptedException");
+      } catch (InterruptedException x) {
+        System.out.println("caught InterruptedException");
+      }
+    }
+  }
+
+  @Test public void testInterruptedJoin() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      final Thread mainThread = Thread.currentThread();
+
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 interrupting main");
+          mainThread.interrupt();
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      try {
+        t.join();
+        fail("join() didn't throw InterruptedException");
+      } catch (InterruptedException x) {
+        System.out.println("caught InterruptedException");
+      }
+    }
+  }
+
+  @Test public void testJoinLoop() {
+    if (verifyDeadlock(JPF_ARGS)) {
+      try {
+        Thread.currentThread().join();
+        fail("main can't get here if waiting for itself");
+      } catch (InterruptedException ex) {
+        fail("thread cannot be interrupted");
+      }
+    }
+  }
+
+  @Test public void testMultipleJoins() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        final Thread t1 = new Thread() {
+          @Override
+               public void run() {
+            Thread.yield();
+          }
+        };
+        Thread t2 = new Thread() {
+          @Override
+               public void run() {
+            try {
+              t1.join();
+            } catch (InterruptedException e) {
+              fail("unexpected interrupt");
+            }
+          }
+        };
+
+        t1.start();
+        t2.start();
+        t1.join();
+        t2.join();
+
+        assert (!t1.isAlive() && !t2.isAlive());
+      } catch (Exception ex) {
+        fail("unexpected exception: " + ex);
+      }
+    }
+  }
+
+  @Test public void testJoinBeforeStart() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        Thread t = new Thread();
+        t.join();
+        System.out.println("join on not-yet-started thread has no effect");
+      } catch (Exception ex) {
+        fail(ex.getMessage());
+      }
+    }
+  }
+
+
+  @Test public void testJoinAfterNotify() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        final Thread t = new Thread() {
+          @Override
+               public void run() {
+            synchronized(this){
+              System.out.println("thread-0 notifying");
+              notifyAll(); // this should not get us out of the join()
+            }
+            System.out.println("thread-0 terminating");
+          }
+        };
+
+        t.start();
+        System.out.println("main joining..");
+        t.join();
+        System.out.println("main joined");
+
+        Verify.printPathOutput("main termination");
+        assert !t.isAlive();
+
+      } catch (Exception ex) {
+        fail("unexpected exception: " + ex);
+      }
+    }
+  }
+
+  @Test public void testJoinNotifyDeadlock() {
+    if (verifyDeadlock(JPF_ARGS)) {
+      try {
+        final Thread t = new Thread() {
+          @Override
+               public void run() {
+            synchronized(this){
+              System.out.println("thread-0 notifying");
+              notifyAll();
+
+              try {
+                System.out.println("thread-0 waiting");
+                wait();
+              } catch (InterruptedException ix){
+                System.out.println("unexpected interrupt");
+              }
+            }
+            System.out.println("thread-0 terminating");
+          }
+        };
+
+        t.start();
+        System.out.println("main joining..");
+        t.join();
+        System.out.println("main joined");
+
+        synchronized (t){
+          System.out.println("main notifying");
+          t.notify();
+        }
+
+        Verify.printPathOutput("main termination");
+        assert !t.isAlive();
+
+      } catch (Exception ex) {
+        fail("unexpected exception: " + ex);
+      }
+    }
+  }
+
+
+  @Test public void testRedundantJoin() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        Thread t = new Thread();
+
+        t.start();
+        t.join();
+        System.out.println("main returned from first join()");
+        t.join();
+        System.out.println("main returned from second join()");
+
+        assert (!t.isAlive());
+      } catch (Exception ex) {
+        fail(ex.getMessage());
+      }
+    }
+  }
+
+  @Test public void testJoinThreadSet(){
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      final Thread[] worker = new Thread[3];
+      worker[0] = new Thread(new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("worker[0] finished");
+        }
+      });
+      worker[1] = new Thread(new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("worker[1] finished");
+        }
+      });
+      worker[2] = new Thread(new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("worker[2] finished");
+        }
+      });
+
+      int nJoin = 0;
+      for (int i = 0; i < worker.length; i++) {
+        worker[i].start();
+        nJoin++;
+      }
+
+      while (nJoin > 0) {
+        for (int i = 0; i < worker.length; i++) {
+          if (worker[i] != null) {
+            try {
+              worker[i].join();
+            } catch (InterruptedException x) {
+              fail("unexpected interrupt");
+            }
+            nJoin--;
+            worker[i] = null;
+            System.out.println("main joined worker[" + i + "]");
+          }
+        }
+      }
+    }
+  }
+
+
+  @Test public void testRecursiveJoinThreadGroup() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+      Verify.resetCounter(2);
+      Verify.resetCounter(3);
+    }
+
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+
+      ThreadGroup workers = new ThreadGroup("workers");
+
+      Thread t = new Thread( workers, new Runnable(){
+        @Override
+               public void run() {
+          Thread t1 = new Thread( new Runnable(){
+            @Override
+                       public void run() {
+              Thread t11 = new Thread(new Runnable() {
+                @Override
+                               public void run() {
+                  System.out.println("t11 run");
+                  Verify.incrementCounter(0);
+                }
+              }, "t11");
+              t11.start();
+              System.out.println("t1 run");
+              Verify.incrementCounter(1);
+            }
+          }, "t1");
+          t1.start();
+
+          Thread t2 = new Thread( new Runnable(){
+            @Override
+                       public void run() {
+              System.out.println("t2 run");
+              Verify.incrementCounter(2);
+            }
+          }, "t2");
+          t2.start();
+          System.out.println("t run");
+          Verify.incrementCounter(3);
+        }
+      }, "t");
+      t.start();
+
+      try {
+        Thread[] actives = new Thread[10]; // the length is just a guess here
+        int nActives = workers.enumerate(actives, true);
+        System.out.println("main joining " + nActives + " active threads");
+
+        while (nActives > 0){
+          assert nActives < actives.length; // it has to be strictly less to know we've got all
+          for (int i=0; i<nActives; i++){
+            System.out.println("main joining: " + actives[i].getName());
+            actives[i].join();
+            System.out.println("main joined: " + actives[i].getName());
+          }
+          nActives = workers.enumerate(actives, true);
+          System.out.println("..main now joining " + nActives + " active threads");
+        }
+      } catch (Throwable x){
+        fail("unexpected exception: " + x);
+      }
+
+      System.out.println("main done");
+      Verify.printPathOutput("end");
+    }
+
+    if (!isJPFRun()){
+      // not an ideal test since we don't know if the threads are still alive
+      assert Verify.getCounter(0) > 0;
+      assert Verify.getCounter(1) > 0;
+      assert Verify.getCounter(2) > 0;
+      assert Verify.getCounter(3) > 0;
+    }
+  }
+
+
+  @Test
+  public void testInterruptThreadWaitingToJoin() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        class ChildThread extends Thread {
+          Thread toInterrupt;
+          public void setToInterrupt(Thread toInterrupt) {
+            this.toInterrupt = toInterrupt;
+          }
+          @Override
+               public void run() {
+            toInterrupt.interrupt();
+          }
+        }
+        final ChildThread child = new ChildThread();
+
+        class WaitingToJoinThread extends Thread {
+          @Override
+               public void run() {
+            try {
+              child.setToInterrupt(this);
+              child.start();
+              child.join();
+            } catch (InterruptedException ix) {
+              System.out.println("-- parent interrupted while child continues to run");
+              Verify.incrementCounter(0);
+            }
+          }
+        }
+
+        WaitingToJoinThread threadWaitingToJoin = new WaitingToJoinThread();
+
+        threadWaitingToJoin.start();
+        try {
+          threadWaitingToJoin.join();
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("main thread was interrupted");
+        }
+        try {
+          child.join();
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("main thread was interrupted");
+        }
+      } catch (Exception ex) {
+        throw new RuntimeException(ex.getMessage());
+      }
+    }
+
+    if (!isJPFRun()){
+      // at least one execution interrupts parent while child continues to run
+      assert Verify.getCounter(0) > 0;
+    }
+  }
+
+  @Test public void testTimeoutJoin () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+          Thread.yield();
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+      //Thread.yield();
+
+      try {
+        System.out.println("main joining..");
+        t.join(42);
+        System.out.println("main joined, t state: " + t.getState());
+
+        // we should get here for both terminated and non-terminated thread
+        switch (t.getState()) {
+          case TERMINATED:
+            System.out.println("got terminated case");
+            Verify.incrementCounter(0);
+            break;
+          case RUNNABLE:
+            System.out.println("got timedout case");
+            Verify.incrementCounter(1);
+            break;
+          default:
+            fail("infeasible thread state: " + t.getState());
+        }
+
+      } catch (InterruptedException ix) {
+        fail("main thread was interrupted");
+      }
+    }
+
+    if (!isJPFRun()) {
+      assert Verify.getCounter(0) > 0;
+      assert Verify.getCounter(1) > 0;
+    }
+  }
+
+  @Test public void testZeroTimeoutJoin () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      Runnable r = new Runnable() {
+        @Override
+               public void run() {
+          System.out.println("thread-0 run");
+          Thread.yield();
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+      //Thread.yield();
+
+      try {
+        System.out.println("main joining..");
+        t.join(0);
+        System.out.println("main joined, t state: " + t.getState());
+
+        // we should get here for both terminated and non-terminated thread
+        switch (t.getState()) {
+          case TERMINATED:
+            Verify.incrementCounter(0);
+            break;
+          case RUNNABLE:
+            Verify.incrementCounter(1);
+            break;
+          default:
+            fail("infeasible thread state: " + t.getState());
+        }
+
+      } catch (InterruptedException ix) {
+        fail("main thread was interrupted");
+      }
+    }
+
+    if (!isJPFRun()) {
+      assert Verify.getCounter(0) > 0;
+      assert Verify.getCounter(1) == 0;
+    }
+  }
+
+  @Test public void testNegativeTimeoutJoin() {
+    if (verifyNoPropertyViolation(JPF_ARGS)) {
+      try {
+        Thread t = new Thread();
+        t.join(-1);
+        fail("should never get here");
+      } catch (InterruptedException ix) {
+        fail("unexpected InterruptedException");
+      } catch (IllegalArgumentException ax){
+        System.out.println("caught " + ax);
+      }
+    }
+  }
+
+  @Test public void testNestedLocksJoin() {
+    if (verifyNoPropertyViolation(JPF_ARGS)){
+      Thread t1 = new Thread() {
+        @Override
+               public synchronized void run() {
+          System.out.println("t1 notifying");
+          notifyAll();
+
+          try{
+            System.out.println("t1 waiting");
+            wait();
+          } catch (InterruptedException ix){
+            System.out.println("t1 unexpectedly interrupted");
+          }
+
+          System.out.println("t1 terminating");
+        }
+      };
+
+      Thread t2 = new Thread() {
+        @Override
+               public synchronized void run() {
+          System.out.println("t2 notifying");
+          notifyAll();
+
+          try{
+            System.out.println("t2 waiting");
+            wait();
+          } catch (InterruptedException ix){
+            System.out.println("t2 unexpectedly interrupted");
+          }
+
+          System.out.println("t2 terminating");
+        }
+      };
+
+      synchronized (t2){
+        try {
+          t2.start();
+          
+          System.out.println("main waiting on t2");
+          t2.wait();
+
+          synchronized(t1){
+            t1.start();
+
+            System.out.println("main waiting on t1");
+            t1.wait();
+
+            System.out.println("main notifying t1");
+            t1.notify();
+          }
+
+          System.out.println("main joining t1");
+          t1.join();
+
+          System.out.println("main notifying t2");
+          t2.notify();
+
+          System.out.println("main joining t2");
+          t2.join();
+
+        } catch (InterruptedException ix){
+          System.out.println("main unexpectedly interrupted");
+        }
+      }
+
+      System.out.println("main terminating");
+      Verify.printPathOutput("main termination");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/LockedStackDepthTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/LockedStackDepthTest.java
new file mode 100644 (file)
index 0000000..f2483c4
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * Tests the functionality of gov.nasa.jpf.listener.LockedStackDepth
+ *
+ * It would be very difficult to put asserts in the test.  Hence, asserts are
+ * added to LockedStackDepth.
+ *
+ * Run all of the JPF tests with 
+ * listener+=,gov.nasa.jpf.listener.LockedStackDepth to take advantage of the 
+ * various tests.
+ */
+public class LockedStackDepthTest extends TestJPF {
+
+  private static final String LISTENER = "+listener+=,.listener.LockedStackDepth";
+
+  @Test
+  public void recursiveLock() {
+    if (verifyNoPropertyViolation(LISTENER)) {
+      synchronized (this) {
+        synchronized (this) {
+        }
+      }
+    }
+  }
+
+  @Test
+  public void waitRetainsDepth() throws InterruptedException {
+    if (verifyNoPropertyViolation(LISTENER)) {
+      synchronized (this) {
+        synchronized (this) {
+          wait(1);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void breadthFirstSearch() throws InterruptedException {
+    if (verifyNoPropertyViolation(LISTENER, "+search.class=gov.nasa.jpf.search.heuristic.BFSHeuristic")) {
+      synchronized (this) {
+        synchronized (this) {
+          wait(1);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void randomHeuristicSearch() throws InterruptedException {
+    if (verifyNoPropertyViolation(LISTENER, "+search.class=gov.nasa.jpf.search.heuristic.RandomHeuristic")) {
+      synchronized (this) {
+        synchronized (this) {
+          wait(1);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void hitSameStateThroughDifferentSearchPaths() {
+    if (verifyNoPropertyViolation(LISTENER)) {
+      Verify.getBoolean();
+
+      synchronized (this) {
+      }
+
+      Verify.getBoolean();
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/SuspendResumeTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/SuspendResumeTest.java
new file mode 100644 (file)
index 0000000..01ef1f3
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * regression test for suspend/resume
+ */
+@SuppressWarnings("deprecation")
+public class SuspendResumeTest extends TestJPF {
+
+  static boolean isRunning;
+  static boolean pass = false;
+  
+  static class T1 extends Thread {
+    @Override
+       public void run(){
+      System.out.println("t1 running");
+      isRunning = true;
+      while (!pass){
+        Thread.yield();
+      }
+      System.out.println("t1 terminating");
+    }
+  }
+
+  @Test
+  public void testBasicSuspendDeadlock(){
+    if (verifyDeadlock("+cg.threads.break_yield")) {
+      Thread t1 = new T1();
+      t1.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+
+      t1.suspend();
+      assertTrue(t1.getState() == Thread.State.RUNNABLE);
+
+      pass = true;
+      
+      // without resuming, T1 should not be scheduled again, despite being in a RUNNABLE state
+      //t1.resume();
+    }
+  }
+  
+  @Test
+  public void testBasicSuspendResume(){
+    if (verifyNoPropertyViolation("+cg.threads.break_yield")) {
+      Thread t1 = new T1();
+      t1.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+
+      System.out.println("main suspending t1");
+      t1.suspend();
+      assertTrue(t1.getState() == Thread.State.RUNNABLE);
+
+      pass = true;
+      
+      System.out.println("main resuming t1");
+      t1.resume();
+      try {
+        System.out.println("main joining t1");
+        t1.join();
+      } catch (InterruptedException ix){
+        fail("t1.join got interrupted");
+      }
+      
+      System.out.println("main terminating after t1.join");
+    }
+  }
+
+  //---------------
+  // this is the main reason to model suspend/resume, since suspension does
+  // *not* give up any held locks, and hence is very prone to creating deadlocks
+  
+  static class T2 extends Thread {
+    @Override
+       public synchronized void run(){
+      System.out.println("t2 running with lock");
+      isRunning = true;
+      while (!pass){
+        Thread.yield();
+      }
+      System.out.println("t2 terminating");
+    }
+  }
+  
+  @Test
+  public void testLockholderSuspendDeadlock(){
+
+    if (verifyDeadlock("+cg.threads.break_yield")) {
+      Thread t2 = new T2();
+      t2.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+
+      System.out.println("main suspending t2");
+      t2.suspend();
+      // now t2 should hold and never give up its lock 
+      
+      synchronized (t2){
+        fail("main should never get here");
+      }
+    }
+  }
+  
+  //------------
+  
+  static class T3 extends Thread {
+    @Override
+       public synchronized void run(){
+      System.out.println("t3 running");
+      isRunning = true;
+      try {
+        wait();
+      } catch (InterruptedException ix){
+        fail("t3 got interrupted");
+      }
+      System.out.println("t3 terminating");
+    }
+  }
+
+  @Test
+  public void testWaitingSuspendNotifyDeadlock(){
+    if (verifyDeadlock("+cg.threads.break_yield")) {
+      Thread t3 = new T3();
+      t3.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+      
+      synchronized (t3){
+        assertTrue( t3.getState() == Thread.State.WAITING);
+        
+        System.out.println("main suspending t3");
+        t3.suspend();
+        
+        System.out.println("main notifying t3");
+        t3.notify();
+        // t3 should be still suspended, despite being notified
+      }
+    }    
+  }
+  
+  @Test
+  public void testWaitingSuspendNotifyResume(){
+    if (verifyNoPropertyViolation("+cg.threads.break_yield")) {
+      Thread t3 = new T3();
+      t3.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+      
+      synchronized (t3){
+        assertTrue( t3.getState() == Thread.State.WAITING);
+        
+        System.out.println("main suspending t3");
+        t3.suspend();
+        
+        System.out.println("main notifying t3");
+        t3.notify();
+        // t3 should be still suspended, despite being notified
+        
+        System.out.println("main resuming t3");
+        t3.resume();
+        try {
+          System.out.println("main joining t3");
+          t3.join();
+        } catch (InterruptedException ix) {
+          fail("t3.join got interrupted");
+        }
+
+        System.out.println("main terminating after t3.join");
+      }
+    }    
+  }
+  
+  
+  //----------------
+  
+  static class T4 extends Thread {
+    @Override
+       public void run(){
+      System.out.println("t4 running ");
+      isRunning = true;
+      while (!pass){
+        Thread.yield();
+      }
+      
+      System.out.println("t4 trying to obtain lock");      
+      synchronized (this){
+        System.out.println("t4 obtained lock");
+      }
+      System.out.println("t4 terminating");
+    }
+  }
+  
+  @Test
+  public void testBlockSuspendUnblockDeadlock(){
+    if (verifyDeadlock("+cg.threads.break_yield")) {
+      Thread t4 = new T4();
+      t4.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+      
+      synchronized (t4){
+        pass = true;
+        
+        while (t4.getState() != Thread.State.BLOCKED){
+          Thread.yield();
+        }
+        
+        System.out.println("main suspending t4");
+        t4.suspend();
+      }
+      System.out.println("main released t4 lock");
+    }
+  }
+
+  
+  @Test
+  public void testBlockSuspendUnblockResume(){
+    if (verifyNoPropertyViolation("+cg.threads.break_yield")) {
+      Thread t4 = new T4();
+      t4.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+      
+      synchronized (t4){
+        pass = true;
+        
+        while (t4.getState() != Thread.State.BLOCKED){
+          Thread.yield();
+        }
+        
+        System.out.println("main suspending t4");
+        t4.suspend();
+      }
+      System.out.println("main released t4 lock");
+
+      System.out.println("main resuming t4");
+      t4.resume();
+      try {
+        System.out.println("main joining t4");
+        t4.join();
+      } catch (InterruptedException ix) {
+        fail("t4.join got interrupted");
+      }
+
+      System.out.println("main terminating after t4.join");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/ThreadExceptionHandlerTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/ThreadExceptionHandlerTest.java
new file mode 100644 (file)
index 0000000..05648d2
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/** Unit tests for the three levels of exception handlers that threads have. */
+public class ThreadExceptionHandlerTest extends TestJPF {
+
+  static int n = 0;
+
+  public final static void main(String[] testMethods) {
+    runTestsOfThisClass(testMethods);
+  }
+
+  class NPEHandler implements Thread.UncaughtExceptionHandler {
+
+    @Override
+       public void uncaughtException(Thread t, Throwable e) {
+      /**
+      System.out.println("--- in NPEHandler.uncaughtException");
+      System.out.print("   "); System.out.println(t);
+      System.out.print("   "); System.out.println(e);
+      assertTrue(e instanceof NullPointerException);
+      **/
+      n = 1;
+    }
+  }
+
+  class NPEHandler2 extends ThreadGroup {
+
+    public NPEHandler2(String name) {
+      super(name);
+    }
+
+    public NPEHandler2(ThreadGroup parent, String name) {
+      super(parent, name);
+    }
+
+    @Override
+       public void uncaughtException(Thread t, Throwable e) {
+      assertTrue(e instanceof NullPointerException);
+      n = 2;
+    }
+  }
+
+  class NPEHandler3 extends ThreadGroup {
+
+    public NPEHandler3(String name) {
+      super(name);
+    }
+
+    @Override
+       public void uncaughtException(Thread t, Throwable e) {
+      assertTrue(e instanceof NullPointerException);
+      n = 3;
+    }
+  }
+
+  class NPEHandler4 extends ThreadGroup {
+    public NPEHandler4(ThreadGroup parent, String name) {
+      super(parent, name);
+    }
+
+    // this one doesn't override uncaughtException()
+  }
+
+  class NPEHandlerExc implements Thread.UncaughtExceptionHandler {
+
+    @Override
+       public void uncaughtException(Thread t, Throwable e) {
+      throw new NullPointerException("test");
+    }
+  }
+  
+  class TestRunnable implements Runnable {
+
+    @Override
+       public void run() {
+      throw new NullPointerException();
+    }
+  }
+
+  class TestRunnable2 implements Runnable {
+
+    @Override
+       public void run() {
+    }
+  }
+
+  /* DefaultHandler is null, the other two are equal */
+  @Test
+  public void checkDefaults() {
+    if (verifyNoPropertyViolation()){
+      Thread ct = Thread.currentThread();
+      assertEquals(ct.getUncaughtExceptionHandler(),
+              ct.getThreadGroup());
+      assertNull(Thread.getDefaultUncaughtExceptionHandler());
+    }
+  }
+
+  /* Test if handler gets executed */
+  @Test
+  public void testChildHandler() {
+    if (verifyNoPropertyViolation("+vm.ignore_uncaught_handler=false",
+                                  "+vm.pass_uncaught_handler")){
+      n = 0;
+      Thread w = new Thread(new TestRunnable());
+      w.setUncaughtExceptionHandler(new NPEHandler());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertEquals(n, 1);
+      n = 0;
+    }
+  }
+
+  /* Test if default handler gets executed */
+  /* The existing handlers should forward the exception to the newly
+   * set default handler. */
+  @Test
+  public void testChildDefaultHandler() {
+    if (verifyNoPropertyViolation("+vm.ignore_uncaught_handler=false",
+                                  "+vm.pass_uncaught_handler")){
+      n = 0;
+      Thread w = new Thread(new TestRunnable());
+      Thread.setDefaultUncaughtExceptionHandler(new NPEHandler());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertEquals(n, 1);
+      n = 0;
+    }
+  }
+
+  /* Handler information should be set to null after thread termination,
+   * except for custom default handler which does not get reset (this is
+   * poorly documented in official Java 1.6. */
+  @Test
+  public void testChildHandlerAfterTermination() {
+    if (verifyNoPropertyViolation()){
+      Thread w = new Thread(new TestRunnable2());
+      w.setUncaughtExceptionHandler(new NPEHandler());
+      Thread.setDefaultUncaughtExceptionHandler(new NPEHandler());
+      assertNotNull(w.getUncaughtExceptionHandler());
+      assertNotNull(w.getThreadGroup());
+      assertNotNull(Thread.getDefaultUncaughtExceptionHandler());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertNull(w.getUncaughtExceptionHandler());
+      assertNull(w.getThreadGroup());
+      assertNotNull(Thread.getDefaultUncaughtExceptionHandler());
+    }
+  }
+
+  /* The uncaughtHandler has precedence over any other handlers,
+   * including the thread group a thread belongs to. */
+  @Test
+  public void testPrecedence1() {
+    if (verifyNoPropertyViolation("+vm.ignore_uncaught_handler=false",
+                                  "+vm.pass_uncaught_handler")){
+      n = 0;
+      Thread w = new Thread(new NPEHandler2("test"), new TestRunnable());
+      w.setUncaughtExceptionHandler(new NPEHandler());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertEquals(n, 1);
+      n = 0;
+    }
+  }
+
+  /* The handler of a thread's ThreadGroup has precedence over
+   * the default handler. */
+  @Test
+  public void testPrecedence2() {
+    if (verifyNoPropertyViolation("+vm.ignore_uncaught_handler=false",
+                                  "+vm.pass_uncaught_handler")){
+      n = 0;
+      Thread w = new Thread(new NPEHandler2("test"), new TestRunnable()); // n = 2
+      Thread.setDefaultUncaughtExceptionHandler(new NPEHandler()); // n = 1
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertEquals(n, 2);
+      n = 0;
+    }
+  }
+
+  /* The handler of a ThreadGroup's parent has precedence over
+   * the child ThreadGroup and the default handler. */
+  @Test
+  public void testPrecedence3() {
+    if (verifyNoPropertyViolation("+vm.ignore_uncaught_handler=false",
+                                  "+vm.pass_uncaught_handler")){
+      n = 0;
+      Thread w =
+              new Thread(new NPEHandler4(new NPEHandler3("parent"), "child"),
+              new TestRunnable());
+      Thread.setDefaultUncaughtExceptionHandler(new NPEHandler());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+      assertEquals(n, 3);
+      n = 0;
+    }
+  }
+
+  /* Custom exception handler throws (unhandled) exception */
+  @Test
+  public void testHandlerThrowsExc() {
+    if (verifyUnhandledException("java.lang.NullPointerException",
+                                "+vm.ignore_uncaught_handler=false",
+                                 "+vm.pass_uncaught_handler")){
+      Thread w = new Thread(new TestRunnable());
+      Thread.setDefaultUncaughtExceptionHandler(new NPEHandlerExc());
+      w.start();
+      try {
+        w.join();
+      } catch (InterruptedException e) {
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/ThreadStopTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/ThreadStopTest.java
new file mode 100644 (file)
index 0000000..00d9cb8
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+
+import gov.nasa.jpf.annotation.NeverBreak;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+/**
+ * regression test for Thread.stop()
+ */
+@SuppressWarnings("deprecation")
+public class ThreadStopTest extends TestJPF {
+
+  @Test
+  public void testStopNewThread(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation()){
+      Thread t = new Thread(){
+        @Override
+               public void run(){
+           Verify.println("# t running, that's bad");
+          fail("t should never run");
+        }
+      };
+
+      t.stop();
+
+       Verify.println("# now starting the stopped thread");
+      t.start();
+      Thread.yield();
+      assertFalse( "t not terminated yet", t.isAlive());
+       Verify.println("# main got past start of stopped thread");
+      Verify.incrementCounter(0);
+
+    } else {
+      assertTrue( "main did not get past starting stopped thread", Verify.getCounter(0) > 0);
+    }
+  }
+
+  @Test
+  public void testStopNewSyncThread(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+    }
+
+    if (verifyNoPropertyViolation()){
+      Thread t = new Thread(){
+        @Override
+               public synchronized void run(){
+           Verify.println("# t running, that's bad");
+          fail("t should never run");
+        }
+      };
+
+      t.stop();
+
+      synchronized (t){
+         Verify.println("# now starting the stopped thread");
+        t.start(); // this should terminate the thread
+         Verify.println("# main got past start of stopped sync thread");
+      }
+       Verify.println("# main released lock for stopped sync thread");
+      Thread.yield();
+      assertFalse( "t not terminated yet", t.isAlive());
+      Verify.incrementCounter(0);
+
+
+    } else {
+      assertTrue( "main did not get past starting stopped thread", Verify.getCounter(0) > 0);
+    }
+  }
+
+  @Test
+  public void testStopSelf(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()){
+      Thread t = new Thread(){
+        @Override
+               public synchronized void run(){
+          Verify.incrementCounter(1);
+
+           Verify.println("# t running");
+          foo();
+          fail("t should not have gotten past self stop");
+        }
+
+        void foo() {
+          stop();
+        }
+      };
+
+       Verify.println("# main now starting the thread");
+      t.start();
+
+      try {
+         Verify.println("# main now joining the thread..");
+        t.join();
+         Verify.println("# main joined thread");
+        assertFalse( "t not terminated yet", t.isAlive());
+        Verify.incrementCounter(0);
+
+      } catch (InterruptedException ix){
+        assert false : "main should not get an InterruptedException while joining";
+      }
+
+    } else {
+      assertTrue( "t did not run", Verify.getCounter(1) > 0);
+      assertTrue( "main did not get past joining stopped thread", Verify.getCounter(0) > 0);
+    }
+  }
+
+
+  // some sync helpers
+  @NeverBreak
+  static boolean isRunning;
+
+  @Test
+  public void testStopRunning () {
+    if (!isJPFRun()) {
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      isRunning = false;
+
+      Thread t = new Thread() {
+        @Override
+               public synchronized void run () {
+          isRunning = true;
+          Verify.incrementCounter(1);
+
+          Verify.println("# t running");
+
+          while (true) { // while(true) will cause no return insn !
+            // keep it alive
+            Thread.yield();
+          }
+        }
+      };
+
+      Verify.println("# main now starting t");
+      t.start();
+
+      while (!isRunning) {
+        Thread.yield();
+      }
+
+      Verify.println("# main now stopping t");
+      t.stop();
+
+      try {
+        Verify.println("# main now joining the stopped thread..");
+        t.join();
+        Verify.println("# main joined thread");
+        assertFalse("t not terminated yet", t.isAlive());
+        Verify.incrementCounter(0);
+
+      } catch (InterruptedException ix) {
+        assert false : "main should not get an InterruptedException while joining";
+      }
+
+    } else {
+      assertTrue("t did not run", Verify.getCounter(1) > 0);
+      assertTrue("main did not get past join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  @NeverBreak
+  static Object lock = new Object();
+
+  @Test
+  public void testStopBlocked () {
+    if (!isJPFRun()) {
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      isRunning = false;
+      Thread t = new Thread() {
+        @Override
+               public synchronized void run () {
+          isRunning = true;
+          Verify.incrementCounter(1);
+          Verify.println("# t running, now blocking on lock..");
+          synchronized (lock) {
+            fail("t should never get here");
+          }
+        }
+      };
+
+      synchronized (lock) {
+        Verify.println("# main now starting t");
+        t.start();
+
+        while (!isRunning) {
+          Thread.yield();
+        }
+        assertTrue("t not blocked", t.getState() == Thread.State.BLOCKED);
+
+        Verify.println("# main now stopping t");
+        t.stop();
+
+        assertTrue("t dead despite main not giving up lock", t.isAlive());
+
+        Verify.println("# main now releasing lock");
+      }
+
+      try {
+        Verify.println("# main now joining the stopped thread..");
+        t.join();
+        Verify.println("# main joined thread");
+        assertFalse("t not terminated yet", t.isAlive());
+        Verify.incrementCounter(0);
+
+      } catch (InterruptedException ix) {
+        assert false : "main should not get an InterruptedException while joining";
+      }
+
+    } else {
+      assertTrue("t did not run", Verify.getCounter(1) > 0);
+      assertTrue("main did not get past join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  @Test
+  public void testStopWaiting(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()){
+      isRunning = false;
+
+      Thread t = new Thread(){
+        @Override
+               public synchronized void run(){
+          isRunning = true;
+          Verify.incrementCounter(1);
+
+           Verify.println("# t running, now waiting on lock");
+
+          synchronized(lock){
+            try {
+              lock.wait();
+            } catch (InterruptedException ix){
+              fail("should not get interrupted");
+            }
+          }
+        }
+      };
+
+      Verify.println("# main now starting t");
+      t.start();
+
+      while (t.getState() != Thread.State.WAITING) {
+        Thread.yield();
+      }
+
+      Verify.println("# main now stopping t");
+      t.stop();
+
+      Thread.yield();
+      assertTrue("t dead despite main not notifying", t.isAlive());
+
+      Verify.println("# main now notifying");
+      synchronized (lock) {
+        lock.notifyAll();
+      }
+
+      try {
+         Verify.println("# main now joining the stopped thread..");
+        t.join();
+         Verify.println("# main joined thread");
+        assertFalse( "t not terminated yet", t.isAlive());
+        Verify.incrementCounter(0);
+
+      } catch (InterruptedException ix){
+        assert false : "main should not get an InterruptedException while joining";
+      }
+
+    } else {
+      assertTrue( "t did not run", Verify.getCounter(1) > 0);
+      assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  static boolean wasHandled;
+
+  @Test
+  public void testStopHandler() {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()){
+      isRunning = false;
+      wasHandled = false;
+
+      Thread t = new Thread(){
+        @Override
+               public synchronized void run(){
+          isRunning = true;
+          Verify.incrementCounter(1);
+
+           Verify.println("# t running, now waiting on lock");
+
+          try {
+            synchronized (lock) {
+              try {
+                lock.wait();
+              } catch (InterruptedException ix) {
+                fail("should not get interrupted");
+              }
+            }
+          } catch (ThreadDeath td){
+            // usually not a good style, but can happen for exit processing
+             Verify.println("# t caught ThreadDeath");
+            wasHandled = true;
+            throw td; // rethrow to continue with kill
+          }
+        }
+      };
+
+       Verify.println("# main now starting t");
+      t.start();
+
+      while (t.getState() != Thread.State.WAITING) {
+        Thread.yield();
+      }
+
+       Verify.println("# main now stopping t");
+      t.stop();
+
+      Thread.yield();
+      assertTrue("t dead despite main not notifying", t.isAlive());
+
+       Verify.println("# main now notifying");
+      synchronized (lock) {
+        lock.notifyAll();
+      }
+
+      try {
+         Verify.println("# main now joining the stopped thread..");
+        t.join();
+         Verify.println("# main joined thread");
+
+        assertFalse( "t not terminated yet", t.isAlive());
+        assertTrue("t did not handle ThreadDeath", wasHandled);
+
+        Verify.incrementCounter(0);
+
+      } catch (InterruptedException ix){
+        assert false : "main should not get an InterruptedException while joining";
+      }
+
+    } else {
+      assertTrue( "t did not run", Verify.getCounter(1) > 0);
+      assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  @Test
+  public void testStopTerminated(){
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()){
+      Thread t = new Thread(){
+        @Override
+               public void run(){
+          Verify.incrementCounter(1);
+           Verify.println("# t running");
+        }
+      };
+
+       Verify.println("# main now starting t");
+      t.start();
+
+      while (t.isAlive()){
+        Thread.yield();
+      }
+
+      assertFalse("t is a zombie", t.isAlive());
+
+       Verify.println("# main now stopping dead t");
+      t.stop();
+       Verify.println("# main survived stopping t");
+
+      assertFalse("t is a zombie", t.isAlive());
+      Verify.incrementCounter(0);
+
+    } else {
+      assertTrue( "t did not run", Verify.getCounter(1) > 0);
+      assertTrue( "main did not get past join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  static Thread waitee;
+
+  @Test
+  public void testStopJoin() {
+    // join() is a lockfree wait, which makes it interesting
+
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+      Verify.resetCounter(2);
+    }
+
+    if (verifyNoPropertyViolation()){
+      waitee = new Thread(){
+        @Override
+               public void run(){
+          Verify.incrementCounter(2);
+           Verify.println("# waitee running");
+          synchronized(lock){
+            try {
+               Verify.println("# waitee now waiting for main to signal..");
+              lock.wait();
+               Verify.println("# waitee terminating");
+            } catch (InterruptedException ix) {
+              fail("waitee should not get interrupted");
+            }
+          }
+        }
+      };
+      waitee.start();
+      while (waitee.getState() != Thread.State.WAITING){
+        Thread.yield();
+      }
+
+      Thread t = new Thread(){
+        @Override
+               public synchronized void run(){
+          Verify.incrementCounter(1);
+          try {
+             Verify.println("# t now joining waitee..");
+            waitee.join();
+
+            fail("t should never get here");
+
+          } catch (InterruptedException ix){
+            fail("t should not get interrupted");
+          }
+        }
+      };
+
+       Verify.println("# main now starting t");
+      t.start();
+
+      while (t.getState() != Thread.State.WAITING){
+        Thread.yield();
+      }
+
+      assertTrue("waitee is a zombie", waitee.isAlive());
+      assertTrue("t is a zombie", t.isAlive());
+
+       Verify.println("# main now stopping t");
+      t.stop();
+      assertTrue("t should not have terminated since waitee not notified yet", t.isAlive());
+
+
+       Verify.println("# main now notifying waitee");
+      synchronized(lock){
+        lock.notifyAll();
+      }
+
+      try {
+         Verify.println("# main now joining waitee");
+        waitee.join();
+      } catch (InterruptedException ix){
+        fail("main should not get interupted joining waitee");
+      }
+      assertFalse("waitee is a zombie", waitee.isAlive());
+
+      try {
+         Verify.println("# main now joining t");
+        t.join();
+      } catch (InterruptedException ix){
+        fail("main should not get interupted joining t");
+      }
+      assertFalse("t is a zombie", t.isAlive());
+
+      Verify.incrementCounter(0);
+
+    } else {
+      assertTrue( "waitee did not run", Verify.getCounter(2) > 0);
+      assertTrue( "t did not run", Verify.getCounter(1) > 0);
+      assertTrue( "main did not get past t join", Verify.getCounter(0) > 0);
+    }
+  }
+
+  // and a lot more.. (e.g. stopping interrupted or timedout threads)
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/ThreadTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/ThreadTest.java
new file mode 100644 (file)
index 0000000..a9757b9
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.annotation.FilterField;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import org.junit.Test;
+
+
+/**
+ * threading test
+ */
+public class ThreadTest extends TestJPF {
+  static String didRunThread = null;
+
+  @Test public void testDaemon () {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          Thread t = Thread.currentThread();
+
+          if (!t.isDaemon()) {
+            throw new RuntimeException("isDaemon failed");
+          }
+
+          didRunThread = t.getName();
+        }
+      };
+
+      didRunThread = null;
+
+      Thread t = new Thread(r);
+      t.setDaemon(true);
+      t.start();
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("thread was interrupted");
+      }
+
+      String tname = Thread.currentThread().getName();
+      if ((didRunThread == null) || (didRunThread.equals(tname))) {
+        throw new RuntimeException("thread did not execute: " + didRunThread);
+      }
+    }
+  }
+
+  @Test public void testDaemonTermination () {
+    if (verifyNoPropertyViolation("+cg.threads.break_start=true",
+                                  "+cg.threads.break_yield=true")) {
+      final Thread mainThread = Thread.currentThread();
+
+      Runnable r = new Runnable() {
+
+        @FilterField  // without this, we have a perfectly open state space and never finish in JPF
+        int n = 0;
+
+        @Override
+               public void run() {
+          while (true) { // loop forever or until main finishes
+            n++;
+
+            // NOTE: this does not necessarily hold outside of JPF, since the daemon might still run for a few cycles
+            assert (n < 100) || mainThread.isAlive() : "main terminated but daemon still running";
+            System.out.println("  daemon running in round: " + n);
+
+            Thread.yield();
+          }
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.setDaemon(true);
+      t.start();
+
+      Thread.yield();
+      // finishing this thread should also (eventually) terminate the daemon
+      System.out.println("main terminating");
+    }
+  }
+  
+  @Test public void testMain () {
+    if (verifyNoPropertyViolation()) {
+      Thread t = Thread.currentThread();
+      String refName = "main";
+
+      String name = t.getName();
+
+      if (!name.equals(refName)) {
+        throw new RuntimeException("wrong main thread name, is: " + name +
+                ", expected: " + refName);
+      }
+
+      refName = "my-main-thread";
+      t.setName(refName);
+      name = t.getName();
+
+      if (!name.equals(refName)) {
+        throw new RuntimeException("Thread.setName() failed, is: " + name +
+                ", expected: " + refName);
+      }
+
+      int refPrio = Thread.NORM_PRIORITY;
+      int prio = t.getPriority();
+
+      if (prio != refPrio) {
+        throw new RuntimeException("main thread has no NORM_PRIORITY: " + prio);
+      }
+
+      refPrio++;
+      t.setPriority(refPrio);
+      prio = t.getPriority();
+
+      if (prio != refPrio) {
+        throw new RuntimeException("main thread setPriority failed: " + prio);
+      }
+
+      if (t.isDaemon()) {
+        throw new RuntimeException("main thread is daemon");
+      }
+    }
+  }
+
+  @Test public void testName () {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          Thread t = Thread.currentThread();
+          String name = t.getName();
+
+          if (!name.equals("my-thread")) {
+            throw new RuntimeException("wrong Thread name: " + name);
+          }
+
+          didRunThread = name;
+        }
+      };
+
+      didRunThread = null;
+
+      Thread t = new Thread(r, "my-thread");
+
+
+      //Thread t = new Thread(r);
+      t.start();
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("thread was interrupted");
+      }
+
+      String tname = Thread.currentThread().getName();
+      if ((didRunThread == null) || (didRunThread.equals(tname))) {
+        throw new RuntimeException("thread did not execute: " + didRunThread);
+      }
+    }
+  }
+
+  public void testSingleYield () {
+    Thread.yield();
+  }
+  
+  @Test public void testYield () {
+    if (verifyNoPropertyViolation("+cg.threads.break_start=true",
+                                  "+cg.threads.break_yield=true")) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          Thread t = Thread.currentThread();
+
+          while (!didRunThread.equals("blah")) {
+            Thread.yield();
+          }
+
+          didRunThread = t.getName();
+        }
+      };
+
+      didRunThread = "blah";
+
+      Thread t = new Thread(r);
+      t.start();
+
+      while (didRunThread.equals("blah")) {
+        Thread.yield();
+      }
+
+      String tname = Thread.currentThread().getName();
+      if ((didRunThread == null) || (didRunThread.equals(tname))) {
+        throw new RuntimeException("thread did not execute: " + didRunThread);
+      }
+    }
+  }
+  
+  
+  @Test public void testPriority () {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          Thread t = Thread.currentThread();
+          int prio = t.getPriority();
+
+          if (prio != (Thread.MIN_PRIORITY + 2)) {
+            throw new RuntimeException("wrong Thread priority: " + prio);
+          }
+
+          didRunThread = t.getName();
+        }
+      };
+
+      didRunThread = null;
+
+      Thread t = new Thread(r);
+      t.setPriority(Thread.MIN_PRIORITY + 2);
+      t.start();
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("thread was interrupted");
+      }
+
+      String tname = Thread.currentThread().getName();
+      if ((didRunThread == null) || (didRunThread.equals(tname))) {
+        throw new RuntimeException("thread did not execute: " + didRunThread);
+      }
+    }
+  }
+  
+  @Test public void testJoin () {
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public synchronized void run() {
+          didRunThread = Thread.currentThread().getName();
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      synchronized (r) {
+        t.start();
+        Thread.yield();
+        if (didRunThread != null) {
+          throw new RuntimeException("sync thread did execute before lock release");
+        }
+      }
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main thread was interrupted");
+      }
+
+      if (didRunThread == null) {
+        throw new RuntimeException("sync thread did not run after lock release");
+      }
+    }
+  }
+
+  @Test public void testTimeoutJoin () {
+    if (!isJPFRun()){
+      Verify.resetCounter(0);
+      Verify.resetCounter(1);
+    }
+
+    if (verifyNoPropertyViolation()) {
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized(this){
+            System.out.println("[t] started");
+          }
+          didRunThread = Thread.currentThread().getName(); // this causes a CG
+          System.out.println("[t] finished");
+        }
+      };
+
+      Thread t = new Thread(r);
+
+      synchronized (r) {
+        t.start();
+        Thread.yield();
+        if (didRunThread != null) {
+          throw new RuntimeException("sync thread did execute before lock release");
+        }
+      }
+
+      try {
+        System.out.println("[main] joining..");
+        t.join(42);
+        System.out.println("[main] joined, t state: " + t.getState());
+
+        // we should get here for both terminated and non-terminated t
+        switch (t.getState()) {
+          case TERMINATED:
+            if (didRunThread != null){
+              Verify.incrementCounter(0);
+            }
+            break;
+          case RUNNABLE:
+            Verify.incrementCounter(1);
+            break;
+          default:
+            throw new RuntimeException("infeasible thread state: " + t.getState());
+        }
+
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main thread was interrupted");
+      }
+    }
+
+    if (!isJPFRun()){
+      assert Verify.getCounter(0) > 0;
+      assert Verify.getCounter(1) > 0;
+    }
+  }
+
+
+  @Test public void testInterrupt() {
+    if (verifyNoPropertyViolation()) {
+
+      Runnable r = new Runnable() {
+
+        @Override
+               public synchronized void run() {
+          try {
+            didRunThread = Thread.currentThread().getName();
+            System.out.println("-- t waiting");
+            wait();
+          } catch (InterruptedException x) {
+            System.out.println("-- t interrupted");
+            didRunThread = null;
+          }
+          System.out.println("-- t terminated");
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      while (didRunThread == null) {
+        Thread.yield();
+      }
+
+      synchronized (r) {
+        System.out.println("-- main thread interrupting...");
+        t.interrupt();
+      }
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main thread was interrupted");
+      }
+
+      if (didRunThread != null) {
+        throw new RuntimeException("t did not get interrupted");
+      }
+
+      System.out.println("-- main thread terminated");
+    }
+  }
+  
+  
+  @Test public void testSimpleThreadGroup () {
+    if (verifyNoPropertyViolation()) {
+
+      System.out.println("-- main thread started");
+
+      Thread mainThread = Thread.currentThread();
+      final ThreadGroup sysGroup = mainThread.getThreadGroup();
+
+
+      assert sysGroup != null && sysGroup.getName().equals("main");
+
+      int active = sysGroup.activeCount();
+      assert active == 1;
+      Thread[] list = new Thread[active];
+
+      int n = sysGroup.enumerate(list);
+      assert (n == active);
+      assert list[0] == mainThread;
+
+
+      Runnable r = new Runnable() {
+
+        @Override
+               public void run() {
+          System.out.println("-- t started");
+          didRunThread = Thread.currentThread().getName();
+
+          Thread t = Thread.currentThread();
+          ThreadGroup group = t.getThreadGroup();
+
+          assert group != null && group == sysGroup;
+
+          int active = group.activeCount();
+          assert active == 2;
+          Thread[] list = new Thread[active];
+
+          int n = group.enumerate(list);
+          assert (n == active);
+
+          assert list[1] == t;
+          System.out.println("-- t terminated");
+        }
+      };
+
+      Thread t = new Thread(r);
+      t.start();
+
+      try {
+        t.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main thread was interrupted");
+      }
+
+      if (didRunThread == null) {
+        throw new RuntimeException("t did not run");
+      }
+
+      System.out.println("-- main thread terminated");
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/vm/threads/WaitTest.java b/src/tests/gov/nasa/jpf/test/vm/threads/WaitTest.java
new file mode 100644 (file)
index 0000000..ecf75f7
--- /dev/null
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.vm.threads;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * signal test (wait/notify)
+ */
+public class WaitTest extends TestJPF
+{
+  int counter;
+  boolean cond;
+  boolean done;
+  
+  @Test public void testVerySimpleWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testVerySimpleWait()");
+      synchronized (this) {
+        try {
+          System.out.println("waiting");
+          wait(100L);
+          System.out.println("timed out");
+
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("got interrupted");
+        }
+      }
+    }
+  }
+
+  @Test public void testSimpleWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testSimpleWait()");
+
+      Runnable notifier = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (WaitTest.this) {
+            System.out.println("notifying");
+            cond = true;
+            WaitTest.this.notify();
+          }
+        }
+      };
+
+      Thread t = new Thread(notifier);
+
+      cond = false;
+
+      synchronized (this) {
+        t.start();
+
+        try {
+          System.out.println("waiting");
+          wait();
+          System.out.println("notified");
+          if (!cond) {
+            throw new RuntimeException("'cond' not set, premature wait return");
+          }
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("got interrupted");
+        }
+      }
+    }
+  }
+  
+  @Test public void testSyncRunWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testSyncRunWait()");
+
+      Runnable waiter = new Runnable() {
+
+        @Override
+               public synchronized void run() {
+          System.out.println("thread-0 running");
+          try {
+            wait(); // needs to be first insn
+            System.out.println("thread-0 notified");
+          } catch (InterruptedException ix) {
+            throw new RuntimeException("thread-0 got interrupted");
+          }
+        }
+      };
+
+      Thread t = new Thread(waiter);
+      t.setDaemon(true); // to make sure we don't get a deadlock
+      t.start();
+
+      synchronized (waiter) {
+        System.out.println("main notifying");
+        waiter.notify();
+      }
+    }
+  }
+
+
+
+  @Test public void testTimeoutWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testTimeoutWait()");
+
+      Runnable notifier = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (WaitTest.this) {
+            System.out.println("notifying");
+            cond = true;
+            WaitTest.this.notify();
+          }
+        }
+      };
+
+      Thread t = new Thread(notifier);
+
+      cond = false;
+
+      synchronized (this) {
+        if (false) {
+          t.start();
+        }
+
+        try {
+          System.out.println("waiting");
+          wait(1);
+          if (cond) {
+            System.out.println("got notified");
+          } else {
+            System.out.println("wait timed out");
+          }
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("got interrupted");
+        }
+      }
+    }
+  }
+
+  @Test public void testLoopedWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testLoopedWait()");
+
+      Runnable notifier = new Runnable() {
+
+        @Override
+               public void run() {
+          while (!done) {
+            synchronized (WaitTest.this) {
+              System.out.println("notifying");
+              cond = true;
+              WaitTest.this.notify();
+            }
+          }
+        }
+      };
+
+      Thread t = new Thread(notifier);
+
+      cond = false;
+      done = false;
+
+      t.start();
+      synchronized (this) {
+        for (int i = 0; i < 2; i++) {
+          try {
+            System.out.println("waiting "); //System.out.println(i);
+            wait();
+            System.out.println("notified "); //System.out.println(i);
+            if (!cond) {
+              throw new RuntimeException("'cond' not set, premature wait return");
+            }
+            cond = false;
+          } catch (InterruptedException ix) {
+            throw new RuntimeException("got interrupted");
+          }
+        }
+        done = true;
+      }
+    }
+  }
+  
+  @Test public void testInterruptedWait () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testInterruptedWait()");
+
+      final Thread current = Thread.currentThread();
+
+      Runnable notifier = new Runnable() {
+
+        @Override
+               public void run() {
+          synchronized (WaitTest.this) {
+            System.out.println("interrupting");
+            cond = true;
+            current.interrupt();
+          }
+        }
+      };
+      Thread t = new Thread(notifier);
+
+      cond = false;
+
+      synchronized (this) {
+        t.start();
+
+        try {
+          System.out.println("waiting");
+          wait();
+          System.out.println("notified");
+          throw new RuntimeException("notified, not interrupted");
+        } catch (InterruptedException ix) {
+          System.out.println("interrupted");
+          //System.out.println(cond);
+          if (!cond) {
+            throw new RuntimeException("'cond' not set, premature wait return");
+          }
+        }
+      }
+    }
+  }
+
+  class Waiter implements Runnable {
+    String name;
+    boolean waiting;
+    boolean done1;
+    
+    Waiter (String name) {
+      this.name = name; 
+      waiting = false;
+      done1 = false;
+    }
+    
+    @Override
+       public void run () {
+      synchronized (WaitTest.this) {
+        try {
+          System.out.print(name); System.out.println(" waiting");
+          waiting = true;
+          WaitTest.this.wait();
+          System.out.print(name); System.out.println(" notified");
+          done1 = true;
+        } catch (InterruptedException ix) {
+          throw new RuntimeException("waiter was interrupted");
+        }
+      }
+    }    
+  }
+  
+  /**
+   * that's a misnomer, since this one executes almost all of signal handling (except of mixed
+   * wait/timeout-wait and timeout joins) it should be called 'testAlmostAll'
+   *
+   */
+  @Test public void testNotifyAll () {
+    if (verifyNoPropertyViolation()) {
+      System.out.println("running testNotifyAll()");
+
+      Waiter waiter1 = new Waiter("waiter1");
+      Thread t1 = new Thread(waiter1);
+      t1.start();
+      while (!waiter1.waiting) {
+        Thread.yield();
+      }
+
+      Waiter waiter2 = new Waiter("waiter2");
+      Thread t2 = new Thread(waiter2);
+      t2.start();
+      while (!waiter2.waiting) {
+        Thread.yield();
+      }
+
+      synchronized (this) {
+        System.out.println("main notifying all waiters..");
+        notifyAll();
+        System.out.println("..done");
+      }
+
+      try {
+        t1.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main interrupted while waiting for thread1 to finish");
+      }
+      try {
+        t2.join();
+      } catch (InterruptedException ix) {
+        throw new RuntimeException("main interrupted while waiting for thread2 to finish");
+      }
+
+      synchronized (this) {
+        if (!waiter1.done1) {
+          throw new RuntimeException("waiter1 was not done");
+        }
+        if (!waiter2.done1) {
+          throw new RuntimeException("waiter2 was not done");
+        }
+      }
+    }
+  }
+}
+
diff --git a/src/tests/gov/nasa/jpf/test/xerces/SAXParserTest.java b/src/tests/gov/nasa/jpf/test/xerces/SAXParserTest.java
new file mode 100644 (file)
index 0000000..b6cc52c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.test.xerces;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.IOException;
+
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+
+import org.junit.Test;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * regression test for parsing xml files with Apache xerces
+ * The data files are shamelessly lifted from Checkstyle 5.3, to use some
+ * real input data
+ */
+public class SAXParserTest extends TestJPF {
+
+  @Test
+  public void testSimpleParse() throws ParserConfigurationException, SAXException, IOException  {
+
+    if (verifyNoPropertyViolation(
+            "+http.connection=http://*.dtd -- gov.nasa.jpf.CachedROHttpConnection",
+            "+http.cache_dir=src/tests/gov/nasa/jpf/test/xerces",
+            "+log.info=http")){
+      String pathName = "src/tests/gov/nasa/jpf/test/xerces/sun_checks.xml";
+
+      DefaultHandler handler = new DefaultHandler();
+
+      XMLReader mParser;
+      SAXParserFactory factory = SAXParserFactory.newInstance();
+      factory.setValidating(true);
+      factory.setNamespaceAware(true);
+      mParser = factory.newSAXParser().getXMLReader();
+      mParser.setContentHandler(handler);
+      mParser.setEntityResolver(handler);
+      mParser.setErrorHandler(handler);
+
+      mParser.parse(pathName);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/test/xerces/http%^^www.puppycrawl.com^dtds^configuration_1_3.dtd b/src/tests/gov/nasa/jpf/test/xerces/http%^^www.puppycrawl.com^dtds^configuration_1_3.dtd
new file mode 100644 (file)
index 0000000..51bb8a6
--- /dev/null
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>\r
+\r
+<!-- Add the following to any file that is to be validated against this DTD:\r
+\r
+<!DOCTYPE module PUBLIC\r
+    "-//Puppy Crawl//DTD Check Configuration 1.3//EN"\r
+    "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">\r
+-->\r
+\r
+<!ELEMENT module (module|property|metadata|message)*>\r
+<!ATTLIST module name NMTOKEN #REQUIRED>\r
+\r
+<!ELEMENT property EMPTY>\r
+<!ATTLIST property\r
+       name NMTOKEN #REQUIRED\r
+    value CDATA #REQUIRED\r
+    default CDATA #IMPLIED\r
+>\r
+\r
+<!--\r
+\r
+   Used to store metadata in the Checkstyle configuration file. This\r
+   information is ignored by Checkstyle. This may be useful if you want to\r
+   store plug-in specific information.\r
+\r
+   To avoid name clashes between different tools/plug-ins you are *strongly*\r
+   encouraged to prefix all names with your domain name. For example, use the\r
+   name "com.mycompany.parameter" instead of "parameter".\r
+\r
+   The prefix "com.puppycrawl." is reserved for Checkstyle.\r
+\r
+-->\r
+\r
+<!ELEMENT metadata EMPTY>\r
+<!ATTLIST metadata\r
+       name NMTOKEN #REQUIRED\r
+       value CDATA #REQUIRED\r
+>\r
+\r
+<!--\r
+   Can be used to replaced some generic Checkstyle messages with a custom\r
+   messages.\r
+\r
+   The 'key' attribute specifies for which actual Checkstyle message the\r
+   replacing should occur, look into Checkstyles message.properties for\r
+   the according message keys.\r
+\r
+   The 'value' attribute defines the custom message patterns including\r
+   message parameter placeholders as defined in the original Checkstyle\r
+   messages (again see message.properties for reference).\r
+-->\r
+<!ELEMENT message EMPTY>\r
+<!ATTLIST message\r
+       key NMTOKEN #REQUIRED\r
+       value CDATA #REQUIRED\r
+>\r
diff --git a/src/tests/gov/nasa/jpf/test/xerces/sun_checks.xml b/src/tests/gov/nasa/jpf/test/xerces/sun_checks.xml
new file mode 100644 (file)
index 0000000..ff0eb85
--- /dev/null
@@ -0,0 +1,189 @@
+<?xml version="1.0"?>\r
+<!DOCTYPE module PUBLIC\r
+          "-//Puppy Crawl//DTD Check Configuration 1.3//EN"\r
+          "http://www.puppycrawl.com/dtds/configuration_1_3.dtd">\r
+\r
+<!--\r
+\r
+  Checkstyle configuration that checks the sun coding conventions from:\r
+\r
+    - the Java Language Specification at\r
+      http://java.sun.com/docs/books/jls/second_edition/html/index.html\r
+\r
+    - the Sun Code Conventions at http://java.sun.com/docs/codeconv/\r
+\r
+    - the Javadoc guidelines at\r
+      http://java.sun.com/j2se/javadoc/writingdoccomments/index.html\r
+\r
+    - the JDK Api documentation http://java.sun.com/j2se/docs/api/index.html\r
+\r
+    - some best practices\r
+\r
+  Checkstyle is very configurable. Be sure to read the documentation at\r
+  http://checkstyle.sf.net (or in your downloaded distribution).\r
+\r
+  Most Checks are configurable, be sure to consult the documentation.\r
+\r
+  To completely disable a check, just comment it out or delete it from the file.\r
+\r
+  Finally, it is worth reading the documentation.\r
+\r
+-->\r
+\r
+<module name="Checker">\r
+    <!--\r
+        If you set the basedir property below, then all reported file\r
+        names will be relative to the specified directory. See\r
+        http://checkstyle.sourceforge.net/5.x/config.html#Checker\r
+\r
+        <property name="basedir" value="${basedir}"/>\r
+    -->\r
+\r
+    <!-- Checks that a package-info.java file exists for each package.     -->\r
+    <!-- See http://checkstyle.sf.net/config_javadoc.html#JavadocPackage -->\r
+    <module name="JavadocPackage"/>\r
+\r
+    <!-- Checks whether files end with a new line.                        -->\r
+    <!-- See http://checkstyle.sf.net/config_misc.html#NewlineAtEndOfFile -->\r
+    <module name="NewlineAtEndOfFile"/>\r
+\r
+    <!-- Checks that property files contain the same keys.         -->\r
+    <!-- See http://checkstyle.sf.net/config_misc.html#Translation -->\r
+    <module name="Translation"/>\r
+    \r
+    <!-- Checks for Size Violations.                    -->\r
+    <!-- See http://checkstyle.sf.net/config_sizes.html -->\r
+    <module name="FileLength"/>\r
+    \r
+    <!-- Checks for whitespace                               -->\r
+    <!-- See http://checkstyle.sf.net/config_whitespace.html -->\r
+    <module name="FileTabCharacter"/>\r
+\r
+    <!-- Miscellaneous other checks.                   -->\r
+    <!-- See http://checkstyle.sf.net/config_misc.html -->\r
+    <module name="RegexpSingleline">\r
+       <property name="format" value="\s+$"/>\r
+       <property name="minimum" value="0"/>\r
+       <property name="maximum" value="0"/>\r
+       <property name="message" value="Line has trailing spaces."/>\r
+    </module>\r
+\r
+    <module name="TreeWalker">\r
+\r
+        <!-- Checks for Javadoc comments.                     -->\r
+        <!-- See http://checkstyle.sf.net/config_javadoc.html -->\r
+        <module name="JavadocMethod"/>\r
+        <module name="JavadocType"/>\r
+        <module name="JavadocVariable"/>\r
+        <module name="JavadocStyle"/>\r
+\r
+\r
+        <!-- Checks for Naming Conventions.                  -->\r
+        <!-- See http://checkstyle.sf.net/config_naming.html -->\r
+        <module name="ConstantName"/>\r
+        <module name="LocalFinalVariableName"/>\r
+        <module name="LocalVariableName"/>\r
+        <module name="MemberName"/>\r
+        <module name="MethodName"/>\r
+        <module name="PackageName"/>\r
+        <module name="ParameterName"/>\r
+        <module name="StaticVariableName"/>\r
+        <module name="TypeName"/>\r
+\r
+\r
+        <!-- Checks for Headers                                -->\r
+        <!-- See http://checkstyle.sf.net/config_header.html   -->\r
+        <!-- <module name="Header">                            -->\r
+            <!-- The follow property value demonstrates the ability     -->\r
+            <!-- to have access to ANT properties. In this case it uses -->\r
+            <!-- the ${basedir} property to allow Checkstyle to be run  -->\r
+            <!-- from any directory within a project. See property      -->\r
+            <!-- expansion,                                             -->\r
+            <!-- http://checkstyle.sf.net/config.html#properties        -->\r
+            <!-- <property                                              -->\r
+            <!--     name="headerFile"                                  -->\r
+            <!--     value="${basedir}/java.header"/>                   -->\r
+        <!-- </module> -->\r
+\r
+        <!-- Following interprets the header file as regular expressions. -->\r
+        <!-- <module name="RegexpHeader"/>                                -->\r
+\r
+\r
+        <!-- Checks for imports                              -->\r
+        <!-- See http://checkstyle.sf.net/config_import.html -->\r
+        <module name="AvoidStarImport"/>\r
+        <module name="IllegalImport"/> <!-- defaults to sun.* packages -->\r
+        <module name="RedundantImport"/>\r
+        <module name="UnusedImports"/>\r
+\r
+\r
+        <!-- Checks for Size Violations.                    -->\r
+        <!-- See http://checkstyle.sf.net/config_sizes.html -->\r
+        <module name="LineLength"/>\r
+        <module name="MethodLength"/>\r
+        <module name="ParameterNumber"/>\r
+\r
+\r
+        <!-- Checks for whitespace                               -->\r
+        <!-- See http://checkstyle.sf.net/config_whitespace.html -->\r
+        <module name="EmptyForIteratorPad"/>\r
+        <module name="GenericWhitespace"/>\r
+        <module name="MethodParamPad"/>\r
+        <module name="NoWhitespaceAfter"/>\r
+        <module name="NoWhitespaceBefore"/>\r
+        <module name="OperatorWrap"/>\r
+        <module name="ParenPad"/>\r
+        <module name="TypecastParenPad"/>\r
+        <module name="WhitespaceAfter"/>\r
+        <module name="WhitespaceAround"/>\r
+\r
+\r
+        <!-- Modifier Checks                                    -->\r
+        <!-- See http://checkstyle.sf.net/config_modifiers.html -->\r
+        <module name="ModifierOrder"/>\r
+        <module name="RedundantModifier"/>\r
+\r
+\r
+        <!-- Checks for blocks. You know, those {}'s         -->\r
+        <!-- See http://checkstyle.sf.net/config_blocks.html -->\r
+        <module name="AvoidNestedBlocks"/>\r
+        <module name="EmptyBlock"/>\r
+        <module name="LeftCurly"/>\r
+        <module name="NeedBraces"/>\r
+        <module name="RightCurly"/>\r
+\r
+\r
+        <!-- Checks for common coding problems               -->\r
+        <!-- See http://checkstyle.sf.net/config_coding.html -->\r
+        <module name="AvoidInlineConditionals"/>\r
+        <module name="DoubleCheckedLocking"/>    <!-- MY FAVOURITE -->\r
+        <module name="EmptyStatement"/>\r
+        <module name="EqualsHashCode"/>\r
+        <module name="HiddenField"/>\r
+        <module name="IllegalInstantiation"/>\r
+        <module name="InnerAssignment"/>\r
+        <module name="MagicNumber"/>\r
+        <module name="MissingSwitchDefault"/>\r
+        <module name="RedundantThrows"/>\r
+        <module name="SimplifyBooleanExpression"/>\r
+        <module name="SimplifyBooleanReturn"/>\r
+\r
+        <!-- Checks for class design                         -->\r
+        <!-- See http://checkstyle.sf.net/config_design.html -->\r
+        <module name="DesignForExtension"/>\r
+        <module name="FinalClass"/>\r
+        <module name="HideUtilityClassConstructor"/>\r
+        <module name="InterfaceIsType"/>\r
+        <module name="VisibilityModifier"/>\r
+\r
+\r
+        <!-- Miscellaneous other checks.                   -->\r
+        <!-- See http://checkstyle.sf.net/config_misc.html -->\r
+        <module name="ArrayTypeStyle"/>\r
+        <module name="FinalParameters"/>\r
+        <module name="TodoComment"/>\r
+        <module name="UpperEll"/>\r
+\r
+    </module>\r
+\r
+</module>\r
diff --git a/src/tests/gov/nasa/jpf/util/ArrayIntSetTestBase.java b/src/tests/gov/nasa/jpf/util/ArrayIntSetTestBase.java
new file mode 100644 (file)
index 0000000..7ab0301
--- /dev/null
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.NoSuchElementException;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+public abstract class ArrayIntSetTestBase extends TestJPF {
+
+  protected abstract ArrayIntSet createArrayIntSet();
+  protected abstract ArrayIntSet createArrayIntSet(int n);
+  
+  @Test
+  public void testInsert(){
+    ArrayIntSet s = createArrayIntSet();
+    s.add(42);
+    s.add(43);
+    s.add(41);
+    s.add(42);
+    s.add(0);
+    
+    System.out.println(s);
+    
+    assertTrue(s.size() == 4);
+    assertTrue(s.contains(0));
+    assertTrue(s.contains(41));
+    assertTrue(s.contains(42));
+    assertTrue(s.contains(43));
+  }
+  
+  @Test
+  public void testRemove(){
+    ArrayIntSet s = createArrayIntSet();
+    s.add(42);
+    assertTrue(s.size() == 1);
+    assertTrue(s.contains(42));
+    
+    s.remove(42);
+    assertFalse(s.contains(42));
+    assertTrue(s.size() == 0);
+    
+    s.add(42);
+    s.add(42000);
+    s.add(0);
+    assertTrue(s.size() == 3);
+    s.remove(42000);
+    assertTrue(s.size() == 2);
+    assertFalse(s.contains(42000));
+    s.remove(0);
+    assertFalse(s.contains(0));
+    assertTrue(s.size() == 1);
+  }
+  
+  @Test
+  public void testRemoveLast(){
+    ArrayIntSet s = createArrayIntSet(2);
+    s.add(1);
+    s.add(2);
+    
+    s.remove(2);
+    assertTrue( s.size() == 1);
+    assertTrue( s.contains(1));
+    
+    s.remove(1);
+    assertTrue( s.isEmpty());
+  }
+  
+  @Test
+  public void testRemoveFirst(){
+    ArrayIntSet s = createArrayIntSet(2);
+    s.add(1);
+    s.add(2);
+    
+    s.remove(1);
+    assertTrue( s.size() == 1);
+    assertTrue( s.contains(2));
+    
+    s.remove(2);
+    assertTrue( s.isEmpty());
+  }
+  
+  
+  @Test
+  public void testIterator(){
+    ArrayIntSet s = createArrayIntSet();
+    s.add(1);
+    s.add(2);
+    s.add(3);
+    
+    int i=0;
+    IntIterator it = s.intIterator();
+    while (it.hasNext()){
+      System.out.print(it.next());
+      i++;
+    }
+    System.out.println();
+    assertTrue(i == 3);
+    
+    assertTrue( !it.hasNext());
+    try {
+      it.next();
+      fail("iterator failed to throw NoSuchElementException");
+    } catch (NoSuchElementException nsex){
+      // that's expected
+    }
+    
+    it = s.intIterator(); // fresh one
+    while (it.hasNext()){
+      if (it.next() == 2){
+        it.remove();
+        assertTrue( s.size() == 2);
+        break;
+      }
+    }
+    i = it.next();
+    assertTrue(i == 3);
+    it.remove();
+    assertTrue(s.size() == 1);
+    assertTrue( !it.hasNext());
+    
+    s.add(42);
+    it = s.intIterator();
+    assertTrue(it.next() == 1);
+    it.remove();
+    assertTrue( it.next() == 42);
+    it.remove();
+    assertTrue( s.isEmpty());
+  }
+  
+  @Test
+  public void testComparison(){
+    int[][] a = {
+        {42, 0, 41},
+        {42, 41, 0},
+        {0, 42, 41},
+        {0, 41, 42},
+        {41, 0, 42},
+        {41, 42, 0}
+    };
+    
+    ArrayIntSet[] set = new ArrayIntSet[a.length];
+    
+    for (int i=0; i< a.length; i++) {
+      set[i]= createArrayIntSet();
+      int[] v = a[i];
+      for (int j=0; j<v.length; j++) {
+        set[i].add(v[j]);
+      }
+    }
+    
+    ArrayIntSet s1 = null, s2 = null;
+    for (int i=1; i< a.length; i++) {
+      s1 = set[i-1];
+      s2 = set[i];
+      System.out.println("comparing " + s1 + " with " + s2);
+      
+      assertTrue(s1.hashCode() == s2.hashCode());
+      assertTrue(s1.equals(s2));
+    }
+        
+    // inverse test
+    s2.remove(41);
+    assertFalse( s1.hashCode() == s2.hashCode());
+    assertFalse( s1.equals(s2));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/ArrayObjectQueueTest.java b/src/tests/gov/nasa/jpf/util/ArrayObjectQueueTest.java
new file mode 100644 (file)
index 0000000..af71934
--- /dev/null
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import org.junit.Test;
+
+/**
+ *
+ */
+public class ArrayObjectQueueTest extends TestJPF {
+  
+  <E> void printLogicalOrder (ArrayObjectQueue<E> q){
+    int n = 0;
+    System.out.print('{');
+    for (E e : q){
+      if (n > 0){
+        System.out.print(',');
+      }
+      System.out.print(e);
+      n++;
+    }
+    System.out.println('}');
+  }
+
+  <E> void printPhysicalOrder (ArrayObjectQueue<E> q){
+    int n = 0;
+    System.out.print('{');
+    for (Iterator<E> it = q.storageIterator(); it.hasNext(); ){
+      E e = it.next();
+      if (n > 0){
+        System.out.print(',');
+      }
+      System.out.print(e);
+      n++;
+    }
+    System.out.println('}');
+  }
+
+  
+  @Test
+  public void testBasic(){
+    ArrayObjectQueue<String> q = new ArrayObjectQueue<String>(4);
+    
+    assertTrue( q.isEmpty());
+    assertTrue( q.size() == 0);
+    
+    q.add("1");
+    assertFalse( q.isEmpty());
+    assertTrue( q.size() == 1);
+    
+    printLogicalOrder(q);
+    
+    q.add("2");
+    q.add("3");
+    q.add("4");
+    
+    printLogicalOrder(q);
+        
+    // now we need to grow
+    q.add("5");
+    
+    printLogicalOrder(q);
+    
+    assertTrue( "1".equals( q.remove()));
+    assertTrue(q.size() == 4);
+    printLogicalOrder(q);
+    
+    assertTrue( "2".equals( q.remove()));
+    assertTrue( "3".equals( q.remove()));
+    assertTrue( "4".equals( q.remove()));
+    assertTrue( "5".equals( q.remove()));
+    
+    printLogicalOrder(q);
+    assertTrue(q.isEmpty());
+    
+    try {
+      q.remove();
+      fail("should never get here");
+    } catch (NoSuchElementException x){
+      // good exception
+    }
+  }
+  
+  @Test
+  public void testTailChasing(){
+    ArrayObjectQueue<String> q = new ArrayObjectQueue<String>(4);
+    q.add("1");
+    q.add("2");
+    q.add("3");
+    q.add("4");
+    
+    q.remove(); // should remove "1"
+    q.add("5"); // should make use of the free space
+    
+    printLogicalOrder(q);
+    assertTrue( q.getCurrentCapacity() == 4);
+    
+    q.remove(); // should remove "2"
+    q.remove(); // should remove "3"
+    q.add("6");
+    
+    printLogicalOrder(q);
+    printPhysicalOrder(q);
+    
+    String s = q.remove();
+    assertTrue( "4".equals(s));
+    // next remove should wrap around
+    assertTrue( "5".equals(q.remove()));
+    
+    printLogicalOrder(q);
+    printPhysicalOrder(q);
+  }
+
+  
+  @Test
+  public void testMidGrow(){
+    ArrayObjectQueue<String> q = new ArrayObjectQueue<String>(4);
+    q.add("1");
+    q.add("2");
+    q.add("3");
+    q.add("4");
+  
+    q.remove(); // 1
+    q.add("5"); // that shouldn't grow yet
+    int len = q.getCurrentCapacity();
+    assertTrue( len == 4);
+    printPhysicalOrder(q);
+    
+    q.add("6"); // that should grow
+    assertTrue(q.getCurrentCapacity() > len);
+    
+    printLogicalOrder(q);
+    printPhysicalOrder(q);    
+  }
+  
+  //--- queue processing
+  
+  static class E {
+    String name;
+    E left, right;
+    boolean visited;
+    
+    E (String name){ this.name = name; }
+  }
+
+  static class EProcessor implements Processor<E> {
+    ArrayObjectQueue queue;
+    int processed = 0;
+    
+    EProcessor (ArrayObjectQueue<E> queue){
+      this.queue = queue;
+    }
+    
+    @Override
+       public void process(E e){
+      e.visited = true;
+      processed++;
+      System.out.println("processed: " + e.name);
+
+      E ref = e.left;
+      if (ref != null && !ref.visited) {
+        ref.visited = true;
+        queue.add(ref);
+      }
+
+      ref = e.right;
+      if (ref != null && !ref.visited) {
+        ref.visited = true;
+        queue.add(ref);
+      }
+    }
+  }
+  
+  @Test
+  public void testProcessing(){
+    E a = new E("a");
+    E b = new E("b");
+    E c = new E("c");
+    E d = new E("d");
+    E e = new E("e");
+    E f = new E("f");
+    E g = new E("g");
+    
+    a.left  = b;
+    b.right = c;
+    c.left  = d;
+    d.left  = a;  d.right = e;
+    e.left  = f;  e.right = g;
+    g.left  = a;
+    
+    ArrayObjectQueue<E> q = new ArrayObjectQueue<E>();
+    q.add(a);
+    q.add(g);
+    
+    EProcessor proc = new EProcessor(q);
+    q.process(proc);
+    
+    assertTrue(a.visited);
+    assertTrue(b.visited);
+    assertTrue(c.visited);
+    assertTrue(d.visited);
+    assertTrue(e.visited);
+    assertTrue(f.visited);
+    assertTrue(g.visited);
+    
+    assertTrue( proc.processed == 7);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/AvailableBufferedInputStreamTest.java b/src/tests/gov/nasa/jpf/util/AvailableBufferedInputStreamTest.java
new file mode 100644 (file)
index 0000000..00469bf
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.io.IOException;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class AvailableBufferedInputStreamTest extends TestJPF {
+
+  private static final int PIPE_SIZE = 1024 * 1024;
+
+  private PipedOutputStream m_output;
+  private AvailableBufferedInputStream m_input;
+
+  @Before
+  public void before() throws IOException {
+    m_output = new PipedOutputStream();
+    m_input = new AvailableBufferedInputStream(new PipedInputStream(m_output, PIPE_SIZE));
+  }
+
+  @After
+  public void after() throws IOException {
+    m_output.flush();
+    m_output.close();
+    m_output = null;
+
+    assertEquals(0, m_input.available());
+    assertEquals(-1, m_input.peek());
+    assertEquals(-1, m_input.read());
+
+    m_input.close();
+    m_input = null;
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void passNullPointerToConstructor() throws IOException {
+    new AvailableBufferedInputStream(null).close();
+  }
+
+  @Test
+  public void availableStuck() throws IOException {
+    int i;
+
+    for (i = 10; --i >= 0;) {
+      m_output.write(i);
+      m_output.flush();
+      assertEquals(1, m_input.available());
+    }
+
+    for (i = 10; --i >= 0;) {
+      assertEquals(i, m_input.peek());
+      assertEquals(i, m_input.read());
+    }
+  }
+
+  @Test
+  public void unreadExtra() throws IOException {
+    int i;
+
+    m_output.write(20);
+    m_output.write(21);
+    m_output.flush();
+
+    assertEquals(20, m_input.peek());
+    assertEquals(20, m_input.read());  // Load the buffer
+
+    for (i = 0; i < 10; i++) {
+      m_input.unread(i);
+    }
+
+    for (i = 10; --i >= 0;) {
+      assertEquals(i, m_input.peek());
+      assertEquals(i, m_input.read());
+      assertEquals(i + 1, m_input.available());
+    }
+
+    assertEquals(21, m_input.peek());
+    assertEquals(21, m_input.read());
+  }
+
+  @Test
+  public void unreadMinus1() throws IOException {
+    m_input.unread(-1);
+    assertEquals(0x00FF, m_input.peek());
+    assertEquals(0x00FF, m_input.read());
+  }
+
+  @Test
+  public void readBufferSplit() throws IOException {
+    byte buffer[];
+
+    buffer = new byte[2];
+
+    m_output.write(30);
+    m_output.flush();
+
+    m_input.available();
+
+    m_output.write(40);
+    m_output.flush();
+
+    assertEquals(30, m_input.peek());
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(30, buffer[0]);
+
+    assertEquals(40, m_input.peek());
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(40, buffer[0]);
+  }
+
+  @Test
+  public void readBufferPartialNoBlock() throws IOException {
+    byte buffer[];
+
+    buffer = new byte[2];
+
+    m_output.write(30);
+    m_output.flush();
+
+    assertEquals(30, m_input.peek());
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(30, buffer[0]);
+  }
+
+  @Test
+  public void readBufferLeftOver() throws IOException {
+    byte buffer[];
+
+    buffer = new byte[2];
+
+    m_output.write(30);
+    m_output.write(40);
+    m_output.write(50);
+    m_output.flush();
+
+    assertEquals(30, m_input.peek());
+    assertEquals(2, m_input.read(buffer));
+    assertEquals(30, buffer[0]);
+    assertEquals(40, buffer[1]);
+
+    assertEquals(50, m_input.peek());
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(50, buffer[0]);
+    assertEquals(40, buffer[1]);
+  }
+
+  @Test
+  public void unreadOverflow() throws IOException {
+    int i;
+
+    try {
+      for (i = 0; i < m_input.getBufferSize(); i++) {
+        m_input.unread(i);
+        assertEquals(i & 0x00FF, m_input.peek());
+      }
+    } catch (IOException e) {
+      fail();
+    }
+
+    try {
+      m_input.unread(0);
+      fail();
+    } catch (IOException e) {
+      e = null;  // Get rid of IDE warning
+    }
+
+    for (i = m_input.getBufferSize(); --i >= 0;) {
+      assertEquals(i & 0x00FF, m_input.peek());
+      assertEquals(i & 0x00FF, m_input.read());
+    }
+  }
+
+  @Test
+  public void fillWithNoMoreData() throws IOException {
+    assertEquals(0, m_input.available());
+    assertEquals(0, m_input.available());
+  }
+
+  @Test
+  public void fillWithTooMuchData() throws IOException {
+    int i;
+
+    for (i = 0; i < m_input.getBufferSize() + 1; i++) {
+      m_output.write(i);
+    }
+
+    m_output.flush();
+
+    assertEquals(m_input.getBufferSize(), m_input.available());
+
+    for (i = 0; i < m_input.getBufferSize() + 1; i++) {
+      assertEquals(i & 0x00FF, m_input.peek());
+      assertEquals(i & 0x00FF, m_input.read());
+    }
+  }
+
+  @Test
+  public void readAfterClose() throws IOException {
+    m_output.write(10);
+    m_output.flush();
+
+    m_input.available();
+
+    m_output.close();
+
+    assertEquals(10, m_input.peek());
+    assertEquals(10, m_input.read());
+    assertEquals(-1, m_input.peek());
+    assertEquals(-1, m_input.read());
+  }
+
+  @Test
+  public void readBufferAfterClose() throws IOException {
+    byte buffer[];
+
+    m_output.write(10);
+    m_output.flush();
+
+    m_input.available();
+
+    m_output.close();
+
+    buffer = new byte[10];
+
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(-1, m_input.read(buffer));
+  }
+
+  @Test
+  public void testToString() throws IOException {
+    int i;
+
+    assertEquals("", m_input.toString());
+
+    m_output.write(new byte[]{'h', 'e', 'l', 'l', 'o'});
+    m_output.flush();
+
+    m_input.available();
+
+    assertEquals("hello", m_input.toString());
+
+    for (i = 5; --i >= 0;) {
+      m_input.read();
+    }
+  }
+
+  @Test
+  public void readBufferEmptyBuffer() throws IOException {
+    byte buffer[];
+
+    m_output.write(10);
+    m_output.flush();
+
+    buffer = new byte[1];
+
+    assertEquals(1, m_input.read(buffer));
+    assertEquals(10, buffer[0]);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/BitSet1024Test.java b/src/tests/gov/nasa/jpf/util/BitSet1024Test.java
new file mode 100644 (file)
index 0000000..135cd1c
--- /dev/null
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.BitSet;
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * unit test for gov.nasa.jpf.util.BitSet
+ */
+public class BitSet1024Test extends TestJPF {
+
+  public static void main (String[] args){
+
+    // our performance evals
+    if (args.length == 1){
+      String mthName = args[0];
+      if (mthName.equals("evalBitSet")){
+        evalBitSet();
+        return;
+      } else if (mthName.equals("evalBitSet1024")){
+        evalBitSet1024();
+        return;
+      }
+    }
+
+    // the regression tests
+    runTestsOfThisClass(args);
+  }
+
+  //--- regression tests
+
+  @Test
+  public void testBasic() {
+    BitSet1024 b = new BitSet1024();
+
+    assert b.isEmpty();
+    assert !b.get(0);
+
+    b.set(0);
+    assert b.get(0);
+
+    b.set(63);
+    assert b.get(63);
+
+    b.set(127);
+    assert b.get(127);
+
+    b.set(128);
+    assert b.get(128);
+
+    b.set(191);
+    assert b.get(191);
+
+    b.set(192);
+    assert b.get(192);
+
+    b.set(255);
+    assert b.get(255);
+
+    b.clear(255);
+
+    assert b.cardinality() == 6;
+    assert b.length() == 193;
+
+    b = new BitSet1024();
+    b.set(4);
+    b.set(4,false);
+    assert !b.get(4);
+
+  }
+
+  @Test
+  public void testEnumeration() {
+    BitSet1024 b = new BitSet1024();
+
+    assert b.nextSetBit(0) == -1;
+
+    b.set(0);
+    assert b.nextSetBit(0) == 0;
+
+    b.clear();
+    b.set(63);
+    assert b.nextSetBit(0) == 63;
+
+    b.clear();
+    b.set(64);
+    assert b.nextSetBit(0) == 64;
+    
+    b.clear();
+    b.set(380);
+    assert b.nextSetBit(0) == 380;
+    
+    b.clear();
+    b.set(442);
+    assert b.nextSetBit(0) == 442;
+    
+    b.clear();
+    b.set(512);
+    assert b.nextSetBit(0) == 512;
+    
+    b.clear();
+    b.set(620);
+    assert b.nextSetBit(0) == 620;
+    
+    b.clear();
+    b.set(980);
+    assert b.nextSetBit(0) == 980;
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(255);
+    b.set(200);
+    b.set(320);
+    b.set(522);
+    b.set(619);
+    b.set(830);
+    b.set(921);
+
+    assert b.nextSetBit(0) == 0;
+    assert b.nextSetBit(1) == 10;
+    assert b.nextSetBit(11) == 42;
+    assert b.nextSetBit(43) == 200;
+    assert b.nextSetBit(201) == 255;
+    assert b.nextSetBit(256) == 320;
+    assert b.nextSetBit(321) == 522;
+    assert b.nextSetBit(523) == 619;
+    assert b.nextSetBit(620) == 830;
+    assert b.nextSetBit(831) == 921;
+  }
+
+  @Test
+  public void testOutOfBounds() {
+    BitSet1024 b = new BitSet1024();
+
+    try {
+      b.set(2264);
+      throw new AssertionError("set(2264) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.get(1256);
+      throw new AssertionError("get(1256) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.clear(-1);
+      throw new AssertionError("clear(-1) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+  }
+
+
+  //--- performance section
+
+  static final int NROUNDS = 2000000;
+  static final int NITER = 500000;
+
+  public static void evalBitSet() {
+    Random r = new Random(0);
+    BitSet b = new BitSet(1024);
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(1024);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(232);
+    b.set(200);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+
+  public static void evalBitSet1024() {
+    Random r = new Random(0);
+    BitSet1024 b = new BitSet1024();
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(1024);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(232);
+    b.set(200);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/BitSet256Test.java b/src/tests/gov/nasa/jpf/util/BitSet256Test.java
new file mode 100644 (file)
index 0000000..3ab3625
--- /dev/null
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.BitSet;
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * unit test for gov.nasa.jpf.util.BitSet
+ */
+public class BitSet256Test extends TestJPF {
+
+  public static void main (String[] args){
+
+    // our performance evals
+    if (args.length == 1){
+      String mthName = args[0];
+      if (mthName.equals("evalBitSet")){
+        evalBitSet();
+        return;
+      } else if (mthName.equals("evalBitSet256")){
+        evalBitSet256();
+        return;
+      }
+    }
+
+    // the regression tests
+    runTestsOfThisClass(args);
+  }
+
+  //--- regression tests
+
+  @Test
+  public void testBasic() {
+    BitSet256 b = new BitSet256();
+
+    assert b.isEmpty();
+    assert !b.get(0);
+
+    b.set(0);
+    assert b.get(0);
+
+    b.set(63);
+    assert b.get(63);
+
+    b.set(127);
+    assert b.get(127);
+
+    b.set(128);
+    assert b.get(128);
+
+    b.set(191);
+    assert b.get(191);
+
+    b.set(192);
+    assert b.get(192);
+
+    b.set(255);
+    assert b.get(255);
+
+    b.clear(255);
+
+    assert b.cardinality() == 6;
+    assert b.length() == 193;
+
+    b = new BitSet256();
+    b.set(4);
+    b.set(4,false);
+    assert !b.get(4);
+
+  }
+
+  @Test
+  public void testEnumeration() {
+    BitSet256 b = new BitSet256();
+
+    assert b.nextSetBit(0) == -1;
+
+    b.set(0);
+    assert b.nextSetBit(0) == 0;
+
+    b.clear();
+    b.set(63);
+    assert b.nextSetBit(0) == 63;
+
+    b.clear();
+    b.set(64);
+    assert b.nextSetBit(0) == 64;
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(255);
+    b.set(200);
+
+    assert b.nextSetBit(0) == 0;
+    assert b.nextSetBit(1) == 10;
+    assert b.nextSetBit(11) == 42;
+    assert b.nextSetBit(43) == 200;
+    assert b.nextSetBit(201) == 255;
+  }
+
+  @Test
+  public void testOutOfBounds() {
+    BitSet256 b = new BitSet256();
+
+    try {
+      b.set(2264);
+      throw new AssertionError("set(2264) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.get(256);
+      throw new AssertionError("get(256) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.clear(-1);
+      throw new AssertionError("clear(-1) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+  }
+
+
+  //--- performance section
+
+  static final int NROUNDS = 2000000;
+  static final int NITER = 500000;
+
+  public static void evalBitSet() {
+    Random r = new Random(0);
+    BitSet b = new BitSet(256);
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(256);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(232);
+    b.set(200);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+
+  public static void evalBitSet256() {
+    Random r = new Random(0);
+    BitSet256 b = new BitSet256();
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(256);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(232);
+    b.set(200);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/BitSet64Test.java b/src/tests/gov/nasa/jpf/util/BitSet64Test.java
new file mode 100644 (file)
index 0000000..6edf525
--- /dev/null
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.BitSet;
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * unit test for BitSet64
+ */
+public class BitSet64Test extends TestJPF {
+  public static void main (String[] args){
+
+    // our performance evals
+    if (args.length == 1){
+      String mthName = args[0];
+      if (mthName.equals("evalBitSet")){
+        evalBitSet();
+        return;
+      } else if (mthName.equals("evalBitSet64")){
+        evalBitSet64();
+        return;
+      }
+    }
+
+    // the regression tests
+    runTestsOfThisClass(args);
+  }
+
+  //--- regression tests
+
+  @Test
+  public void testBasic() {
+    BitSet64 b = new BitSet64();
+
+    assert b.isEmpty();
+    assert !b.get(0);
+
+    b.set(0);
+    assert b.get(0);
+
+    b.set(63);
+    assert b.get(63);
+
+    b.set(27);
+    assert b.get(27);
+
+    b.set(58);
+    assert b.get(58);
+
+    b.set(31);
+    assert b.get(31);
+
+
+    b.clear(31);
+
+    assert b.cardinality() == 4;
+    assert b.length() == 64 : " wrong length: " + b.length();
+
+    b = new BitSet64();
+    b.set(4);
+    b.set(4,false);
+    assert !b.get(4);
+  }
+
+  @Test
+  public void testOutOfBounds() {
+    BitSet64 b = new BitSet64();
+
+    try {
+      b.set(64);
+      throw new AssertionError("set(64) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.get(256);
+      throw new AssertionError("get(256) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+
+    try {
+      b.clear(-1);
+      throw new AssertionError("clear(-1) failed to throw");
+    } catch (IndexOutOfBoundsException x){
+      System.out.println(x);
+    }
+  }
+
+  @Test
+  public void testEnumeration() {
+    BitSet64 b = new BitSet64();
+
+    assert b.nextSetBit(0) == -1;
+
+    b.set(0);
+    assert b.nextSetBit(0) == 0;
+
+    b.clear();
+    b.set(24);
+    assert b.nextSetBit(0) == 24;
+
+    b.clear();
+    b.set(63);
+    assert b.nextSetBit(0) == 63;
+
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(55);
+
+    assert b.nextSetBit(0) == 0;
+    assert b.nextSetBit(1) == 10;
+    assert b.nextSetBit(11) == 42;
+    assert b.nextSetBit(43) == 55;
+  }
+
+  @Test
+  public void testIntSetInterface(){
+    IntSet s = new BitSet64();
+    
+    s.add(42);
+    s.add(0);
+    s.add(63);
+    
+    assertTrue(s.size() == 64);
+    assertTrue( s.contains(42));
+    
+    for (IntIterator it = s.intIterator(); it.hasNext();){
+      int i = it.next();
+      System.out.println(i);
+    }
+  }
+  
+  //--- performance section
+
+  static final int NROUNDS = 2000000;
+  static final int NITER = 500000;
+
+  public static void evalBitSet() {
+    Random r = new Random(0);
+    BitSet b = new BitSet(64);
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(64);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(32);
+    b.set(60);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+
+  public static void evalBitSet64() {
+    Random r = new Random(0);
+    BitSet64 b = new BitSet64();
+
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      int i = r.nextInt(64);
+      b.set(i);
+      b.get(i);
+      b.clear(i);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("BitSet random access: " + (t2-t1));
+
+    b.clear();
+    b.set(0);
+    b.set(42);
+    b.set(10);
+    b.set(32);
+    b.set(60);
+
+    t1 = System.currentTimeMillis();
+    for (int j=0; j<NROUNDS; j++){
+      for (int k=b.nextSetBit(0); k>=0; k=b.nextSetBit(k+1));
+      int n = b.cardinality();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("BitSet set bits iteration: " + (t2-t1));
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/util/CommitOutputStreamTest.java b/src/tests/gov/nasa/jpf/util/CommitOutputStreamTest.java
new file mode 100644 (file)
index 0000000..3d9adcc
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.security.SecureRandom;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class CommitOutputStreamTest extends TestJPF {
+
+  private static final SecureRandom s_random = new SecureRandom();
+
+  private PipedInputStream m_result;
+  private CommitOutputStream m_fixture;
+
+  @Before
+  public void before() throws IOException {
+    PipedOutputStream pipeOut;
+
+    pipeOut = new PipedOutputStream();
+    m_fixture = new CommitOutputStream(pipeOut);
+    m_result = new PipedInputStream(pipeOut, 8 * 1024);
+  }
+
+  @After
+  public void after() throws IOException {
+    m_fixture.flush();
+    assertEquals(0, m_result.available());
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void constructorNullArg() {
+    new CommitOutputStream(null);
+  }
+
+  @Test
+  public void flush() throws IOException {
+    CountedOutputStream counted;
+    CommitOutputStream fixture;
+
+    counted = new CountedOutputStream();
+    fixture = new CommitOutputStream(counted);
+
+    fixture.write(10);
+    fixture.flush();
+
+    assertEquals(0, counted.getWriteCount());
+    assertEquals(1, counted.getFlushCount());
+    assertEquals(1, fixture.getSize());
+  }
+
+  @Test
+  public void close() throws IOException {
+    CountedOutputStream counted;
+    CommitOutputStream fixture;
+
+    counted = new CountedOutputStream();
+    fixture = new CommitOutputStream(counted);
+
+    fixture.write(10);
+    fixture.close();
+
+    assertEquals(0, counted.getWriteCount());
+    assertEquals(1, counted.getCloseCount());
+    assertEquals(1, fixture.getSize());
+  }
+
+  @Test
+  public void rollback() throws IOException {
+    m_fixture.write(10);
+    assertEquals(1, m_fixture.getSize());
+    m_fixture.rollback();
+    assertEquals(0, m_fixture.getSize());
+    m_fixture.commit();
+    m_fixture.flush();
+    assertEquals(0, m_result.available());
+  }
+
+  @Test
+  public void expand() throws IOException {
+    int i;
+
+    for (i = 0; i < 2 * 1024; i++) {
+      m_fixture.write(i);
+    }
+
+    assertEquals(2 * 1024, m_fixture.getSize());
+
+    m_fixture.commit();
+
+    assertEquals(0, m_fixture.getSize());
+    assertEquals(2 * 1024, m_result.available());
+
+    for (i = 0; i < 2 * 1024; i++) {
+      assertEquals(i & 0x00FF, m_result.read());
+    }
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void writeNullBuffer() throws IOException {
+    m_fixture.write(null, 0, 1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeIndexNegOne() throws IOException {
+    m_fixture.write(new byte[0], -1, 0);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeLengthNegOne() throws IOException {
+    m_fixture.write(new byte[0], 0, -1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeBeyondEnd() throws IOException {
+    m_fixture.write(new byte[16], 8, 9);
+  }
+
+  @Test
+  public void writeLengthZero() throws IOException {
+    m_fixture.write(new byte[1], 0, 0);
+
+    assertEquals(0, m_fixture.getSize());
+  }
+
+  @Test
+  public void writeArray() throws IOException {
+    byte expected[], actual[];
+
+    expected = new byte[10];
+
+    s_random.nextBytes(expected);
+
+    m_fixture.write(expected);
+    assertEquals(expected.length, m_fixture.getSize());
+
+    m_fixture.commit();
+    assertEquals(0, m_fixture.getSize());
+
+    m_fixture.flush();
+    assertEquals(expected.length, m_result.available());
+
+    actual = new byte[expected.length];
+
+    assertEquals(actual.length, m_result.read(actual));
+    assertArrayEquals(expected, actual);
+  }
+
+  @Test
+  public void expandDouble() throws IOException {
+    byte expected[], actual[];
+
+    expected = new byte[3 * 1024 / 2];
+
+    s_random.nextBytes(expected);
+
+    m_fixture.write(expected);
+    assertEquals(expected.length, m_fixture.getSize());
+
+    m_fixture.commit();
+    assertEquals(0, m_fixture.getSize());
+
+    m_fixture.flush();
+    assertEquals(expected.length, m_result.available());
+
+    actual = new byte[expected.length];
+
+    assertEquals(actual.length, m_result.read(actual));
+    assertArrayEquals(expected, actual);
+  }
+
+  @Test
+  public void expandTriple() throws IOException {
+    byte expected[], actual[];
+
+    expected = new byte[3 * 1024];
+
+    s_random.nextBytes(expected);
+
+    m_fixture.write(expected);
+    assertEquals(expected.length, m_fixture.getSize());
+
+    m_fixture.commit();
+    assertEquals(0, m_fixture.getSize());
+
+    m_fixture.flush();
+    assertEquals(expected.length, m_result.available());
+
+    actual = new byte[expected.length];
+
+    assertEquals(actual.length, m_result.read(actual));
+    assertArrayEquals(expected, actual);
+  }
+
+  private static class CountedOutputStream extends OutputStream {
+
+    private int m_write;
+    private int m_flush;
+    private int m_close;
+
+    @Override
+       public void write(int data) {
+      m_write++;
+    }
+
+    public int getWriteCount() {
+      return (m_write);
+    }
+
+    @Override
+       public void flush() {
+      m_flush++;
+    }
+
+    public int getFlushCount() {
+      return (m_flush);
+    }
+
+    @Override
+       public void close() {
+      m_close++;
+    }
+
+    public int getCloseCount() {
+      return (m_close);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/IdentityArrayObjectSetTest.java b/src/tests/gov/nasa/jpf/util/IdentityArrayObjectSetTest.java
new file mode 100644 (file)
index 0000000..7a07ba4
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * regression test for IdentityArrayObjectSet
+ */
+public class IdentityArrayObjectSetTest extends TestJPF {
+
+  @Test
+  public void testBasic(){
+    String a = "a";
+    String b = "b";
+    
+    IdentityArrayObjectSet<String> s = new IdentityArrayObjectSet<String>();
+    
+    assertTrue(  s.isEmpty());
+    System.out.println(s);
+
+    assertTrue(  s.add(a));
+    assertTrue(  s.size() == 1);
+    assertTrue(  s.contains(a));
+    System.out.println(s);
+    
+    assertFalse( s.add(a)); // already in
+    assertTrue(  s.size() == 1);
+    System.out.println(s);
+    
+    assertTrue(  s.add(b));
+    assertTrue(  s.size() == 2);
+    assertTrue(  s.contains(b));
+    System.out.println(s);
+
+    assertTrue(  s.remove(a));
+    assertTrue(  s.size() == 1);
+    assertFalse( s.contains(a));
+    assertTrue(  s.contains(b));
+    System.out.println(s);
+    
+    assertFalse( s.remove(a)); // can't remove it a second time
+    System.out.println(s);
+    
+    assertTrue(  s.remove(b));
+    assertTrue(  s.isEmpty());
+    System.out.println(s);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/IntTableTest.java b/src/tests/gov/nasa/jpf/util/IntTableTest.java
new file mode 100644 (file)
index 0000000..0afad51
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * unit test for IntTable
+ */
+public class IntTableTest extends TestJPF {
+
+  @Test
+  public void testBasicPut(){
+    IntTable<Integer> tbl = new IntTable<Integer>();
+
+    assert tbl.size() == 0;
+    final int N = 5000;
+
+    for (int i=0; i<N; i++){
+      tbl.put(i, i);
+
+      IntTable.Entry<Integer> e = tbl.get(i);
+      assert e.val == i;
+    }
+
+    assert tbl.size() == N;
+  }
+
+  
+  @Test
+  public void testStringKeyAdd(){
+    IntTable<String> tbl = new IntTable<String>();
+
+    assert tbl.size() == 0;
+    final int N = 5000;
+
+    for (int i=0; i<N; i++){
+      String key = "averylonginttablekey-" + i;
+      tbl.add(key, i);
+
+      IntTable.Entry<String> e = tbl.get(key);
+      assert e.val == i;
+    }
+
+    assert tbl.size() == N;
+  }
+
+  @Test
+  public void testClone(){
+    IntTable<String> tbl = new IntTable<String>(3); // make it small so that we rehash
+
+    tbl.add("1", 1);
+    tbl.add("2", 2);
+    tbl.add("3", 3);
+
+    IntTable<String> t1 = tbl.clone();
+
+    for (int i=10; i<20; i++){
+      t1.add(Integer.toString(i), i);
+    }
+
+    assert tbl.size() == 3;
+    System.out.println("-- original table");
+    for (IntTable.Entry<String> e : tbl){
+      assert Integer.parseInt(e.key) == e.val;
+      System.out.println(e);
+    }
+
+    assert t1.size() == 13;
+    System.out.println("-- cloned+modified table");
+    for (IntTable.Entry<String> e : t1){
+      assert Integer.parseInt(e.key) == e.val;
+      System.out.println(e);
+    }
+  }
+  
+  @Test
+  public void testSnapshot (){
+    IntTable<String> tbl = new IntTable<String>();
+    
+    tbl.add("1", 1);
+    tbl.add("2", 2);
+    tbl.add("3", 3);
+    tbl.add("12345", 12345);
+    tbl.dump();
+    
+    IntTable.Snapshot<String> snap = tbl.getSnapshot();
+    
+    tbl.remove("3");
+    tbl.remove("1");
+    tbl.add("42", 42);
+    tbl.dump();
+    
+    assertTrue(tbl.size() == 3);
+    
+    tbl.restore(snap);
+    tbl.dump();
+    
+    assertTrue(tbl.size() == 4);
+    assertTrue(tbl.hasEntry("1"));
+    assertTrue(tbl.hasEntry("2"));
+    assertTrue(tbl.hasEntry("3"));
+    assertTrue(tbl.hasEntry("12345"));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/IntVectorTest.java b/src/tests/gov/nasa/jpf/util/IntVectorTest.java
new file mode 100644 (file)
index 0000000..3dfe1a6
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * unit test for gov.nasa.jpf.util.IntVector
+ */
+public class IntVectorTest extends TestJPF {
+
+  @Test
+  public void testPackedBoolean() {
+    IntVector v = new IntVector();
+    boolean[] b = { true,true,true,true, false,false,false,false };
+
+    v.appendPacked(b);
+    int x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xf0000000;
+
+    v.clear();
+    b = new boolean[32];
+    b[0] = true;
+    b[31] = true;
+    v.appendPacked(b);
+    assert v.size() == 1;
+    x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x80000001;
+
+    v.clear();
+    b = new boolean[33];
+    b[0] = true;
+    b[32] = true;
+    v.appendPacked(b);
+    assert v.size() == 2;
+    x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x80000000;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x80000000;
+
+    v.clear();
+    b = new boolean[34];
+    b[0] = true;
+    b[33] = true;
+    v.appendPacked(b);
+    assert v.size() == 2;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x40000000;
+  }
+
+  @Test
+  public void testPackedByte() {
+    IntVector v = new IntVector();
+    byte[] b = { (byte)0xf, 0, (byte)0xf, 0 };
+
+    v.appendPacked(b);
+    assert v.size() == 1;
+    int x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x0f000f00;
+
+    v.clear();
+    b = new byte[5];
+    b[0] = (byte)0xff;
+    b[4] = (byte)0xff;
+    v.appendPacked(b);
+    assert v.size() == 2;
+    x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xff000000;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xff000000;
+
+    v.clear();
+    b = new byte[6];
+    b[0] = (byte)0xff;
+    b[5] = (byte)0xff;
+    v.appendPacked(b);
+    assert v.size() == 2;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x00ff0000;
+
+  }
+
+  @Test
+  public void testPackedChar() {
+    IntVector v = new IntVector();
+    char[] c = { (char)0xffff, 0};
+
+    v.appendPacked(c);
+    int x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert v.size() == 1;
+    assert x == 0xffff0000;
+
+    v.clear();
+    c = new char[3];
+    c[0] = (char)0xffff;
+    c[2] = (char)0xff;
+    v.appendPacked(c);
+    assert v.size() == 2;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0x00ff0000;
+  }
+
+  @Test
+  public void testLongBits(){
+    IntVector v = new IntVector();
+
+    long[] a = { 1, 0x00000000ffff0000L};
+    v.appendBits(a);
+    assert v.size() == 4;
+
+    int x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 1;
+    x = v.get(2);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0;
+    x = v.get(3);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xffff0000;
+
+    a = new long[1];
+    a[0] = -1;
+    v.clear();
+    v.appendBits(a);
+    x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    x = v.get(0);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xffffffff;
+    x = v.get(1);
+    System.out.println(Integer.toHexString(x));
+    assert x == 0xffffffff;
+
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/util/LimitedInputStreamTest.java b/src/tests/gov/nasa/jpf/util/LimitedInputStreamTest.java
new file mode 100644 (file)
index 0000000..62d48c4
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.SecureRandom;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class LimitedInputStreamTest extends TestJPF {
+
+  private static final SecureRandom s_random = new SecureRandom();
+
+  private byte m_expected[];
+  private ByteArrayInputStream m_source;
+  private LimitedInputStream m_fixture;
+
+  @Before
+  public void before() {
+    m_expected = new byte[10];
+
+    s_random.nextBytes(m_expected);
+
+    m_source = new ByteArrayInputStream(m_expected);
+    m_fixture = new LimitedInputStream(m_source);
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void constructorNullArg() {
+    new LimitedInputStream(null);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void setLimitNegArg() {
+    m_fixture.setLimit(-1);
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void readNullBuffer() throws IOException {
+    m_fixture.read(null, 0, 0);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readNegOffset() throws IOException {
+    m_fixture.read(m_expected, -1, 1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readNegLength() throws IOException {
+    m_fixture.read(m_expected, 0, -1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readOffsetLengthTooLong() throws IOException {
+    m_fixture.read(m_expected, 5, 6);
+  }
+
+  @Test
+  public void setLimitZero() {
+    m_fixture.setLimit(0);
+
+    assertEquals(0, m_fixture.getLimit());
+  }
+
+  @Test
+  public void setLimitMax() {
+    m_fixture.setLimit(Integer.MAX_VALUE);
+
+    assertEquals(Integer.MAX_VALUE, m_fixture.getLimit());
+  }
+
+  @Test
+  public void readMaxLimit() throws IOException {
+    read(Integer.MAX_VALUE);
+  }
+
+  @Test
+  public void readExactLimit() throws IOException {
+    read(m_expected.length);
+  }
+
+  @Test
+  public void readShortLimit() throws IOException {
+    read(m_expected.length / 2);
+  }
+
+  @Test
+  public void readZeroLimit() throws IOException {
+    read(0);
+  }
+
+  private void read(int limit) throws IOException {
+    int i;
+
+    m_fixture.setLimit(limit);
+
+    limit = Math.min(limit, m_expected.length);
+
+    for (i = 0; i < limit; i++) {
+      assertEquals(limit - i, m_fixture.available());
+      assertEquals(m_expected[i] & 0x00FF, m_fixture.read());
+    }
+
+    assertEquals(-1, m_fixture.read());
+    assertEquals(0, m_fixture.available());
+  }
+
+  @Test
+  public void readBufferZeroLength() throws IOException {
+    assertEquals(0, m_fixture.read(m_expected, 0, 0));
+  }
+
+  @Test
+  public void readBufferZeroLimit() throws IOException {
+    assertEquals(-1, m_fixture.read(m_expected, 0, 1));
+  }
+
+  @Test
+  public void readBufferShortLimit() throws IOException {
+    int i, length;
+    byte actual[];
+
+    length = m_expected.length / 2;
+    actual = new byte[m_expected.length];
+
+    m_fixture.setLimit(length);
+
+    assertEquals(length, m_fixture.read(actual, 0, actual.length));
+    assertEquals(-1, m_fixture.read());
+    assertEquals(0, m_fixture.available());
+
+    for (i = 0; i < length; i++) {
+      assertEquals(m_expected[i], actual[i]);
+    }
+
+    for (i = length; i < actual.length; i++) {
+      assertEquals(0, actual[i]);
+    }
+  }
+
+  @Test
+  public void readBufferEOF() throws IOException {
+    int i;
+    byte actual[];
+
+    actual = new byte[m_expected.length];
+
+    m_fixture.setLimit(Integer.MAX_VALUE);
+
+    assertEquals(actual.length, m_fixture.read(actual));
+
+    for (i = actual.length; --i >= 0;) {
+      assertEquals(m_expected[i], actual[i]);
+    }
+
+    assertEquals(-1, m_fixture.read(actual));
+  }
+
+  @Test
+  public void skipZeroLimit() throws IOException {
+    assertEquals(0, m_fixture.skip(1));
+  }
+
+  @Test
+  public void skipShortLimit() throws IOException {
+    int length;
+
+    length = m_expected.length / 2;
+
+    m_fixture.setLimit(m_expected.length);
+
+    assertEquals(length, m_fixture.skip(length));
+  }
+
+  @Test
+  public void skipEOF() throws IOException {
+    m_fixture.setLimit(Integer.MAX_VALUE);
+
+    assertEquals(m_expected.length, m_fixture.skip(Integer.MAX_VALUE));
+    assertEquals(0, m_fixture.skip(1));
+  }
+
+  @Test
+  public void close() throws IOException {
+    CountClose m_counter;
+
+    m_counter = new CountClose();
+    m_fixture = new LimitedInputStream(m_counter);
+
+    m_fixture.setLimit(5);
+    m_fixture.close();
+
+    assertEquals(1, m_counter.getCloseCount());
+    assertEquals(0, m_fixture.getLimit());
+  }
+
+  private static class CountClose extends InputStream {
+
+    private int m_close;
+
+    @Override
+       public int read() {
+      return (0);
+    }
+
+    @Override
+       public void close() {
+      m_close++;
+    }
+
+    public int getCloseCount() {
+      return (m_close);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/LocationSpecTest.java b/src/tests/gov/nasa/jpf/util/LocationSpecTest.java
new file mode 100644 (file)
index 0000000..d63ba9d
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * unit test for gov.nasa.jpf.util.LocationSpec
+ */
+public class LocationSpecTest extends TestJPF {
+
+  @Test
+  public void testSingleLocation() {
+    LocationSpec ls = LocationSpec.createLocationSpec("Foobar.java:42");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.matchesFile("Foobar.java"));
+    assertTrue(!ls.isLineInterval());
+    assertTrue(ls.getLine() == 42);
+
+    assertFalse(ls.matchesFile("Bull"));
+
+    assertTrue(ls.matchesFile("/x/y/Foobar.java"));
+  }
+
+  @Test
+  public void testAbsoluteLocation() {
+    LocationSpec ls = LocationSpec.createLocationSpec("/x/y/z/Foobar.java:42");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.matchesFile("/x/y/z/Foobar.java"));
+  }
+
+  @Test
+  public void testPlatformLocation() {
+    LocationSpec ls = LocationSpec.createLocationSpec("C:\\x\\y\\z\\Foobar.java:42");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.matchesFile("C:\\x\\y\\z\\Foobar.java"));
+    assertTrue(ls.getLine() == 42);
+  }
+
+
+  @Test
+  public void testRelativeLocation() {
+    LocationSpec ls = LocationSpec.createLocationSpec("x/y/z/Foobar.java:42");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.matchesFile("x/y/z/Foobar.java"));
+  }
+
+  @Test
+  public void testWildcards() {
+    LocationSpec ls = LocationSpec.createLocationSpec("x/*/Foo*.java:42");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.matchesFile("x/y/z/Foobar.java"));
+    assertTrue(ls.matchesFile("Fooboo.java"));
+  }
+
+  @Test
+  public void testAbsoluteRange(){
+    LocationSpec ls = LocationSpec.createLocationSpec("Foobar.java:42-48");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.isLineInterval());
+    assertTrue(ls.getFromLine() == 42);
+    assertTrue(ls.getToLine() == 48);
+  }
+
+  @Test
+  public void testRelativeRange(){
+    LocationSpec ls = LocationSpec.createLocationSpec("Foobar.java:42+6");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.isLineInterval());
+    assertTrue(ls.getFromLine() == 42);
+    assertTrue(ls.getToLine() == 48);
+  }
+
+  @Test
+  public void testOpenRange(){
+    LocationSpec ls = LocationSpec.createLocationSpec("Foobar.java:42+");
+    System.out.println("# testing: " + ls);
+
+    assertTrue(ls.isLineInterval());
+    assertTrue(ls.getFromLine() == 42);
+    assertTrue(ls.getToLine() == Integer.MAX_VALUE);
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/util/MethodSpecTest.java b/src/tests/gov/nasa/jpf/util/MethodSpecTest.java
new file mode 100644 (file)
index 0000000..0629082
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * unit test for MethodSpecs
+ */
+public class MethodSpecTest extends TestJPF {
+
+  @Test
+  public void testConstruction(){
+
+    //-- should be all non-null
+    String spec = "x.y.Foo.bar(java.lang.String,^float[])";
+    MethodSpec ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = "x.y.Foo+.*";
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = "*.foo(^int, ^double)";
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = "( ^int, ^double)";
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = ".foo";
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = ".(int)";
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms != null);
+
+    spec = "!java.*.*";  // first '*' belongs to class spec, second to method
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(ms);
+    assertTrue(ms != null);
+
+    spec = "java.*"; // not what you think - the class spec is "java" and the method is "*"
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(ms);
+    
+
+    //--- should all produce null
+
+    spec = "*.foo(^int, ^double";  // missing ')'
+    ms = MethodSpec.createMethodSpec(spec);
+    System.out.println(spec + " => " + ms);
+    assertTrue(ms == null);
+
+
+    //System.out.println("matches (java.lang.Object,*): " +
+    //                    ms.matches("java.lang.Object", "*"));
+
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/OATHashTest.java b/src/tests/gov/nasa/jpf/util/OATHashTest.java
new file mode 100644 (file)
index 0000000..4dbc437
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import java.util.HashSet;
+import java.util.Random;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import static gov.nasa.jpf.util.OATHash.*;
+
+/**
+ * just the very basic sanity checks for a hash function.
+ * <2do> should add a test for uniformity of hash vals and measure collisions in medium bin size constraints
+ */
+public class OATHashTest extends TestJPF {
+
+  @Test
+  public void testRandom(){
+    int maxRounds = 256;
+    int maxKey = 8;
+    Random r = new Random(42);
+    HashSet<Integer> seen = new HashSet<Integer>(); // not very smart, but the number of rounds is reasonably small
+    
+    for (int i=0; i<maxRounds; i++){
+      int h = 0;
+      for (int j=0; j<maxKey; j++){
+        int x = r.nextInt();
+        h = hashMixin(h, x);
+        
+        if (j>0){
+          System.out.print(',');
+        }
+        System.out.print(Integer.toHexString(x));
+      }
+      h = hashFinalize(h);
+      System.out.print(" => ");
+      System.out.println(h);
+      
+      if (seen.contains(h)){
+        fail("collision on round " + i);
+      }
+      seen.add(h);
+    }
+  }
+  
+  @Test
+  public void testRandomSmall(){
+    int maxRounds = 256;
+    int maxKey = 4;
+    int maxVal = 32;
+    Random r = new Random(42);
+    HashSet<Integer> seen = new HashSet<Integer>(); // not very smart, but the number of rounds is reasonably small
+    
+    for (int i=0; i<maxRounds; i++){
+      int h = 0;
+      for (int j=0; j<maxKey; j++){
+        int x = r.nextInt(maxVal);
+        h = hashMixin(h, x);
+        
+        if (j>0){
+          System.out.print(',');
+        }
+        System.out.print(x);
+      }
+      h = hashFinalize(h);
+      System.out.print(" => ");
+      System.out.println(h);
+      
+      if (seen.contains(h)){
+        fail("collision on round " + i);
+      }
+      seen.add(h);
+    }
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/util/ObjVectorTest.java b/src/tests/gov/nasa/jpf/util/ObjVectorTest.java
new file mode 100644 (file)
index 0000000..a2eab3a
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+/**
+ * regression test for ObjVector
+ */
+public class ObjVectorTest extends TestJPF {
+
+  void assertEquals( ObjVector<Integer> v1, ObjVector<Integer> v2) {
+    assertTrue( v1.size() == v2.size());
+
+    int n = v1.size();
+    for (int i=0; i<n; i++) {
+      Object a = v1.get(i);
+      Object b = v2.get(i);
+      if (a == null) {
+        assertTrue( b == null);
+      } else {
+        assertTrue( a.equals(b));
+      }
+    }
+  }
+  
+  @Test
+  public void testSnapshot () {
+    ObjVector<Integer> v = new ObjVector<Integer>(100);
+    
+    // all empty snapshot
+    ObjVector.Snapshot<Integer> snap = v.getSnapshot();
+    Integer val = Integer.valueOf(42);
+    v.set(0,  val);
+    assertTrue(v.size() == 1 && v.get(0) == val);
+    v.restore(snap);
+    assertTrue(v.size() == 0 && v.get(0) == null);
+    
+    //--- all full snapshot
+    for (int i=0; i<100; i++) {
+      v.set(i, i);
+    }
+    ObjVector<Integer> v0 = v.clone();
+    ObjVector.Snapshot<Integer> snap0 = v.getSnapshot();
+    v.clear();
+    v.restore(snap0);
+    assertEquals( v0, v);
+    
+    //--- punch holes into it
+    v.setRange(11,  20, null);
+    v.set( 25,null);
+    v.set( 26, null);
+    v.set( 42, null);
+    v.setRange(70, 85, null);
+    ObjVector.Snapshot<Integer> snap1 = v.getSnapshot();    
+    ObjVector<Integer> v1 = v.clone();
+    v.clear();
+    v.restore(snap1);
+    //v.printOn( System.out);
+    assertEquals( v1, v);
+    
+    //--- chop off the ends
+    v.restore(snap0);
+    v.setRange(81, 99, null);
+    v.setRange(0, 19, null);
+    ObjVector.Snapshot<Integer> snap2 = v.getSnapshot();    
+    ObjVector<Integer> v2 = v.clone();
+    v.clear();
+    v.restore(snap2);
+    assertEquals( v2, v); 
+  }
+  
+  //--- mutating snapshot
+  
+  static class X {
+    int val;
+    X (Integer o) { val = o.intValue(); }
+  }
+  
+  static class IXTransformer implements Transformer<Integer,X> {
+    @Override
+       public X transform( Integer obj) {
+      return new X(obj);
+    }
+  }
+  
+  static class XITransformer implements Transformer<X,Integer>{
+    @Override
+       public Integer transform( X obj) {
+      return Integer.valueOf(obj.val);
+    }
+  }
+  
+  @Test
+  public void testMutatingSnapshot() {
+    ObjVector<Integer> v = new ObjVector<Integer>(100);
+
+    for (int i=0; i<100; i+=2) {
+      v.set(i, Integer.valueOf(i));
+    }
+    print(v);
+    
+    System.out.println("now storing snapshot for ObjVector of size " + v.size());
+    ObjVector.MutatingSnapshot<Integer,X> snap = v.getSnapshot(new IXTransformer());
+    
+    System.out.println("now modifying ObjVector");
+    v.clear();
+    v.setRange(0, 30, Integer.valueOf(42));
+    print(v);
+    
+    System.out.println("now restoring ObjVector");
+    v.restore(snap, new XITransformer());
+    
+    int n = print(v);
+    assert n == 50 : "got wrong number of non-null elements: " + n;
+  }
+  
+  // utilities
+  
+  int print(ObjVector<Integer> v) {
+    int n = 0;
+    for (int i=0; i<=v.size(); i++) {
+      Integer e = v.get(i);
+      if (e != null) {
+        if (n++ > 0) {
+          System.out.print(',');
+        }
+        System.out.print(e);
+      }
+    }
+    System.out.println();
+    
+    return n;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/ObjectListTest.java b/src/tests/gov/nasa/jpf/util/ObjectListTest.java
new file mode 100644 (file)
index 0000000..5e71bea
--- /dev/null
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * regression unit test for ObjectList API
+ */
+public class ObjectListTest extends TestJPF {
+  
+  Object attr;
+  
+  @Test
+  public void testAdd(){
+    assertTrue( ObjectList.isEmpty(attr));
+    
+    attr = ObjectList.add(attr, "one");
+    assertTrue( ObjectList.size(attr) == 1);
+    assertTrue( attr != null && attr.equals("one"));
+    
+    attr = ObjectList.add(attr, new Integer(2));
+    assertTrue( ObjectList.size(attr) == 2);
+    assertTrue( attr != null && !(attr instanceof Integer));
+  }
+  
+  
+  @Test
+  public void testListIteration() {
+    Object[] v = { new Integer(2), "one" };
+    
+    for (Object a: ObjectList.iterator(attr)){
+      fail("list should be empty");
+    }
+    
+    for (int i=v.length-1; i>=0; i--){
+      attr = ObjectList.add(attr, v[i]);
+    }
+    
+    int len = ObjectList.size(attr);
+    for (int i=0; i<len; i++){
+      Object a = ObjectList.get(attr, i);
+      System.out.printf("[%d] = %s\n", i, a.toString());
+      assertEquals( a, v[i]);
+    }
+
+    attr = ObjectList.add(attr, "three");
+    attr = ObjectList.add(attr, new Integer(4));
+    
+    int i=0;
+    for (Integer a = ObjectList.getFirst(attr, Integer.class); a!=null; a = ObjectList.getNext(attr, Integer.class, a)){
+      System.out.println(a);
+      i++;
+    }
+    assertTrue( i == 2);
+
+    System.out.println("-- untyped iterator");
+    i=0;
+    for (Object a: ObjectList.iterator(attr)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }
+    assertTrue( i == 4);
+    
+    System.out.println("-- typed iterator (all Strings)");
+    i=0;
+    for (String a: ObjectList.typedIterator(attr, String.class)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }
+    assertTrue(i == 2);
+    
+    System.out.println("-- typed iterator (all Floats) => none");
+    i=0;
+    for (float f: ObjectList.typedIterator(attr, Float.class)){
+      System.out.printf("[%d] = %f\n", i++, f);
+    }
+    assertTrue(i == 0);
+    
+  }
+  
+  @Test
+  public void testSingleValueIteration(){
+    attr = ObjectList.add(attr, "one");
+    
+    System.out.println("-- untyped iterator");
+    int i=0;
+    for (Object a: ObjectList.iterator(attr)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }
+    assertTrue( i == 1);
+    
+    System.out.println("-- typed iterator (all Strings)");
+    i=0;
+    for (String a: ObjectList.typedIterator(attr, String.class)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }
+    assertTrue(i == 1);
+    
+    System.out.println("-- typed iterator (all Floats) => none");
+    i=0;
+    for (float f: ObjectList.typedIterator(attr, Float.class)){
+      System.out.printf("[%d] = %f\n", i++, f);
+    }
+    assertTrue(i == 0);
+  }
+  
+  
+  @Test
+  public void testRemove(){
+    attr = ObjectList.add(attr, "one");
+    
+    attr = ObjectList.remove(attr, attr);
+    assertTrue(attr == null);
+    
+    String one = "one";
+    String two = "two";
+    attr = ObjectList.add(attr, one);
+    attr = ObjectList.add(attr, two);
+    attr = ObjectList.remove( attr, one);
+    assertTrue(attr != null && attr == two);
+    
+    attr = one;
+    attr = ObjectList.add(attr, two);
+    attr = ObjectList.remove( attr, two);
+    assertTrue(attr != null && attr == one);
+   
+    
+    attr = one;
+    attr = ObjectList.add(attr, two);
+    attr = ObjectList.add(attr, "three");
+    attr = ObjectList.remove( attr, two);
+    int i=0;
+    for (Object a: ObjectList.iterator(attr)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }    
+    assertTrue( ObjectList.size(attr) == 2);
+  }
+  
+  @Test
+  public void testReplace(){
+    String one = "one";
+    attr = ObjectList.add(attr, one);
+    
+    Integer i1 = new Integer(1);
+    attr = ObjectList.replace(attr, one, i1);
+    assertTrue(attr == i1);
+    
+    String two = "two";
+    Integer i2 = new Integer(2);
+    attr = ObjectList.add(attr, two);
+    attr = ObjectList.replace(attr, two, i2);
+    Integer o = ObjectList.getFirst(attr, Integer.class);
+    assertTrue( o == i2);
+    
+    int i=0;
+    for (Object a: ObjectList.iterator(attr)){
+      System.out.printf("[%d] = %s\n", i++, a.toString());
+    }    
+    assertTrue( ObjectList.size(attr) == 2);
+  }
+  
+  
+  Object a1, a2;
+  
+  @Test
+  public void testEquals(){
+    assertTrue( ObjectList.equals(a1, a2));
+    
+    a1 = null;
+    a2 = "one";
+    assertFalse( ObjectList.equals(a1, a2));
+    
+    a1 = "one";
+    a2 = null;
+    assertFalse( ObjectList.equals(a1, a2));
+
+    a1 = ObjectList.createList("one", "two");
+    a2 = null;
+    assertFalse( ObjectList.equals(a1, a2));
+
+    a1 = "one";
+    a2 = "one";
+    assertTrue( ObjectList.equals(a1, a2));
+    
+    a1 = ObjectList.createList("one", "two");
+    a2 = "one";
+    assertFalse( ObjectList.equals(a1, a2));
+
+    a1 = "one";
+    a2 = ObjectList.createList("one", "two");
+    assertFalse( ObjectList.equals(a1, a2));
+    
+    a1 = ObjectList.createList("one", "two");
+    a2 = ObjectList.createList("one", "two");
+    assertTrue( ObjectList.equals(a1, a2));
+    
+    a1 = ObjectList.createList("one", "two", "three");
+    a2 = ObjectList.createList("one", "two");
+    assertFalse( ObjectList.equals(a1, a2));
+
+    a1 = ObjectList.createList("one", "two");
+    a2 = ObjectList.createList("one", "two", "three");
+    assertFalse( ObjectList.equals(a1, a2));
+  }
+  
+  
+  static class A implements Cloneable {
+    String id;
+    
+    A (String id){
+      this.id = id;
+    }
+    
+    @Override
+       public int hashCode(){
+      return id.hashCode();
+    }
+    
+    @Override
+       public boolean equals (Object o){
+      if (o instanceof A){
+        return id.equals(((A)o).id);
+      } else {
+        return false;
+      }
+    }
+    
+    @Override
+       public Object clone(){
+      try {
+        A a = (A)super.clone();
+        a.id += "_clone";
+        return a;
+        
+      } catch (CloneNotSupportedException ex) {
+        return null;
+      }
+    }
+    
+    @Override
+       public String toString(){
+      return id;
+    }
+  }
+  
+  static class B extends A implements CloneableObject {
+    B (String id){
+      super(id);
+    }
+  }
+  
+  @Test
+  public void testClone(){
+    Object l1, l1c, l2;
+    
+    // lists with generic data clone()
+    l1 = ObjectList.createList(new A("one"), new A("two"));
+    l2 = ObjectList.createList(new A("one_clone"), new A("two_clone"));    
+    try {
+      l1c = ObjectList.clone(l1);
+      assertTrue( ObjectList.equals(l1c, l2));
+    } catch (CloneNotSupportedException cnsx){
+      fail("A.clone() did throw CloneNotSupportedException");
+    }
+    
+    // clone() attempt of non-Cloneables
+    l1 = "one";
+    l2 = "one_clone";
+    try {
+      l1c = ObjectList.clone(l1);
+      fail("Strings are not cloneable");
+    } catch (CloneNotSupportedException cnsx){
+      // this is Ok
+    }
+
+    // single object clone
+    l1 = new A("one");
+    try {
+      l1c = ObjectList.clone(l1);
+      assertTrue( l1.equals( new A("one")));
+    } catch (CloneNotSupportedException cnsx){
+      fail("object clone failed");
+    }
+
+    // null clone
+    l1 = null;
+    try {
+      l1c = ObjectList.clone(l1);
+      assertTrue( l1c == null);
+    } catch (CloneNotSupportedException cnsx){
+      fail("empty list clone failed");
+    }
+    
+    // single object clone of CloneableObject instance
+    l1 = new B("one");
+    try {
+      l1c = ObjectList.clone(l1);
+      assertTrue( l1.equals( new B("one")));
+    } catch (CloneNotSupportedException cnsx){
+      fail("object clone failed");
+    }
+    
+    // list clone with CloneableObject instances
+    l1 = ObjectList.createList(new B("one"), new B("two"));
+    l2 = ObjectList.createList(new A("one_clone"), new A("two_clone"));
+    try {
+      l1c = ObjectList.clone(l1);
+      assertTrue( ObjectList.equals(l1c, l2));
+    } catch (CloneNotSupportedException cnsx){
+      fail("B.clone() did throw CloneNotSupportedException");
+    }
+  }
+    
+  @Test
+  public void testHash() {
+    // test single object and list hash
+    // test if list nodes are hash transparent
+    
+    // test equals
+    Object l = null;
+    HashData hd = new HashData();
+    ObjectList.hash(l, hd);
+    HashData hd1 = new HashData();
+    assertTrue( hd.getValue() == hd1.getValue());
+        
+    l = "one";
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("one");    
+    assertTrue( hd.getValue() == hd1.getValue());
+    
+    l = ObjectList.createList(new A("one"), new A("two"));
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("one");
+    hd1.add("two");
+    assertTrue( hd.getValue() == hd1.getValue());
+
+    
+    // test non-equals
+    l = null;
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("one");
+    assertTrue( hd.getValue() != hd1.getValue());
+
+    l = "one";
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    assertTrue( hd.getValue() != hd1.getValue());
+    
+    l = "one";
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("two");    
+    assertTrue( hd.getValue() != hd1.getValue());
+    
+    l = ObjectList.createList(new A("one"), new A("two"));
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("one");
+    assertTrue( hd.getValue() != hd1.getValue());
+    
+    l = ObjectList.createList(new A("one"), new A("two"));
+    hd = new HashData();
+    ObjectList.hash(l, hd);
+    hd1 = new HashData();
+    hd1.add("two");
+    hd1.add("one");
+    assertTrue( hd.getValue() != hd1.getValue());
+
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/PSIntMapTest.java b/src/tests/gov/nasa/jpf/util/PSIntMapTest.java
new file mode 100644 (file)
index 0000000..75ba4df
--- /dev/null
@@ -0,0 +1,443 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.util.Arrays;
+import java.util.BitSet;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import org.junit.Test;
+
+/**
+ * regression test for PSIntMap
+ */
+public class PSIntMapTest extends TestJPF {
+
+  PSIntMap<Integer> createPersistentIntMap(){
+    return new PSIntMap<Integer>();  
+  }
+  
+  PSIntMap<Integer> set (PSIntMap<Integer> m, int i){
+    return m.set(i, Integer.valueOf(i));
+  }
+  
+  PSIntMap<Integer> set (PSIntMap<Integer> m, int[] data){
+    for (int i=0; i<data.length; i++){
+      int v = data[i];
+      if (v >= 0){
+        m = m.set( v, Integer.valueOf(v));
+      } else {
+        m = m.remove(-v);
+      }
+    }
+    return m;
+  }
+  
+  static class IntegerProcessor implements Processor<Integer>{
+    int count=0;
+    
+    @Override
+       public void process( Integer i){
+      if (count++ > 0){
+        System.out.print(',');
+      }
+      System.out.print(i);
+    }
+    
+    public int getCount(){
+      return count;
+    }
+  }
+  
+  static void dump (String prefix, PSIntMap<Integer> map, String postfix){
+    if (prefix != null) {
+      System.out.print(prefix);
+      System.out.print(' ');
+    }
+    
+    System.out.print(map.getClass().getSimpleName() + " {");
+    map.process( new IntegerProcessor());
+    System.out.print('}');
+    
+    if (postfix != null){
+      System.out.print(' ');
+      System.out.print(postfix);
+    }    
+  }
+  
+  static void dump (PSIntMap<Integer> map){
+    dump(null,map, "\n");
+  }
+  
+  static void assertNullValue (PSIntMap<Integer> map, int key){
+    Integer v = map.get(key);
+    if (v != null){
+      fail("non-null value for: " + key + " = " + v);
+    }
+  }
+
+  static void assertNonNullValue (PSIntMap<Integer> map, int key){
+    Integer v = map.get(key);
+    if (v == null || v.intValue() != key){
+      fail("wrong value for: " + key + " = " + v);
+    }
+  }
+
+  static void assertEquals( PSIntMap<Integer> map, int[] data){
+    int[] a = data.clone();
+    Arrays.sort(a);
+    
+    System.out.print("assertEquals {");
+    for (int i=0; i<data.length; i++){
+      if (i > 0){ 
+        System.out.print(',');
+      }
+      System.out.print(a[i]);
+    }
+    System.out.println('}');
+    
+    assertTrue( map.size() == data.length);
+    
+    int[] b = new int[data.length];
+    int j=0;
+    for (Integer v : map){
+      int i = v.intValue();
+      b[j++] = i;
+    }
+    Arrays.sort(b);
+    
+    for (int i=0; i<a.length; i++){
+      if (a[i] != b[i]){
+        fail("different values : " + a[i] + ',' + b[i]);
+      }
+    }
+  }
+  
+  //--- the tests
+  
+  @Test
+  public void testSingleAdd(){
+    PSIntMap<Integer> m = createPersistentIntMap();
+    assertTrue( m.size() == 0);
+    
+    m = set( m, 0);
+    dump("0: ", m, "\n");
+    assertTrue( m.size() == 1);
+    assertNonNullValue( m, 0);
+    assertNullValue( m, 42);
+    
+    m = new PSIntMap<Integer>();
+    m = set( m, 42);
+    dump("42: ", m, "\n");
+    assertTrue( m.size() == 1);
+    assertNullValue( m, 0);
+    assertNonNullValue( m, 42);
+
+    int k = 32*32*32*32 + 1;
+    m = new PSIntMap<Integer>();
+    m = set( m, k);    
+    dump("32**4 + 1: ", m, "\n");
+    assertTrue( m.size() == 1);
+    assertNullValue( m, 0);
+    assertNonNullValue( m, k);
+    m.printOn(System.out);    
+  }  
+  
+  @Test
+  public void testMultiAdd(){
+    PSIntMap<Integer> m = createPersistentIntMap();
+    
+    int[] data = { 0,1, 32, 4,10, 666,669, 36, 37 }; 
+    m = set( m, data);
+    
+    dump(m);
+    //m.printOn(System.out);
+    
+    assertEquals( m, data);    
+  }
+  
+  @Test
+  public void testConsecutiveAdd(){
+    int len = 32*32*32;
+    PSIntMap<Integer> m = createPersistentIntMap();
+    for (int i=0; i<len; i++){
+      m = set(m, i);
+    }
+        
+    for (int i=0; i<len; i++){
+      Integer v = m.get(i);
+      assertNonNullValue( m, i);
+    }
+    
+    System.out.println("m.size() = " + m.size());
+    assertTrue(m.size() == len);
+  }
+
+  @Test
+  public void testConsecutiveAddRemove(){
+    int len = 32*32*32;
+    PSIntMap<Integer> m = createPersistentIntMap();
+    for (int i=0; i<len; i++){
+      m = set(m, i);
+    }
+    
+    for (int i=0; i<len; i++){
+      Integer v = m.get(i);
+      assertNonNullValue( m, i);
+    }
+    
+    for (int i=len-1; i>= 0; i--){
+      m = m.remove(i);
+    }
+    
+    System.out.println("m.size() = " + m.size());
+    assertTrue(m.size() == 0);
+  }
+  
+  @Test
+  public void testPredicateRemoval(){
+    PSIntMap<Integer> m = createPersistentIntMap();
+    
+    int[] data = { 0,1, 32, 4,10, 666,669, 36, 37, 95,97 }; 
+    m = set( m, data);
+
+    dump("before removal:", m, "\n");
+    
+    Predicate<Integer> pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue(Integer i){
+        return ((i & 1) != 0);
+      }
+    };
+    
+    m = m.removeAllSatisfying(pred);
+    
+    dump("after removal:", m, "\n");
+    m.printOn(System.out);
+  }
+
+  @Test
+  public void testRangePredicateRemoval(){
+    PSIntMap<Integer> m = createPersistentIntMap();
+    int len = 20000;
+    for (int i=0; i<len; i++){
+      m = set(m, i);
+    }
+
+    // completely remove first value node
+    Predicate<Integer> pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue (Integer n){
+        return (n <= 31);
+      }
+    };
+    m = m.removeAllSatisfying(pred);
+    
+    System.out.println("m.size() = " + m.size());
+    assertTrue( m.size() == (len - 32));
+    for (int i=0; i<32; i++){
+      assertTrue( m.get(i) == null);
+    }
+    len -= 32;
+    
+    // remove all but one value from the second node
+    pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue (Integer n){
+        return (n >32 && n <= 63);
+      }
+    };
+    m = m.removeAllSatisfying(pred);
+    System.out.println("m.size() = " + m.size());
+    assertTrue( m.size() == (len - 31));
+    assertTrue( m.get(32) != null);
+    for (int i=33; i<64; i++){
+      assertTrue( m.get(i) == null);
+    }
+    len -= 31;
+    
+    // remove all but one from bitmap node
+    pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue (Integer n){
+        return (n == 64);
+      }
+    };
+    m = m.removeAllSatisfying(pred);
+    pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue (Integer n){
+        return (n >= 64 && n < 95);
+      }
+    };
+    m = m.removeAllSatisfying(pred);
+    for (int i=64; i<95; i++){
+      assertTrue( m.get(i) == null);
+    }    
+    assertTrue( m.get(95) != null);
+  }  
+  
+  @Test
+  public void testHeapPattern(){
+    Random r = new Random(42);
+    final BitSet removed = new BitSet();
+    
+    Predicate<Integer> pred = new Predicate<Integer>(){
+      @Override
+       public boolean isTrue (Integer n){
+        return removed.get(n.intValue());
+      }
+    };
+    
+    PSIntMap<Integer> m = createPersistentIntMap();
+    int max = 20000;
+    for (int i=0; i<max; i++){
+      m = set(m, i);
+      
+      if ((i > 0) && (i % 500) == 0){
+         for (int j=0; j<120; j++){
+           int k = r.nextInt(i);
+           removed.set(k);
+         }
+         
+         m = m.removeAllSatisfying(pred);
+      }
+    }
+    
+    System.out.println("m.size() = " + m.size());
+    int nRemoved = removed.cardinality();
+    assertTrue( m.size() == (max - nRemoved));
+    
+    int n = 0;
+    for (int i=0; i<max; i++){
+      if (removed.get(i)){
+        assertTrue( m.get(i) == null);
+      } else {
+        assertTrue( m.get(i) != null);
+        n++;
+      }
+    }
+    assertTrue( n == (max - nRemoved));
+  }
+    
+  
+  //--- benchmarks
+  
+  final static int NSTATES = 20000;
+  final static int NOBJECTS = 2000;
+  final static int NGC = 400;
+  
+  
+  public void benchmark (){
+    long t1, t2;
+
+    //--- PersistentIntMap
+    Predicate<Integer> pred = new Predicate<Integer>() {
+      @Override
+       public boolean isTrue (Integer o) {
+        int i = o.intValue();
+        return (i < NGC);
+      }
+    };
+    
+    Runtime.getRuntime().gc();
+    t1 = System.currentTimeMillis();
+    for (int l=0; l<NSTATES; l++) {
+      PSIntMap<Integer> t = createPersistentIntMap();
+      
+      //--- allocations
+      for (int i=0; i<NOBJECTS; i++){
+        t = t.set(i,  Integer.valueOf(i));
+      }
+
+      //--- lookup
+      for (int i=0; i<NOBJECTS; i++) {
+        Integer o = t.get(i);
+      }
+      
+      //--- gc
+      t = t.removeAllSatisfying(pred);
+      
+      //--- no store/backtrack costs for container
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("PersistentIntMap (" + NSTATES + " cycles): " + (t2 - t1));
+  
+
+    //--- HashMap
+    Runtime.getRuntime().gc();
+    t1 = System.currentTimeMillis();
+    for (int l=0; l<NSTATES; l++) {
+      HashMap<Integer,Integer> m = new HashMap<Integer,Integer>();
+      //--- allocations
+      for (int i=0; i<NOBJECTS; i++){
+        m.put(i, i);
+      }
+
+      //--- lookup
+      for (int i=0; i<NOBJECTS; i++) {
+        Integer o = m.get(i);
+      }
+      
+      //--- gc
+      for (Iterator<Map.Entry<Integer,Integer>> it = m.entrySet().iterator(); it.hasNext();) {
+        Map.Entry<Integer, Integer> e = it.next();
+        if (pred.isTrue(e.getValue())) {
+          it.remove();
+        }      
+      }
+      
+      //--- 2 x clone (upon store and backtrack)
+      m = (HashMap<Integer,Integer>)m.clone();
+      m = (HashMap<Integer,Integer>)m.clone();
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("HashMap (" + NSTATES + " cycles): " + (t2 - t1));
+
+    //--- ObjVector (needs to be adjusted for holes -> increased size)
+    Runtime.getRuntime().gc();
+    t1 = System.currentTimeMillis();
+    for (int l=0; l<NSTATES; l++) {
+      ObjVector<Integer> v = new ObjVector<Integer>();
+      //--- allocations
+      for (int i=0; i<NOBJECTS; i++){
+        v.set(i, i);
+      }
+
+      //--- lookup
+      for (int i=0; i<NOBJECTS; i++) {
+        Integer o = v.get(i);
+      }
+      
+      //--- gc
+      v.clearAllSatisfying(pred);
+      
+      //--- snap & restore
+      ObjVector.Snapshot<Integer> snap = v.getSnapshot();
+      v.restore(snap);
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("ObjVector (" + NSTATES + " cycles): " + (t2 - t1));
+
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/util/PermutationGeneratorTest.java b/src/tests/gov/nasa/jpf/util/PermutationGeneratorTest.java
new file mode 100644 (file)
index 0000000..f383ddc
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * regression test for PermutationGenerator
+ */
+public class PermutationGeneratorTest extends TestJPF {
+  
+  @Test
+  public void testTotalPermutation(){
+    PermutationGenerator pg = new TotalPermutationGenerator(4);
+    long nPerm = pg.getNumberOfPermutations();
+    assertTrue( nPerm == 24);
+    
+    while (pg.hasNext()){
+      int[] perms = pg.next();
+      assertTrue(perms != null);
+      pg.printOn(System.out);
+    }
+  }
+  
+  @Test
+  public void testPairPermutation(){
+    PermutationGenerator pg = new PairPermutationGenerator(4);
+    long nPerm = pg.getNumberOfPermutations();
+    assertTrue( nPerm == 7);
+    
+    while (pg.hasNext()){
+      int[] perms = pg.next();
+      assertTrue(perms != null);
+      pg.printOn(System.out);
+    }
+  }
+
+  @Test
+  public void testRandomPermutation(){
+    int nPermutations = 14;
+    PermutationGenerator pg = new RandomPermutationGenerator(4, nPermutations, 42);
+    long nPerm = pg.getNumberOfPermutations();
+    assertTrue( nPerm == nPermutations);
+    
+    System.out.println("this CAN have duplicates");
+    while (pg.hasNext()){
+      int[] perms = pg.next();
+      assertTrue(perms != null);
+      pg.printOn(System.out);
+    }    
+  }
+  
+  boolean isEqual (int[] a, int[] b){
+    if (a.length == b.length){
+      for (int i=0; i<a.length; i++){
+        if (a[i] != b[i]){
+          return false;
+        }
+      }
+      return true;
+    }
+    return false;
+  }
+  
+  @Test
+  public void testUniqueRandomPermutation(){
+    int nPermutations = 14;
+    PermutationGenerator pg = new UniqueRandomPermGenerator(4, nPermutations, 42);
+    long nPerm = pg.getNumberOfPermutations();
+    assertTrue( nPerm == nPermutations);
+    
+    int[][] seen = new int[nPermutations][];
+    int n = 0;
+    
+    System.out.println("this should NOT have duplicates");
+    
+    while (pg.hasNext()){
+      int[] perms = pg.next();
+      assertTrue(perms != null);
+      pg.printOn(System.out);
+      
+      for (int i=0; i<n; i++){
+        assertFalse(isEqual(seen[i], perms));
+      }
+      seen[n++] = perms.clone();
+    }    
+  }
+
+  @Test
+  public void testMaxUniqueRandomPermutation(){
+    int nPermutations = 14; // too high, this only has 3! different permutations
+    PermutationGenerator pg = new UniqueRandomPermGenerator(3, nPermutations, 42);
+    long nPerm = pg.getNumberOfPermutations();
+    assertTrue( nPerm == 6);
+
+    while (pg.hasNext()){
+      int[] perms = pg.next();
+      assertTrue(perms != null);
+      pg.printOn(System.out);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/SortedArrayIntSetTest.java b/src/tests/gov/nasa/jpf/util/SortedArrayIntSetTest.java
new file mode 100644 (file)
index 0000000..61b4ff1
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public class SortedArrayIntSetTest extends ArrayIntSetTestBase {
+
+  @Override
+  protected ArrayIntSet createArrayIntSet() {
+    return new SortedArrayIntSet();
+  }
+
+  @Override
+  protected ArrayIntSet createArrayIntSet(int n) {
+    return new SortedArrayIntSet(n);
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/util/SortedArrayObjectSetTest.java b/src/tests/gov/nasa/jpf/util/SortedArrayObjectSetTest.java
new file mode 100644 (file)
index 0000000..47d7621
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+
+/**
+ * regression test for SortedArrayObjectSet
+ */
+public class SortedArrayObjectSetTest extends TestJPF {
+  
+  static class X implements Comparable<X> {
+    String id;
+    int x;
+    
+    X (String id, int x){
+      this.id = id;
+      this.x = x;
+    }
+    
+    @Override
+       public int compareTo (X other){
+      return (x - other.x);
+    }
+    
+    @Override
+       public String toString(){
+      return id;
+    }
+    
+    @Override
+       public boolean equals(Object o){
+      if (o instanceof X){
+        X other = (X)o;
+        if (x == other.x){
+          if (id.equals(other.id)){
+            return true;
+          }
+        }
+      }
+      
+      return false;
+    }
+  }
+  
+  @Test
+  public void testBasic(){
+    SortedArrayObjectSet<X> s = new SortedArrayObjectSet<X>();
+    
+    X o1 = new X("1",1);
+    X o2 = new X("20",20);
+    X o3 = new X("5",5);
+    X o4 = new X("7",7);
+    
+    s.add(o1);
+    System.out.println(s);
+    s.add(o2);
+    System.out.println(s);
+    s.add(o3);
+    System.out.println(s);
+    s.add(o4);
+    System.out.println(s);
+    s.add(o1);
+    System.out.println(s);
+
+    assertTrue(s.size() == 4);
+    assertTrue(s.contains(o1));
+    assertTrue(s.contains(o2));
+    assertTrue(s.contains(o3));
+    assertTrue(s.contains(o4));
+    
+    X o3a = new X("5a", 5);
+    s.add(o3a);
+    System.out.println(s);
+    assertTrue(s.size() == 5);
+    assertTrue(s.contains(o3a));
+    
+    s.remove(o3a);
+    System.out.println(s);
+    assertTrue(s.size() == 4);
+    assertFalse(s.contains(o3a));
+    assertTrue(s.contains(o3));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/SparseClusterArrayTest.java b/src/tests/gov/nasa/jpf/util/SparseClusterArrayTest.java
new file mode 100644 (file)
index 0000000..d88f5c2
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import static gov.nasa.jpf.util.SparseClusterArray.S1;
+import gov.nasa.jpf.util.SparseClusterArray.Entry;
+import gov.nasa.jpf.util.SparseClusterArray.Snapshot;
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.util.HashMap;
+import java.util.Random;
+
+import org.junit.Test;
+
+/**
+ * unit test for gov.nasa.jpf.util.SparseClusterArray
+ */
+public class SparseClusterArrayTest extends TestJPF {
+
+  public static void main (String[] args){
+
+    // our performance evals
+    if (args.length == 1){
+      String mthName = args[0];
+      if (mthName.equals("evalHashMap")){
+        evalHashMap();
+        return;
+      } else if (mthName.equals("evalSparseClusterArray")){
+        evalSparseClusterArray();
+        return;
+      }
+    }
+
+    // the regression tests
+    runTestsOfThisClass(args);
+  }
+
+
+  @Test
+  public void testBasic() {
+    SparseClusterArray<Object> arr = new SparseClusterArray<Object>();
+    Object v;
+    int ref;
+
+    ref = (1 << S1) | 42;
+    arr.set(ref, (v = new Integer(ref)));
+
+    Object o = arr.get(ref);
+    System.out.println(o);
+    assert o.equals(v);
+
+    ref = (2 << S1);
+    arr.set(ref, new Integer(ref));
+
+    System.out.println("cardinality = " + arr.cardinality());
+    assert arr.cardinality() == 2;
+
+    for (Object e : arr) {
+      System.out.println(e);
+    }
+  }
+
+  @Test
+  public void testNextNull () {
+    Object e = new Integer(42);
+    SparseClusterArray<Object> arr = new SparseClusterArray<Object>();
+    int k;
+    int limit = 10000000;
+
+    arr.set(0, e);
+    k = arr.firstNullIndex(0, limit);
+    System.out.println("k=" + k);  // 1
+    assert k == 1;
+
+    arr.set(0,null);
+    k = arr.firstNullIndex(0, limit);
+    System.out.println("k=" + k);  // 0
+    assert k == 0;
+
+    arr.set(511, 511);
+
+    int i=0;
+    for (;i<512; i++) {
+      arr.set(i, e);
+    }
+    System.out.println(arr.get(511));
+    System.out.println(arr.get(512));
+    k = arr.firstNullIndex(0, limit);
+    assert k == 512;
+    
+    long t1 = System.currentTimeMillis();
+    for (int j=0; j<100000; j++) {
+      k = arr.firstNullIndex(0, limit);
+    }
+    long t2 = System.currentTimeMillis();
+    System.out.println("k=" + k + ", 100000 lookups in: " + (t2 - t1)); // 512
+
+    for (;i<2048;i++) {
+      arr.set(i, e);
+    }
+    k = arr.firstNullIndex(0, limit);
+    System.out.println("k=" + k);  // 2048 (no chunk)
+    assert k == 2048;
+
+    k = arr.firstNullIndex(0, 2048);
+    System.out.println("k=" + k); // -1
+    assert k == -1;
+
+    arr.set(2048, e);
+    arr.set(2048,null);
+    k = arr.firstNullIndex(0, limit);
+    System.out.println("k=" + k);  // 2048 (new chunk)
+    assert k == 2048;
+
+    for (; i<2500; i++) {
+      arr.set(i, e);
+    }
+    k = arr.firstNullIndex(0, limit);
+    System.out.println("k=" + k);  // 2500
+    assert k == 2500;
+  }
+
+  @Test
+  public void testClusterNextNull () {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    arr.set(0, 0); // have a lower chunk
+
+    int tid = 2;
+    int base = (tid << SparseClusterArray.S1);
+    int r = arr.firstNullIndex(base, SparseClusterArray.MAX_CLUSTER_ENTRIES);
+    assert r == 0x02000000;
+    System.out.println(Integer.toHexString(r));
+    arr.set(r, 42);
+
+    r = arr.firstNullIndex(base, SparseClusterArray.MAX_CLUSTER_ENTRIES);
+    assert r == 0x02000001;
+    System.out.println(Integer.toHexString(r));
+
+    for (int i=r; i < 0x0200ffff; i++){
+      arr.set(i,42);
+    }
+
+    arr.set(0x200f0ff, null);
+    r = arr.firstNullIndex(base, SparseClusterArray.MAX_CLUSTER_ENTRIES);
+    assert r == 0x200f0ff;
+    System.out.println(Integer.toHexString(r));
+    arr.set(0x200f0ff, 42);
+
+    r = arr.firstNullIndex(base, SparseClusterArray.MAX_CLUSTER_ENTRIES);
+    assert r == 0x200ffff;
+    System.out.println(Integer.toHexString(r));
+
+  }
+
+  @Test
+  public void testClone() {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    arr.set(0, new Integer(0));
+    arr.set(42, new Integer(42));
+    arr.set(6762, new Integer(6762));
+    arr.set(6762, null);
+
+    Cloner<Integer> cloner = new Cloner<Integer>() {
+      @Override
+       public Integer clone (Integer other) {
+        return new Integer(other);
+      }
+    };
+    SparseClusterArray<Integer> newArr = arr.deepCopy(cloner);
+    for (Integer i : newArr) {
+      System.out.println(i);
+    }
+
+    assert newArr.cardinality() == 2;
+    assert newArr.get(0) == 0;
+    assert newArr.get(42) == 42;
+    assert newArr.get(6762) == null;
+  }
+
+  @Test
+  public void testSnapshot() {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    arr.set(0, new Integer(0));
+    arr.set(42, new Integer(42));
+    arr.set(4095, new Integer(4095));
+    arr.set(4096, new Integer(4096));
+    arr.set(7777, new Integer(7777));
+    arr.set(67620, new Integer(67620));
+    arr.set(67620, null);
+    arr.set(7162827, new Integer(7162827));
+
+    Transformer<Integer,String> i2s = new Transformer<Integer,String>() {
+      @Override
+       public String transform (Integer n) {
+        return n.toString();
+      }
+    };
+
+    Transformer<String,Integer> s2i = new Transformer<String,Integer>() {
+      @Override
+       public Integer transform (String s) {
+        return new Integer( Integer.parseInt(s));
+      }
+    };
+
+    Snapshot<Integer,String> snap = arr.getSnapshot(i2s);
+    // just for debugging purposes
+    int len = snap.size();
+    for (int i=0; i<len; i++){
+      System.out.println("a[" + snap.getIndex(i) + "] = " + snap.getValue(i));
+    }
+
+    arr.set(42,null);
+    arr.set(87, new Integer(87));
+    arr.set(7162827, new Integer(-1));
+
+    arr.restore(snap, s2i);
+    for (Integer i : arr) {
+      System.out.println(i);
+    }
+
+    assert arr.cardinality() == 6;
+    assert arr.get(0) == 0;
+    assert arr.get(42) == 42;
+    assert arr.get(4095) == 4095;
+    assert arr.get(4096) == 4096;
+    assert arr.get(7777) == 7777;
+    assert arr.get(7162827) == 7162827;
+  }
+
+  @Test
+  public void testChanges() {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    arr.set(42, new Integer(42));
+    arr.set(6276, new Integer(6276));
+
+    arr.trackChanges();
+
+    arr.set(0, new Integer(0));
+    arr.set(42, new Integer(-1));
+    arr.set(4095, new Integer(4095));
+    arr.set(4096, new Integer(4096));
+    arr.set(7777, new Integer(7777));
+    arr.set(7162827, new Integer(7162827));
+
+    Entry<Integer> changes = arr.getChanges();
+    arr.revertChanges(changes);
+
+    for (Integer i : arr) {
+      System.out.println(i);
+    }
+
+    assert arr.cardinality() == 2;
+    assert arr.get(42) == 42;
+    assert arr.get(6276) == 6276;
+  }
+
+  @Test
+  public void testIterator() {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    for (int i=0; i<300; i++){
+      arr.set(i,i);
+    }
+    for (int i=700; i < 1000; i++){
+      arr.set(i,i);
+    }
+    
+    // remove some while we iterate
+    boolean lastSeen = false;
+    int n = 0;
+    for (Integer i : arr){
+      if (i == 200){
+        for (int j = 150; j<750; j++){
+          arr.set(j, null);
+        }
+      } else if (i == 999){
+        lastSeen = true;
+      }
+      n++;
+    }
+    
+    assert n == 451 : "wrong number of visited elements: " + n; // [0-200] + [750-999]
+    assert lastSeen : "last element not seen";
+  }
+
+  @Test
+  public void testIndexIterator() {
+    SparseClusterArray<Integer> arr = new SparseClusterArray<Integer>();
+
+    for (int i=0; i<300; i++){
+      arr.set(i,i);
+    }
+    for (int i=700; i < 1000; i++){
+      arr.set(i,i);
+    }
+
+    // remove some while we iterate
+    boolean lastSeen = false;
+    int n = 0;
+    IndexIterator it = arr.getElementIndexIterator(100);
+    for (int i=it.next(); i>= 0; i = it.next()){
+System.out.println(i);
+      if (i == 200){
+        for (int j = 150; j<750; j++){
+          arr.set(j, null);
+        }
+      } else if (i == 999){
+        lastSeen = true;
+      }
+      n++;
+    }
+
+    assert n == 351 : "wrong number of visited elements: " + n; // [100-200] + [750-999]
+    assert lastSeen : "last element not seen";
+  }
+
+
+   //--- the performance sectopm
+
+  final static int MAX_ROUNDS = 1000;
+  final static int MAX_N = 10000;
+  final static int MAX_T = 8;
+
+
+  static void evalSparseClusterArray() {
+    Random random = new Random(0);
+    Object elem = new Object();
+    long t1, t2;
+    int n = 0;
+
+    t1 = System.currentTimeMillis();
+    SparseClusterArray<Object> arr = new SparseClusterArray<Object>();
+
+    for (int i=0; i<MAX_ROUNDS; i++) {
+      int seg = random.nextInt(MAX_T) << S1;
+      for (int j=0; j<MAX_N; j++) {
+        int ref = seg | random.nextInt(MAX_N);
+        //ref |= j;
+        arr.set(ref, elem);
+        if (arr.get(ref) == null) throw new RuntimeException("element not set: " + i);
+      }
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("SparseArray random write/read of " + arr.cardinality() + " elements: "+ (t2 - t1));
+
+    n=0;
+    t1 = System.currentTimeMillis();
+    for (Object e : arr) {
+      n++;
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("SparseArray iteration over " + n + " elements: " + (t2 - t1));
+  }
+
+  static void evalHashMap() {
+    Random random = new Random(0);
+    Object elem = new Object();
+    long t1, t2;
+
+    t1 = System.currentTimeMillis();
+    HashMap<Integer,Object> arr = new HashMap<Integer,Object>();
+
+    for (int i=0; i<MAX_ROUNDS; i++) {
+      int seg = random.nextInt(MAX_T) << S1;
+      for (int j=0; j<MAX_N; j++) {
+        int ref = seg | random.nextInt(MAX_N);
+        //ref |= j;
+        arr.put(ref, elem);
+        if (arr.get(ref) == null) throw new RuntimeException("element not set: " + i);
+      }
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("HashMap random write/read of " + arr.size() + " elements: " + (t2 - t1));
+
+    int n=0;
+    t1 = System.currentTimeMillis();
+    for (Object e : arr.values()) {
+      n++;
+    }
+    t2 = System.currentTimeMillis();
+    System.out.println("HashMap iteration over " + n + " elements: " + (t2 - t1));
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/SparseIntVectorTest.java b/src/tests/gov/nasa/jpf/util/SparseIntVectorTest.java
new file mode 100644 (file)
index 0000000..400b72a
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+public class SparseIntVectorTest extends TestJPF {
+  
+  void assertEquals( SparseIntVector v1, SparseIntVector v2) {
+    assertTrue( v1.size() == v2.size());
+
+    int n = v1.size();
+    for (int i=0; i<n; i++) {
+      int a = v1.get(i);
+      int b = v2.get(i);
+      assertTrue(a == b);
+    }
+  }
+  
+  @Test
+  public void testSetGet () {
+    SparseIntVector v = new SparseIntVector();
+
+    v.set(0, 10);
+    v.set(42, 11);
+    v.set(111111111, 12);
+    
+    assertTrue( v.get(0) == 10);
+    assertTrue( v.get(42) == 11);
+    assertTrue( v.get(111111111) == 12);
+    
+    assertTrue( v.get(10) == 0);
+    
+    v.clear(42);
+    assertTrue( v.get(42) == 0);
+  }
+  
+  @Test
+  public void testSnapshot () {
+    SparseIntVector v = new SparseIntVector();
+
+    // all empty snapshot
+    SparseIntVector.Snapshot snap = v.getSnapshot();
+    int val = 42;
+    v.set(0,  val);
+    assertTrue(v.size() == 1 && v.get(0) == val);
+    v.restore(snap);
+    assertTrue(v.size() == 0);
+
+    //--- all full snapshot
+    for (int i=0; i<100; i++) {
+      v.set(i, i);
+      assertTrue( "size out of sync: " + i, v.size() == (i+1));
+    }
+    SparseIntVector.Snapshot snap0 = v.getSnapshot();
+    v.clear();
+    v.restore(snap0);
+    for (int i=0; i<100; i++) {
+      assertTrue( i == v.get(i));
+    }
+    
+    //--- punch holes into it
+    v.setRange(11,  20, 0);
+    v.set( 25,0);
+    v.set( 26, 0);
+    v.set( 42, 0);
+    v.setRange(70, 85, 0);
+    SparseIntVector.Snapshot snap1 = v.getSnapshot();    
+    SparseIntVector v1 = v.clone();
+    v.clear();
+    v.restore(snap1);
+    //v.printOn( System.out);
+    assertEquals( v1, v);
+    
+    //--- chop off the ends
+    v.restore(snap0);
+    v.setRange(81, 99, 0);
+    v.setRange(0, 19, 0);
+    SparseIntVector.Snapshot snap2 = v.getSnapshot();    
+    SparseIntVector v2 = v.clone();
+    v.clear();
+    v.restore(snap2);
+    assertEquals( v2, v); 
+  }
+  
+  static final int MAX_SIZE = 10000;
+  static final int MAX_ROUNDS = 1000;
+  
+  public void benchmark() {
+    long t1, t2;
+
+    for (int rep = 0; rep < 2; rep++) {
+      Runtime.getRuntime().gc();
+      SparseIntVector siv = new SparseIntVector();
+      t1 = System.currentTimeMillis();
+      for (int i = 0; i < MAX_ROUNDS; i++) {
+        SparseIntVector.Snapshot snap = siv.getSnapshot();
+        for (int j = 0; j < MAX_SIZE; j++) {
+          siv.set(j, j);
+          assert siv.get(j) == j;
+          // assert siv.size() == (j+1) : "size differs: " + siv.size() + " / "
+          // + (j+1);
+        }
+        assert siv.size() == MAX_SIZE : "wrong size: " + siv.size();
+        siv.restore(snap);
+      }
+      t2 = System.currentTimeMillis();
+      System.out.printf("SparseIntVector size %d, rounds %d: %d\n", MAX_SIZE,
+          MAX_ROUNDS, (t2 - t1));
+
+      Runtime.getRuntime().gc();
+      IntTable<Integer> tbl = new IntTable<Integer>();
+      t1 = System.currentTimeMillis();
+      for (int i = 0; i < MAX_ROUNDS; i++) {
+        IntTable.Snapshot<Integer> snap = tbl.getSnapshot();
+        for (int j = 0; j < MAX_SIZE; j++) {
+          tbl.put(j, j);
+
+          IntTable.Entry<Integer> e = tbl.get(j);
+          assert e != null && e.val == j  : "wrong IntTable entry for index: " + j + " : " + e + " in round: " + i;
+        }
+        assert tbl.size() == MAX_SIZE : "wrong size: " + tbl.size();
+        tbl.restore(snap);
+      }
+      t2 = System.currentTimeMillis();
+      System.out.printf("IntTable size %d, rounds %d: %d\n", MAX_SIZE,
+          MAX_ROUNDS, (t2 - t1));
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/SplitInputStreamTest.java b/src/tests/gov/nasa/jpf/util/SplitInputStreamTest.java
new file mode 100644 (file)
index 0000000..15351c1
--- /dev/null
@@ -0,0 +1,660 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Verify;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.security.SecureRandom;
+import java.util.Random;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+
+public class SplitInputStreamTest extends TestJPF {
+
+  private static final Random s_random = Verify.isRunningInJPF() ? null : new SecureRandom();
+  private static final String s_jpfArgs[] = new String[]{
+    "+listener+=,gov.nasa.jpf.listener.PreciseRaceDetector",
+    "+classpath=build/main"
+  };
+  private SplitInputStream m_fixture;
+  private InputStream m_input;
+  private byte m_expect[];
+
+  @Before
+  public void before() throws IOException {
+    initialize(10, SplitInputStream.INITIAL_BUFFER_SIZE);
+  }
+
+  public void initialize(int length, int bufferSize) throws IOException {
+    ByteArrayInputStream source;
+    int i;
+
+    m_expect = new byte[length];
+
+    if (s_random != null) {
+      s_random.nextBytes(m_expect);
+    } else {
+      for (i = m_expect.length; --i >= 0;) {
+        m_expect[i] = (byte) i;
+      }
+    }
+
+    source = new ByteArrayInputStream(m_expect);
+    m_fixture = new SplitInputStream(source, 2, bufferSize);
+    m_input = m_fixture.getStream(0);
+
+    assertEquals(2, m_fixture.getStreamCount());
+  }
+
+  @After
+  public void after() throws IOException {
+    InputStream input;
+    int i, j;
+
+    for (i = m_fixture.getStreamCount(); --i > 0;) {
+      input = m_fixture.getStream(i);
+
+      for (j = m_expect.length - input.available(); j < m_expect.length; j++) {
+        assertEquals(m_expect[j] & 0x0FF, input.read());
+      }
+
+      assertEquals(-1, input.read());
+      assertEquals(-1, input.read(new byte[1]));
+    }
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void passNullPointerToConstructor() throws IOException {
+    InputStream source;
+
+    source = null;
+
+    new SplitInputStream(source, 1);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void passZeroStreamsToConstructor() throws IOException {
+    InputStream source;
+
+    source = new ByteArrayInputStream(m_expect);
+
+    new SplitInputStream(source, 0);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void passZeroBufferSizeToConstructor() throws IOException {
+    InputStream source;
+
+    source = new ByteArrayInputStream(m_expect);
+
+    new SplitInputStream(source, 2, 0);
+  }
+
+  @Test
+  public void readByte() throws IOException {
+    assertEquals(m_expect[0] & 0x0FF, m_input.read());
+  }
+
+  @Test
+  public void readEveryByteValue() throws IOException {
+    ByteArrayInputStream source;
+    int i;
+
+    m_expect = new byte[256];
+
+    for (i = 256; --i >= 0;) {
+      m_expect[i] = (byte) i;
+    }
+
+    source = new ByteArrayInputStream(m_expect);
+    m_fixture = new SplitInputStream(source, 2, 256);
+    m_input = m_fixture.getStream(0);
+
+    for (i = 0; i < 256; i++) {
+      assertEquals(i, m_input.read());
+    }
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void readNullBuffer() throws IOException {
+    m_input.read(null, 0, 1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readIndexNegOne() throws IOException {
+    m_input.read(new byte[0], -1, 0);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readLengthNegOne() throws IOException {
+    m_input.read(new byte[0], 0, -1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void readBeyondEnd() throws IOException {
+    m_input.read(new byte[16], 8, 9);
+  }
+
+  @Test
+  public void readLengthZero() throws IOException {
+    m_input.read(new byte[1], 0, 0);
+    assertEquals(m_expect[0] & 0x0FF, m_input.read());
+  }
+
+  @Test
+  public void readArray() throws IOException {
+    int offset, length, delta;
+    byte buffer[];
+
+    length = m_expect.length;
+    buffer = new byte[length];
+
+    for (offset = 0; offset < length; offset += delta) {
+      delta = m_input.read(buffer);
+
+      assertTrue(delta >= 0);
+    }
+
+    assertArrayEquals(m_expect, buffer);
+  }
+
+  @Test
+  public void readArrayEveryByteValue() throws IOException {
+    ByteArrayInputStream source;
+    int i, delta;
+    byte actual[];
+
+    m_expect = new byte[256];
+    actual = new byte[256];
+
+    for (i = 256; --i >= 0;) {
+      m_expect[i] = (byte) i;
+    }
+
+    source = new ByteArrayInputStream(m_expect);
+    m_fixture = new SplitInputStream(source, 2);
+    m_input = m_fixture.getStream(0);
+
+    for (i = 0; i < 256; i += delta) {
+      delta = m_input.read(actual, i, 256 - i);
+
+      assertTrue(delta >= 0);
+    }
+
+    assertArrayEquals(m_expect, actual);
+  }
+
+  @Test
+  public void skipZero() throws IOException {
+    assertEquals(0, m_input.skip(0));
+  }
+
+  @Test
+  public void skipNegOne() throws IOException {
+    assertEquals(0, m_input.skip(-1));
+  }
+
+  @Test
+  public void skip() throws IOException {
+    assertEquals(m_expect.length / 2, m_input.skip(m_expect.length / 2));
+  }
+
+  @Test
+  public void skipToEnd() throws IOException {
+    assertEquals(m_expect.length, m_input.skip(m_expect.length));
+  }
+
+  @Test
+  public void skipBeyondEnd() throws IOException {
+    assertEquals(m_expect.length, m_input.skip(m_expect.length + 1));
+    assertEquals(0, m_input.skip(1));
+  }
+
+  @Test
+  public void readByteAfterClose() throws IOException {
+    m_input.close();
+    assertEquals(-1, m_input.read());
+  }
+
+  @Test
+  public void readBufferAfterClose() throws IOException {
+    m_input.close();
+    assertEquals(-1, m_input.read(new byte[1], 0, 1));
+  }
+
+  @Test
+  public void skipAfterClose() throws IOException {
+    m_input.close();
+    assertEquals(0, m_input.skip(1));
+  }
+
+  @Test
+  public void availableAfterClose() throws IOException {
+    assertEquals(m_expect.length, m_input.available());
+    m_input.close();
+    assertEquals(0, m_input.available());
+  }
+
+  @Test
+  public void availableAtEnd() throws IOException {
+    int i;
+
+    for (i = 0; i < m_expect.length; i++) {
+      assertEquals(m_expect.length - i, m_input.available());
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+
+    assertEquals(0, m_input.available());
+  }
+
+  @Test
+  public void availableNeverReads() throws IOException {
+    SplitInputStream split;
+    InputStream source, stream;
+
+    source = new InputStream() {
+
+      @Override
+       public int read() {
+        fail();
+
+        return (0);
+      }
+    };
+
+    split = new SplitInputStream(source, 1);
+    stream = split.getStream(0);
+
+    assertEquals(0, stream.available());
+  }
+
+  @Test
+  public void closeSource() throws IOException {
+    SplitInputStream split;
+    CloseCountInputStream source;
+    InputStream input;
+    int i;
+
+    source = new CloseCountInputStream();
+    split = new SplitInputStream(source, 2);
+    input = split.getStream(0);
+
+    for (i = split.getStreamCount() + 5; --i >= 0;) {
+      input.close();
+    }
+
+    assertEquals(0, source.getCloseCount());
+
+    input = split.getStream(1);
+
+    input.close();
+    assertEquals(1, source.getCloseCount());
+
+    for (i = split.getStreamCount() + 5; --i >= 0;) {
+      input.close();
+    }
+
+    assertEquals(1, source.getCloseCount());
+  }
+
+  @Test
+  public void overflowAvailable() throws IOException {
+    SplitInputStream split;
+    MaxAvailableInputStream source;
+    InputStream input;
+
+    source = new MaxAvailableInputStream();
+    split = new SplitInputStream(source, 1);
+    input = split.getStream(0);
+
+    assertEquals(0, input.read());
+    assertEquals(Integer.MAX_VALUE, input.available());
+  }
+
+  @Test
+  public void expand() throws IOException {
+    int i, length;
+
+    length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
+
+    initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
+
+    for (i = 0; i < length; i++) {
+      assertEquals(length - i, m_input.available());
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+  }
+
+  @Test
+  public void wrap() throws IOException {
+    int i, length;
+
+    length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
+
+    initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
+
+    for (i = 0; i < SplitInputStream.INITIAL_BUFFER_SIZE - 5; i++) {
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+
+    m_input = m_fixture.getStream(1);
+
+    for (i = 0; i < SplitInputStream.INITIAL_BUFFER_SIZE; i++) {
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+
+    assertEquals(SplitInputStream.INITIAL_BUFFER_SIZE, m_input.available());
+  }
+
+  @Test
+  public void ignoreClosedStream() throws IOException, NoSuchFieldException, IllegalAccessException {
+    Field bufferField;
+    int i, length;
+    byte expect[], actual[];
+
+    length = 2 * SplitInputStream.INITIAL_BUFFER_SIZE;
+
+    initialize(length, SplitInputStream.INITIAL_BUFFER_SIZE);
+    m_input.close();
+
+    bufferField = SplitInputStream.class.getDeclaredField("m_buffer");
+
+    bufferField.setAccessible(true);
+
+    expect = (byte[]) bufferField.get(m_fixture);
+    m_input = m_fixture.getStream(1);
+
+    for (i = 0; i < length; i++) {
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+
+    actual = (byte[]) bufferField.get(m_fixture);
+
+    assertSame(expect, actual);
+  }
+
+  @Test
+  public void bufferSize1() throws IOException {
+    int i;
+
+    initialize(10, 1);
+
+    for (i = 0; i < m_expect.length; i++) {
+      assertEquals(m_expect[i] & 0x0FF, m_input.read());
+    }
+  }
+
+  @Ignore("This test takes too long for everyone to run all the time.  There are 101,174 states which take about 4 minutes to run.")
+  @Test
+  public void concurrentRead() throws IOException, InterruptedException {
+    InputStream source;
+    Thread thread1, thread2;
+    Runnable task;
+
+    if (verifyNoPropertyViolation(s_jpfArgs)) {
+      source = new InputStream() {
+
+        private Thread m_reader;
+
+        @Override
+               public int read() {
+          if (m_reader == null) {
+            m_reader = Thread.currentThread();    // JPF will catch the race condition if 2 threads call read concurrently.
+          } else {
+            assertSame(m_reader, Thread.currentThread());
+          }
+
+          return (0);
+        }
+      };
+
+      m_fixture = new SplitInputStream(source, 2);
+
+      task = new Runnable() {
+
+        @Override
+               public void run() {
+          try {
+            m_fixture.getStream(0).read();
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+
+      thread1 = new Thread(task);
+
+      task = new Runnable() {
+
+        @Override
+               public void run() {
+          try {
+            m_fixture.getStream(1).read();
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+
+      thread2 = new Thread(task);
+
+      thread1.start();
+      thread2.start();
+
+      thread1.join();
+      thread2.join();
+    }
+  }
+
+  @Test
+  public void concurrentAvailable() throws InterruptedException {
+    InputStream source;
+    Thread thread1, thread2;
+    Runnable task;
+
+    if (verifyNoPropertyViolation(s_jpfArgs)) {
+      source = new InputStream() {
+
+        private Thread m_access;
+
+        @Override
+               public int read() {
+          fail();
+
+          return (0);
+        }
+
+        @Override
+               public int available() {
+          assertNull(m_access);
+
+          m_access = Thread.currentThread();    // JPF will catch the race condition if 2 threads call concurrently.
+          m_access = null;
+
+          return (0);
+        }
+      };
+
+      m_fixture = new SplitInputStream(source, 2);
+
+      task = new Runnable() {
+
+        @Override
+               public void run() {
+          try {
+            m_fixture.getStream(0).available();
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+
+      thread1 = new Thread(task);
+
+      task = new Runnable() {
+
+        @Override
+               public void run() {
+          try {
+            m_fixture.getStream(1).available();
+          } catch (IOException e) {
+            throw new RuntimeException(e);
+          }
+        }
+      };
+
+      thread2 = new Thread(task);
+
+      thread1.start();
+      thread2.start();
+
+      thread1.join();
+      thread2.join();
+    }
+  }
+
+  @Ignore("This test takes too long for everyone to run all the time.  There are 230,360 states which take about 5 minutes to run.")
+  @Test
+  public void thoroughJPFTest() throws InterruptedException, IOException {
+    Thread thread1, thread2;
+
+    if (verifyNoPropertyViolation(s_jpfArgs)) {
+      initialize(4, 2);
+
+      thread1 = new Thread(new JPFTask(0));
+      thread2 = new Thread(new JPFTask(1));
+
+      thread1.start();
+      thread2.start();
+
+      thread1.join();
+      thread2.join();
+    }
+  }
+
+  private static class CloseCountInputStream extends InputStream {
+
+    private int m_closeCount;
+
+    @Override
+       public int read() {
+      return (0);
+    }
+
+    @Override
+       public int available() {
+      return (m_closeCount == 0 ? 1 : 0);
+    }
+
+    @Override
+       public void close() {
+      m_closeCount++;
+    }
+
+    public int getCloseCount() {
+      return (m_closeCount);
+    }
+  }
+
+  private static class MaxAvailableInputStream extends InputStream {
+
+    private int m_data;
+
+    @Override
+       public int read() {
+      return (m_data++);
+    }
+
+    @Override
+       public int available() {
+      return (Integer.MAX_VALUE);
+    }
+  }
+
+  private class JPFTask implements Runnable {
+
+    private final InputStream m_input;
+
+    public JPFTask(int index) {
+      m_input = m_fixture.getStream(index);
+    }
+
+    @Override
+       public void run() {
+      try {
+        unsafe();
+      } catch (Exception e) {
+        throw new RuntimeException(e);
+      }
+    }
+
+    private void unsafe() throws IOException {
+      int i, expect, actual, test;
+      byte buffer[];
+
+      //System.out.print("#" + Thread.currentThread().getId() + " | Test ");
+      test = Verify.getInt(0, 4);
+
+      //System.out.println(test);
+
+      switch (test) {
+        case 0:
+          m_input.close();
+          break;
+
+        case 1:
+          assertEquals(4, m_input.available());
+          break;
+
+        case 2:
+          expect = Verify.getInt(-1, 5);
+          actual = (int) m_input.skip(expect);
+          expect = Math.max(expect, 0);
+          expect = Math.min(expect, 4);
+
+          assertTrue(actual <= expect);
+          break;
+
+        case 3:
+          for (i = 0; i < 4; i++) {
+            assertEquals(m_expect[i], m_input.read());
+          }
+
+          break;
+
+        case 4:
+          buffer = new byte[1];
+
+          for (i = 0; i < 4; i++) {
+            assertEquals(1, m_input.read(buffer));
+            assertEquals(m_expect[i], buffer[0]);
+          }
+          break;
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/SplitOutputStreamTest.java b/src/tests/gov/nasa/jpf/util/SplitOutputStreamTest.java
new file mode 100644 (file)
index 0000000..a72748e
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.PipedInputStream;
+import java.io.PipedOutputStream;
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class SplitOutputStreamTest extends TestJPF {
+
+  private PipedInputStream m_sinks[];
+  private SplitOutputStream m_fixture;
+
+  @Before
+  public void before() throws IOException {
+    PipedOutputStream output[];
+    int i;
+
+    m_sinks = new PipedInputStream[2];
+    output = new PipedOutputStream[2];
+
+    for (i = m_sinks.length; --i >= 0;) {
+      m_sinks[i] = new PipedInputStream();
+      output[i] = new PipedOutputStream(m_sinks[i]);
+    }
+
+    m_fixture = new SplitOutputStream(output);
+
+    Arrays.fill(output, null);   // Force SplitOutputStream to make a copy of output
+  }
+
+  @After
+  public void after() throws IOException {
+    int i, length, offset, delta;
+    byte expect[], actual[];
+
+    m_fixture.flush();
+    m_fixture.close();
+
+    expect = new byte[128];
+    actual = new byte[128];
+
+    while (true) {
+      length = m_sinks[0].read(expect);
+
+      if (length < 0) {
+        break;
+      }
+
+      for (i = m_sinks.length; --i > 0;) {
+        assertTrue(m_sinks[i].available() >= length);
+
+        for (offset = 0; offset < length; offset += delta) {
+          delta = m_sinks[i].read(actual, offset, length - offset);
+
+          assertTrue(delta >= 0);
+        }
+
+        assertArrayEquals(actual, expect);
+      }
+    }
+
+    for (i = m_sinks.length; --i > 0;) {
+      assertEquals(-1, m_sinks[i].read());
+    }
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void passNullPointerToConstructor() throws IOException {
+    OutputStream sinks[];
+
+    sinks = null;
+
+    new SplitOutputStream(sinks);
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void passNoArgsToConstructor() throws IOException {
+    new SplitOutputStream();
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void passNullArgsToConstructor() throws IOException {
+    OutputStream sink;
+
+    sink = null;
+
+    new SplitOutputStream(sink);
+  }
+
+  @Test
+  public void writeByte() throws IOException {
+    m_fixture.write(123);
+  }
+
+  @Test
+  public void modifyAfterConstructor() {
+
+  }
+
+  @Test(expected = NullPointerException.class)
+  public void writeNullBuffer() throws IOException {
+    m_fixture.write(null, 0, 1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeIndexNegOne() throws IOException {
+    m_fixture.write(new byte[0], -1, 0);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeLengthNegOne() throws IOException {
+    m_fixture.write(new byte[0], 0, -1);
+  }
+
+  @Test(expected = IndexOutOfBoundsException.class)
+  public void writeBeyondEnd() throws IOException {
+    m_fixture.write(new byte[16], 8, 9);
+  }
+
+  @Test
+  public void writeLengthZero() throws IOException {
+    m_fixture.write(new byte[1], 0, 0);
+  }
+
+  @Test
+  public void writeArray() throws IOException {
+    byte buffer[];
+
+    buffer = new byte[]{2, 3, 5, 7, 11, 13};
+
+    m_fixture.write(buffer);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/StringSetMatcherTest.java b/src/tests/gov/nasa/jpf/util/StringSetMatcherTest.java
new file mode 100644 (file)
index 0000000..5710524
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2015, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * regression test for .util.StringSetMatcher
+ */
+public class StringSetMatcherTest extends TestJPF {
+  @Test
+  public void testInversion (){
+    StringSetMatcher ssm = new StringSetMatcher("!failure-*", "failure-10");
+    
+    assertTrue( ssm.matchesAny("blah"));
+    assertFalse( ssm.matchesAny("failure-0"));
+    
+    assertTrue( ssm.matchesAny("failure-10"));
+    assertFalse( ssm.matchesAll("failure-10"));
+  }
+  
+  @Test
+  public void testMatchesAll (){
+    StringSetMatcher ssm = new StringSetMatcher("a*", "*blah");
+    
+    assertTrue( ssm.matchesAll("aXXblah"));
+    assertFalse( ssm.matchesAll("xblah"));
+  }
+  
+  @Test
+  public void testMatchesAny (){
+    StringSetMatcher ssm = new StringSetMatcher("blah", "gna");
+    
+    assertTrue( ssm.matchesAny("blah"));
+    assertFalse( ssm.matchesAny("xblah"));
+  }
+
+  @Test
+  public void testHasAnyPattern(){
+    StringSetMatcher ssm = new StringSetMatcher("*", "gna");
+    assertTrue( ssm.matchesAny("blubb"));
+    assertTrue( ssm.matchesAll("gna"));
+    
+    ssm = new StringSetMatcher("*");  // single pattern optimization
+    assertTrue(ssm.matchesAll("gna"));
+    assertTrue(ssm.matchesAny("gulp"));
+  }
+  
+}
diff --git a/src/tests/gov/nasa/jpf/util/UnsortedArrayIntSetTest.java b/src/tests/gov/nasa/jpf/util/UnsortedArrayIntSetTest.java
new file mode 100644 (file)
index 0000000..161a4ab
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util;
+
+public class UnsortedArrayIntSetTest extends ArrayIntSetTestBase {
+
+  @Override
+  protected ArrayIntSet createArrayIntSet() {
+    return new UnsortedArrayIntSet();
+  }
+
+  @Override
+  protected ArrayIntSet createArrayIntSet(int n) {
+    return new UnsortedArrayIntSet(n);
+  }
+
+}
\ No newline at end of file
diff --git a/src/tests/gov/nasa/jpf/util/event/EventTreeTest.java b/src/tests/gov/nasa/jpf/util/event/EventTreeTest.java
new file mode 100644 (file)
index 0000000..d48103d
--- /dev/null
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.event;
+
+import gov.nasa.jpf.util.event.EventTree;
+import gov.nasa.jpf.util.event.Event;
+import gov.nasa.jpf.util.test.TestJPF;
+import org.junit.Test;
+
+/**
+ * regression test for EventTree
+ */
+public class EventTreeTest extends TestJPF {
+  
+  protected boolean checkGeneratedPaths (EventTree m, String[] expected){
+    System.out.println("event tree: ");
+    m.printTree();
+    
+    int nMatches = 0;
+    for (Event ee : m.visibleEndEvents()){
+      String trace = ee.getPathString(null);
+      System.out.print("checking path: \"" + trace + '"');
+      
+      if (!m.checkPath(ee, expected)){
+        System.out.println("   UNEXPECTED");
+        return false;
+      } else {
+        System.out.println("   OK");
+      }
+      
+      nMatches++;
+    }
+    
+    if (nMatches != expected.length){
+      System.out.println("UNCOVERED PATH: ");
+      for (int i=0; i<expected.length; i++){
+        if (expected[i] != null){
+          System.err.println(expected[i]);
+        }
+      }
+      return false;
+    }
+    
+    return true;
+  }
+  
+  
+  //--------------------------------------------------------------------
+    
+  static class SimpleTree extends EventTree {    
+    @Override
+    public Event createRoot() {
+      return 
+        sequence(
+          event("a"),
+          alternatives(
+            event("1"),
+            iteration(2,
+              event("x")
+            )
+          ),
+          event("b")
+        );
+    }
+  }
+
+  @Test
+  public void testSimpleTree(){
+    SimpleTree m = new SimpleTree();
+    
+    String[] expected = {
+        "a1b",
+        "axxb"     
+    };
+    
+    if (!checkGeneratedPaths(m, expected)){
+      fail("failed to match traces");
+    }
+  }
+  
+  //--------------------------------------------------------------------
+  public static class CombinationTree extends EventTree {    
+    @Override
+    public Event createRoot() {
+      return anyCombination(
+               event("a"),
+               event("b"),
+               event("c"),
+               event("d")
+             );
+    }    
+  }
+  
+  @Test
+  public void testCombinationTree(){
+    CombinationTree t = new CombinationTree();
+    //t.printPaths();
+
+    String[] expected = {
+        "",
+        "a",
+        "b",
+        "ab",
+        "c",
+        "ac",
+        "bc",
+        "abc",
+        "d",
+        "ad",
+        "bd",
+        "abd",
+        "cd",
+        "acd",
+        "bcd",
+        "abcd"
+    };
+    
+    if (!checkGeneratedPaths(t, expected)){
+      fail("failed to match traces");
+    }
+  }  
+
+  static class SimpleCombinationTree extends EventTree {
+    @Override
+    public Event createRoot() {
+      return anyCombination(
+               event("a"),
+               event("b")
+             );
+    }
+  }
+
+  //@Test
+  public void testSimpleCombinationTree(){
+    SimpleCombinationTree t = new SimpleCombinationTree();
+    System.out.println("--- tree:");
+    t.printTree();
+    System.out.println("--- paths:");
+    t.printPaths();
+  }
+
+  //--------------------------------------------------------------------
+  static class EmbeddedCombinationTree extends EventTree {
+    @Override
+    public Event createRoot() {
+      return sequence(
+                event("1"),
+                anyCombination(
+                   event("a"),
+                   event("b")),
+                event("2"));
+    }
+  }
+
+  //@Test
+  public void testEmbeddedCombinationTree(){
+    EventTree t = new EmbeddedCombinationTree();
+    System.out.println("--- tree:");
+    t.printTree();
+    System.out.println("--- paths:");
+    t.printPaths();
+  }
+    
+  
+  //--------------------------------------------------------------------
+  static class DT extends EventTree {    
+    @Override
+    public Event createRoot() {
+      return sequence(
+              event("a"),
+              alternatives(
+                  event("1"),
+                  sequence(
+                      event("X"),
+                      event("Y")
+                  ),
+                  iteration(3,
+                      event("r")
+                  )
+              ),
+              event("b"));
+    }    
+  }
+
+  
+  @Test
+  public void testMaxDepth(){
+    DT t = new DT();
+    t.printTree();
+    t.printPaths();
+    
+    int maxDepth = t.getMaxDepth();
+    System.out.println("max depth: " + maxDepth);
+    
+    assertTrue( maxDepth == 5);
+  }
+
+  //--------------------------------------------------------------------
+  static class PermutationTree extends EventTree {
+    @Override
+    public Event createRoot(){
+      return anyPermutation(
+               event("a"),
+               event("b"),
+               event("c")
+              );
+    }
+  }
+
+  @Test
+  public void testPermutationTree(){
+    PermutationTree t = new PermutationTree();
+    //t.printPaths();
+    
+    String[] expected = {
+        "abc",
+        "acb",
+        "bac",
+        "bca",
+        "cab",
+        "cba"
+      };
+    
+    if (!checkGeneratedPaths(t, expected)){
+      fail("failed to match traces");
+    }
+  }
+  
+  //--------------------------------------------------------------------
+  static class AddPathTree extends EventTree {        
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("a"),
+               event("b"),
+               event("c")
+              );
+    } 
+  }
+  
+  @Test
+  public void testAddPath () {
+    AddPathTree t = new AddPathTree();
+    t.addPath(
+            new Event("a"), 
+            new Event("b"), 
+            new Event("3"));
+
+    String[] expected = { "abc", "ab3" };
+    
+    if (!checkGeneratedPaths(t, expected)){
+      fail("failed to match traces");
+    }
+  }
+    
+  //-------------------------------------------------------------------
+  static class MT1 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("a"),
+               event("b"),
+               event("c")
+              );
+    }
+  }
+  
+  static class MT2 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("1"),
+               event("2"),
+               event("3")
+              );
+    }
+  }
+
+  static class MT3 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("X"),
+               event("Y")
+              );
+    }
+  }
+
+  
+  @Test
+  public void testMerge (){
+    MT1 t1 = new MT1();
+    MT2 t2 = new MT2();
+    //MT3 t3 = new MT3();
+    
+    EventTree t = t1.interleave( t2);
+    // t.printPaths();
+    
+    String[] expected = {
+      "a123bc",
+      "a12b3c",
+      "a12bc3",
+      "a1b23c",
+      "a1b2c3",
+      "a1bc23",
+      "ab123c",
+      "ab12c3",
+      "ab1c23",
+      "abc123",
+      "123abc",
+      "12a3bc",
+      "12ab3c",
+      "12abc3",
+      "1a23bc",
+      "1a2b3c",
+      "1a2bc3",
+      "1ab23c",
+      "1ab2c3",
+      "1abc23"
+    };
+    
+    if (!checkGeneratedPaths(t, expected)){
+      fail("failed to match traces");
+    }
+  }
+  
+  //-------------------------------------------------------------------
+  static class SMT1 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("a"),
+               event("b")
+              );
+    }
+  }
+  
+  static class SMT2 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("1"),
+               event("2")
+              );
+    }
+  }
+
+  //@Test
+  public void testSimpleMerge (){
+    SMT1 t1 = new SMT1();
+    SMT2 t2 = new SMT2();
+    //MT3 t3 = new MT3();
+    
+    EventTree t = t1.interleave( t2);
+    System.out.println("--- merged tree:");
+    t.printTree();
+    System.out.println("--- merged paths:");
+    t.printPaths();
+  }
+    
+
+  //-------------------------------------------------------------------
+  static class RT1 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("a"),
+               event("b")
+              );
+    }
+  }
+  
+  static class RT2 extends EventTree {
+    @Override
+    public Event createRoot(){
+      return sequence(
+               event("1"),
+               event("2")
+              );
+    }
+  }
+
+  
+  @Test
+  public void testRemove (){
+    RT1 t1 = new RT1();
+    RT2 t2 = new RT2();
+    
+    EventTree t = t1.interleave( t2);
+    System.out.println("merged tree: ");
+    //t.printTree();
+    t.printPaths();
+    
+    t = new EventTree( t.removeSource(t2));
+    System.out.println("reduced tree: ");
+    //t.printTree();
+    //t.printPaths();
+    
+    String[] expected = { "ab" };
+    if (!checkGeneratedPaths(t, expected)){
+      fail("failed to match traces");
+    }    
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/json/JSONLexerTest.java b/src/tests/gov/nasa/jpf/util/json/JSONLexerTest.java
new file mode 100644 (file)
index 0000000..e3c4830
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import java.io.StringReader;
+
+import org.junit.Test;
+
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+public class JSONLexerTest extends TestJPF {
+  @Test
+  public void testEmptyObject() {
+    String json = "{}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)};
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+
+  private void assertTokenSequence(Token[] expectedTokens, JSONLexer lexer) {
+    int i = 0;
+    for (; i < expectedTokens.length; i++) {
+      Token t = lexer.getNextToken();
+      assert t.equals(expectedTokens[i]);
+    }
+
+    assert i == expectedTokens.length;
+    assert lexer.hasMore() == false;
+  }
+
+  @Test
+  public void testKeyIdObject() {
+    String json = "{ "
+            + "\"key1\" : true"
+            + "\"key2\" : false"
+            + "}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.String, "key1"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Identificator, "true"),
+      new Token(Token.Type.String, "key2"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Identificator, "false"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)};
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+
+  @Test
+  public void testKeyIdObjectWithEscapes() {
+    String json = "{ "
+            + "\"key\\t\\n\\\\ \" : true"
+            + "\"key\\u1234\" : false"
+            + "}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.String, "key\t\n\\ "),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Identificator, "true"),
+      new Token(Token.Type.String, "key\u1234"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Identificator, "false"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)};
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+
+  @Test
+  public void testKeyIdObjectWithNumbers() {
+    String json = "{ "
+            + "\"key1\" : -123.234e3"
+            + "\"key2\" : 1.4"
+            + "\"key3\" : 132"
+            + "}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.String, "key1"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Number, "-123.234e3"),
+      new Token(Token.Type.String, "key2"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Number, "1.4"),
+      new Token(Token.Type.String, "key3"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Number, "132"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)};
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+
+  @Test
+  public void testArrayTokens() {
+    String json = "{ "
+            + "[ ]"
+            + "}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.ArrayStart, "["),
+      new Token(Token.Type.ArrayEnd, "]"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)
+    };
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+
+  @Test
+  public void testCGCallTokens() {
+    String json = "{ "
+            + "\"i\" : MyCG(1, \"str\")"
+            + "}";
+    JSONLexer lexer = new JSONLexer(new StringReader(json));
+
+    Token expectedTokens[] = {
+      new Token(Token.Type.ObjectStart, "{"),
+      new Token(Token.Type.String, "i"),
+      new Token(Token.Type.KeyValueSeparator, ":"),
+      new Token(Token.Type.Identificator, "MyCG"),
+      new Token(Token.Type.CGCallParamsStart, "("),
+      new Token(Token.Type.Number, "1"),
+      new Token(Token.Type.Comma, ","),
+      new Token(Token.Type.String, "str"),
+      new Token(Token.Type.CGCallParamsEnd, ")"),
+      new Token(Token.Type.ObjectEnd, "}"),
+      new Token(Token.Type.DocumentEnd, null)
+    };
+
+    assertTokenSequence(expectedTokens, lexer);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/util/json/JSONParserTest.java b/src/tests/gov/nasa/jpf/util/json/JSONParserTest.java
new file mode 100644 (file)
index 0000000..5b59987
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.util.json;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ *
+ * @author Ivan Mushketik
+ */
+public class JSONParserTest extends TestJPF {
+
+  @Test
+  public void testOneLevelJSON() {
+    String json = "{"
+            + "\"key1\" : \"str\","
+            + "\"key2\" : 123"
+            + "}";
+    JSONObject o = parseJSON(json);
+
+    Value key1 = o.getValue("key1");
+    assert key1.getString().equals("str");
+    Value key2 = o.getValue("key2");
+    assert key2.getDouble() == 123;
+  }
+
+  @Test
+  public void testEmptyObject() {
+    String json = "{}";
+    JSONObject o = parseJSON(json);
+
+    assert o.getValue("noValue") == null;
+  }
+
+  @Test
+  public void testArrayParse() {
+    String json = "{"
+            + "\"key\" : ["
+            + "{ \"key1\" : 123 },"
+            + "{ \"key2\" : \"str\" } ]"
+            + "}";
+    JSONObject o = parseJSON(json);
+
+    Value objects[] = o.getValue("key").getArray();
+    assert objects[0].getObject().getValue("key1").getDouble() == 123;
+    assert objects[1].getObject().getValue("key2").getString().equals("str") == true;
+  }
+
+  @Test
+  public void testEmptyArray() {
+    String json = "{ \"emptyArr\" : [] }";
+    JSONObject o = parseJSON(json);
+
+    assert o.getValue("noArray") == null;
+  }
+
+  @Test
+  public void testIdentifacatorsParsing() {
+    String json = "{ "
+            + "\"Null\" : null,"
+            + "\"True\" : true,"
+            + "\"False\": false"
+            + "}";
+    JSONObject o = parseJSON(json);
+
+    assert o.getValue("Null").getString() == null;
+    assert o.getValue("True").getBoolean() == true;
+    assert o.getValue("False").getBoolean() == false;
+  }
+
+  @Test
+  public void testCGCallParsing() {
+    String json = "{"
+            + "\"id\" : CGName(1, \"str\", true, false, null)"
+            + "}";
+
+    JSONObject o = parseJSON(json);
+    CGCall cgCall = o.getCGCall("id");
+
+    assert cgCall.getName().equals("CGName") == true;
+    Value params[] = cgCall.getValues();
+
+    assertEquals(1d, params[0].getDouble(), 0.0001);
+    assert params[1].getString().equals("str") == true;
+    assert params[2].getBoolean() == true;
+    assert params[3].getBoolean() == false;
+    assert params[4].getDouble() == null;
+  }
+
+  private JSONObject parseJSON(String json) {
+    JSONLexer lexer = new JSONLexer(json);
+    JSONParser parser = new JSONParser(lexer);
+    JSONObject o = parser.parse();
+    return o;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/AnnotationInfoTest.java b/src/tests/gov/nasa/jpf/vm/AnnotationInfoTest.java
new file mode 100644 (file)
index 0000000..7c06833
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.jvm.ClassFile;
+import gov.nasa.jpf.jvm.DirClassFileContainer;
+import gov.nasa.jpf.jvm.JVMAnnotationParser;
+import gov.nasa.jpf.jvm.JVMClassFileContainer;
+import gov.nasa.jpf.util.test.TestJPF;
+import java.io.File;
+
+import org.junit.Test;
+
+/**
+ * unit test for AnnotationInfo creation
+ */
+public class AnnotationInfoTest extends TestJPF {
+
+  @interface X {
+    String value() default "nothing";
+  }
+
+  @interface Y {
+    String name();
+    int[] someArray() default { 1, 2, 3 };
+  }
+
+  protected AnnotationInfo createAnnotationInfo (String annotationName) throws ClassParseException {
+    DirClassFileContainer dfc = new DirClassFileContainer( new File("build/tests"));
+    ClassPath cp = new ClassPath();
+    cp.addClassFileContainer(dfc);
+    
+    JVMClassFileContainer.JVMClassFileMatch match = (JVMClassFileContainer.JVMClassFileMatch)cp.findMatch( annotationName);
+    byte[] data = match.getData();
+    
+    ClassFile cf = new ClassFile( data);
+    JVMAnnotationParser parser = new JVMAnnotationParser(cf);
+    
+    return new AnnotationInfo( annotationName, null, parser);
+  }
+  
+  @Test
+  public void testStringDefaultValue() {
+    try {
+      String annotationName = "gov.nasa.jpf.vm.AnnotationInfoTest$X";
+      
+      AnnotationInfo ai = createAnnotationInfo(annotationName);
+      AnnotationInfo.Entry[] entries = ai.getEntries();
+      
+      assertTrue(entries.length == 1);
+      assertTrue(entries[0].getKey().equals("value"));
+      assertTrue(entries[0].getValue().equals("nothing"));
+    
+    } catch (Throwable t){
+      t.printStackTrace();
+      fail("unexpected exception: " + t);
+    }
+  }
+
+  @Test
+  public void testIntArrayDefaultValue() {
+    try {
+      String annotationName = "gov.nasa.jpf.vm.AnnotationInfoTest$Y";
+      
+      AnnotationInfo ai = createAnnotationInfo(annotationName);
+      AnnotationInfo.Entry[] entries = ai.getEntries();
+
+      assertTrue(entries.length == 2);
+      assertTrue(entries[1].getKey().equals("someArray"));
+
+      Object[] a = (Object[]) entries[1].getValue();
+      assertTrue(a.length == 3);
+      assertTrue((Integer)a[0] == 1 && (Integer)a[1] == 2 && (Integer)a[2] == 3);
+      
+    } catch (Throwable t){
+      t.printStackTrace();
+      fail("unexpected exception: " + t);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/ClassLoaderInfoTest.java b/src/tests/gov/nasa/jpf/vm/ClassLoaderInfoTest.java
new file mode 100644 (file)
index 0000000..b72d5ef
--- /dev/null
@@ -0,0 +1,92 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.Config;
+import gov.nasa.jpf.JPF;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+import gov.nasa.jpf.vm.Heap;
+import gov.nasa.jpf.vm.VM;
+import gov.nasa.jpf.vm.SystemClassLoaderInfo;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * unit test for ClassLoaderInfo
+ */
+public class ClassLoaderInfoTest extends TestJPF {
+
+  @Test
+  public void testSystemClassLoader() {
+    //--- Sets up the JPF environment
+    String[] args = { "+vm.class=.vm.MultiProcessVM", "+target.1=HelloWorld", "+target.2=HelloWorld" };
+    Config config = new Config(args);
+    JPF jpf = new JPF(config);
+    VM vm = jpf.getVM();
+    Heap heap = vm.getHeap();
+
+    vm.initialize(); // this should instantiate two SystemClassLoaders
+
+    ThreadInfo[] threads = vm.getLiveThreads();
+    assertTrue( threads.length == 2);
+
+    //--- app 0
+    SystemClassLoaderInfo cl0 = threads[0].getSystemClassLoaderInfo();
+    assertTrue( cl0 != null);
+    assertTrue( cl0.parent == null);
+
+    int cl0ObjRef = cl0.objRef;
+    assertTrue( cl0ObjRef != MJIEnv.NULL);
+    ElementInfo ei0 = heap.get(cl0ObjRef);
+    assertTrue( ei0.getIntField( ClassLoaderInfo.ID_FIELD) == cl0.getId());
+    
+    //--- app 1
+    SystemClassLoaderInfo cl1 = threads[1].getSystemClassLoaderInfo();
+    assertTrue( cl1 != null);
+    assertTrue( cl0.parent == null);
+    
+    int cl1ObjRef = cl1.objRef;
+    assertTrue( cl1ObjRef != MJIEnv.NULL);
+    ElementInfo ei1 = heap.get(cl1ObjRef);
+    assertTrue( ei1.getIntField( ClassLoaderInfo.ID_FIELD) == cl1.getId());
+    
+    //--- compare them
+    assertTrue( cl0 != cl1);
+    assertTrue( cl0.getId() != cl1.getId());
+    assertTrue( cl0.statics != cl1.statics);
+    assertTrue( cl0ObjRef != cl1ObjRef);
+
+    //--- compare the loaded classes
+    ClassInfo ci0 = cl0.getResolvedClassInfo("java.lang.Class");
+    ClassInfo ci1 = cl1.getResolvedClassInfo("java.lang.Class");
+
+    assertTrue( ci0 != ci1);
+    assertTrue( ci0.getUniqueId() != ci1.getUniqueId());
+
+    assertTrue( ci0.getName().equals(ci1.getName()));
+    assertTrue( ci0.getClassFileUrl().equals(ci1.getClassFileUrl()));
+    
+    //--- should compare on-demand loaded classes here..
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/ElementInfoTest.java b/src/tests/gov/nasa/jpf/vm/ElementInfoTest.java
new file mode 100644 (file)
index 0000000..594b343
--- /dev/null
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.JPFException;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.DynamicElementInfo;
+import gov.nasa.jpf.vm.ElementInfo;
+
+import org.junit.Test;
+
+/**
+ * unit test for ElementInfos
+ */
+public class ElementInfoTest extends TestJPF {
+
+  @Test
+  public void testPinDownCounter() {
+    DynamicElementInfo ei = new DynamicElementInfo();
+
+    assert !ei.isPinnedDown();
+
+    assert ei.incPinDown(); // should return true because this is the first inc
+
+    // count up to the max number
+    for (int i=2; i<= ElementInfo.ATTR_PINDOWN_MASK; i++){
+      assert !ei.incPinDown();
+      assert ei.getPinDownCount() == i;
+    }
+
+    // count exceeded, now it should throw a JPFException
+    try {
+      ei.incPinDown();
+      assert false : "incPinDown did not throw";
+    } catch (JPFException x){
+      System.out.println("caught " + x + ", getPinDownCount() = " + ei.getPinDownCount());
+    }
+
+    // count down to the first one
+    for (int i=ElementInfo.ATTR_PINDOWN_MASK-1; i>0; i--){
+      assert !ei.decPinDown() : "decPinDown() from " + ei.getPinDownCount() + " returned true";
+      assert ei.getPinDownCount() == i : "getPinDownCount() = " + ei.getPinDownCount() +
+              " != " + i;
+    }
+
+    assert ei.decPinDown(); // should return true now
+    assert ei.getPinDownCount() == 0 : "getPinDownCount() != 0";
+  }
+  
+  
+  
+  @Test
+  public void testALiveFlag() {
+    DynamicElementInfo ei = new DynamicElementInfo();
+
+    assert !ei.isMarked();
+    
+    ei.setAlive(true);
+    
+    assert  ei.isAlive(true);
+    assert !ei.isAlive(false);
+    assert  ei.isMarkedOrAlive(true);
+    assert !ei.isMarkedOrAlive(false);
+    
+    
+    ei.setAlive(false);
+    
+    assert !ei.isAlive(true);
+    assert  ei.isAlive(false);
+    assert !ei.isMarkedOrAlive(true);
+    assert  ei.isMarkedOrAlive(false);
+    
+  }
+
+  @Test
+  public void testMarkedFlag() {
+    DynamicElementInfo ei = new DynamicElementInfo();
+
+    assert !ei.isMarked();
+    
+    ei.setMarked();
+    assert ei.isMarked();
+
+    ei.setUnmarked();
+    assert !ei.isMarked();
+    
+  }
+
+  @Test
+  public void testMarkedOrAlive() {
+    DynamicElementInfo ei = new DynamicElementInfo();
+    boolean[] boolValues = { true, false};
+    
+    assert !ei.isMarked();
+    
+    ei.setMarked();
+    assert ei.isMarked();
+    
+    for(boolean b : boolValues) {
+       ei.setAlive(b);
+       
+      assert ei.isMarkedOrAlive(true);
+      assert ei.isMarkedOrAlive(false);
+    }
+    
+
+    ei.setUnmarked();
+    assert !ei.isMarked();
+    
+    for(boolean b : boolValues) {
+      ei.setAlive(b);
+      
+      assert ei.isMarkedOrAlive(true) == ei.isAlive(true);
+      assert ei.isMarkedOrAlive(false) == ei.isAlive(false);
+    }
+  }
+
+}
diff --git a/src/tests/gov/nasa/jpf/vm/SystemStateTest.java b/src/tests/gov/nasa/jpf/vm/SystemStateTest.java
new file mode 100644 (file)
index 0000000..355f719
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.BooleanChoiceGenerator;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.SystemState;
+import gov.nasa.jpf.vm.choice.DoubleChoiceFromList;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+
+import org.junit.Test;
+
+
+/**
+ * unit test driver for SystemState functions
+ */
+public class SystemStateTest extends TestJPF {
+
+  static class MyJVM extends SingleProcessVM {
+
+    @Override
+       protected void notifyChoiceGeneratorSet (ChoiceGenerator<?>cg) {
+      System.out.println("notifyChoiceGeneratorSet: " + cg);
+    }
+    @Override
+       protected void notifyChoiceGeneratorAdvanced (ChoiceGenerator<?>cg) {
+      System.out.println("notifyChoiceGeneratorAdvanced: " + cg);
+    }
+    @Override
+       protected void notifyChoiceGeneratorProcessed (ChoiceGenerator<?>cg) {
+      System.out.println("notifyChoiceGeneratorProcessed: " + cg);
+    }
+  }
+
+  static class MySystemState extends SystemState {
+  }
+
+
+  @Test
+  public void testCascadedCGops() {
+
+    MyJVM vm = new MyJVM();
+    MySystemState ss = new MySystemState();
+
+    IntChoiceFromSet       cg0 = new IntChoiceFromSet( "cg0", -100, -200); // not cascaded
+    BooleanChoiceGenerator cg1 = new BooleanChoiceGenerator("cg1"); // false,true
+    IntChoiceFromSet       cg2 = new IntChoiceFromSet( "cg2", 1, 2);
+    DoubleChoiceFromList    cg3 = new DoubleChoiceFromList( "cg3", 42.1, 42.2);
+
+    cg2.isCascaded = true;
+    cg1.isCascaded = true;
+
+    cg3.prev = cg2;
+    cg2.prev = cg1;
+    cg1.prev = cg0;
+    ss.curCg = cg3;
+
+    cg0.advance();
+
+    //--- test initial advance
+    System.out.println("--- testing advanceCurCg()");
+    ss.advanceCurCg(vm);
+
+    assert cg0.getNextChoice() == -100;
+    assert cg1.getNextChoice() == false;
+    assert cg2.getNextChoice() == 1;
+    assert cg3.getNextChoice() == 42.1;
+
+
+    //--- test advanceCascadedParent
+    System.out.println("--- testing advanceCascadedParent()");
+    cg2.advance(2);
+    cg3.advance(2);
+
+    assert !cg2.hasMoreChoices();
+    assert !cg3.hasMoreChoices();
+
+    System.out.println(cg1);
+    System.out.println(cg2);
+    System.out.println(cg3);
+        
+    ss.advanceCascadedParent(vm,cg3);
+
+    assert cg0.getNextChoice() == -100;
+    assert cg1.getNextChoice() == true;
+    assert cg2.getNextChoice() == 1;
+    assert cg3.getNextChoice() == 42.1;
+  }
+
+  @Test
+  public void testCascadedCGadvance() {
+
+    MyJVM vm = new MyJVM();
+    MySystemState ss = new MySystemState();
+
+    BooleanChoiceGenerator cg1 = new BooleanChoiceGenerator("cg1"); // false,true
+    IntChoiceFromSet       cg2 = new IntChoiceFromSet( "cg2", 1, 2);
+    DoubleChoiceFromList    cg3 = new DoubleChoiceFromList( "cg3", 42.1, 42.2);
+
+    cg2.isCascaded = true;
+    cg1.isCascaded = true;
+
+    cg3.prev = cg2;
+    cg2.prev = cg1;
+    ss.curCg = cg3;
+
+    int n = 0;
+    while (ss.advanceCurCg(vm)){
+      System.out.println("--");
+      n++;
+    }
+
+    assert n == 8;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/TypesTest.java b/src/tests/gov/nasa/jpf/vm/TypesTest.java
new file mode 100644 (file)
index 0000000..cd7cec1
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.Types;
+
+import org.junit.Test;
+
+/**
+ * unit tests for gov.nasa.jpf.vm.Types
+ */
+public class TypesTest extends TestJPF {
+
+  @Test public void testGetSignatureName () {
+    
+    String in  = "int foo(int,java.lang.String)";
+    String out = "foo(ILjava/lang/String;)I";    
+    String s = Types.getSignatureName(in);
+    System.out.println( in + " => " + s);
+    assert out.equals(s);
+
+    in  = "double[] what_ever (char[], X )";
+    out = "what_ever([CLX;)[D";
+    s = Types.getSignatureName(in);
+    System.out.println( in + " => " + s);
+    assert out.equals(s);
+
+    in  = "bar()";
+    out = "bar()";
+    s = Types.getSignatureName(in);
+    System.out.println( in + " => " + s);
+    assert out.equals(s);
+
+  }
+
+  //... and many more to come
+}
diff --git a/src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromListTest.java b/src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromListTest.java
new file mode 100644 (file)
index 0000000..7d906f5
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.choice.IntChoiceFromList;
+
+import org.junit.Test;
+
+/**
+ * unit test for IntChoiceFromList
+ */
+public class IntChoiceFromListTest extends TestJPF {
+
+  private void testListContents(ChoiceGenerator<Integer> cg) {
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 3);
+    cg.advance();
+    assertEquals ((int)cg.getNextChoice(), 4);
+  }
+
+  @Test
+  public void testListWithOutDuplicates() {
+    IntChoiceFromList cg = new IntChoiceFromList("test", 1, 2, 3, 4);
+    testListContents(cg);
+    assertFalse (cg.hasMoreChoices());
+  }
+
+  @Test
+  public void testListWithDuplicates() {
+    IntChoiceFromList cg = new IntChoiceFromList("test1", 1, 2, 3, 4, 4);
+    testListContents(cg);
+    cg.advance();
+    assertFalse (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 4);
+  }
+
+  @Test
+  public void testListWithDuplicates2() {
+    IntChoiceFromList cg = new IntChoiceFromList("test2", 1, 2, 1, 2, 1, 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertFalse (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromSetTest.java b/src/tests/gov/nasa/jpf/vm/choice/IntChoiceFromSetTest.java
new file mode 100644 (file)
index 0000000..cace102
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.choice;
+
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.choice.IntChoiceFromSet;
+
+import org.junit.Test;
+
+/**
+ * unit test for IntChoiceFromSet
+ */
+public class IntChoiceFromSetTest extends TestJPF {
+
+  private void testSetContents(ChoiceGenerator<Integer> cg) {
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 3);
+    cg.advance();
+    assertFalse (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 4);
+  }
+
+  @Test
+  public void testSetWithOutDuplicates() {
+    IntChoiceFromSet cg = new IntChoiceFromSet("test", 1, 2, 3, 4);
+    testSetContents(cg);
+  }
+
+  @Test
+  public void testSetWithDuplicates() {
+    IntChoiceFromSet cg = new IntChoiceFromSet("test1", 1, 2, 3, 4, 4, 4);
+    testSetContents(cg);
+  }
+
+  @Test
+  public void testSetWithDuplicates2() {
+    IntChoiceFromSet cg = new IntChoiceFromSet("test2", 1, 2, 1, 2, 1, 2);
+    cg.advance();
+    assertTrue (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 1);
+    cg.advance();
+    assertFalse (cg.hasMoreChoices());
+    assertEquals ((int)cg.getNextChoice(), 2);
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/FinalizerThreadTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/FinalizerThreadTest.java
new file mode 100644 (file)
index 0000000..39625e8
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ * 
+ * Test suit for FinalizerThread & FinalizeThreadInfo
+ */
+public class FinalizerThreadTest extends TestMultiProcessJPF {
+  //This is to make sure that idle finalizers do not cause deadlocks in multiprocess apps
+  @Test
+  public void testMultipleIdleFinalizerThreads () {
+    if (mpVerifyNoPropertyViolation(2, "+vm.process_finalizers=true")){
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_MethodTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_MethodTest.java
new file mode 100644 (file)
index 0000000..f8cd0a3
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.JPF_java_lang_reflect_Method;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class JPF_gov_nasa_jpf_vm_multiProcess_MethodTest extends NativePeer {
+  private static List<Integer> prcIds = new ArrayList<Integer>();
+
+  protected static void resetPrcIds() {
+    prcIds.clear();
+  }
+
+  private static List<MethodInfo> methods =  new ArrayList<MethodInfo>();
+
+  @MJI
+  public void keepMethod__Ljava_lang_reflect_Method_2I__V(MJIEnv env, int objRef, int mthRef, int prcId) {
+    MethodInfo mi = JPF_java_lang_reflect_Method.getMethodInfo(env, mthRef);
+    if(!prcIds.contains(prcId)) {
+      prcIds.add(prcId);
+      methods.add(mi);
+    }
+  }
+
+  protected static List<MethodInfo> getMethods() {
+    return methods;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_NativePeerTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_NativePeerTest.java
new file mode 100644 (file)
index 0000000..069de1b
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class JPF_gov_nasa_jpf_vm_multiProcess_NativePeerTest extends NativePeer {
+  protected static int staticCounter = 0;
+  private int counter = 0;
+
+  @MJI
+  public void incNativeCounters____V(MJIEnv env, int objRef) {
+    staticCounter++;
+    counter++;
+  }
+
+  @MJI
+  public int getNativeCounter____I(MJIEnv env, int objRef) {
+    return counter;
+  }
+
+  public static int getStaticNativeCounter() {
+    return staticCounter;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.java
new file mode 100644 (file)
index 0000000..683e541
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.ThreadInfo;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest extends NativePeer {
+  private static List<Integer> prcIds = new ArrayList<Integer>();
+
+  protected static void resetPrcIds() {
+    prcIds.clear();
+  }
+
+  private static List<ThreadInfo> threads =  new ArrayList<ThreadInfo>();
+
+  protected static void resetThreads() {
+    threads.clear();
+  }
+
+  @MJI
+  public void keepThread__Ljava_lang_Thread_2I__V(MJIEnv env, int objRef, int thdRef, int prcId) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(thdRef);
+    if(!prcIds.contains(prcId)) {
+      prcIds.add(prcId);
+      threads.add(ti);
+    }
+  }
+
+  protected static List<ThreadInfo> getThreads() {
+    return threads;
+  }
+
+  private static List<Integer> threadIds = new ArrayList<Integer>();
+
+  @MJI
+  public static void addToThreads__Ljava_lang_Thread_2__V (MJIEnv env, int objRef, int thdRef) {
+    ThreadInfo ti = env.getThreadInfoForObjRef(thdRef);
+
+    int id = ti.getId();
+    if(!threadIds.contains(id)) {
+      threadIds.add(id);
+    }
+  }
+
+  protected static List<Integer> getThreadIds() {
+    return threadIds;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.java
new file mode 100644 (file)
index 0000000..594b436
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest extends NativePeer {
+  private static List<Integer> prcIds = new ArrayList<Integer>();
+
+  protected static void resetPrcIds() {
+    prcIds.clear();
+  }
+
+  private static List<ClassInfo> annClasses =  new ArrayList<ClassInfo>();
+
+  @MJI
+  public void keepAnnotationClass__Ljava_lang_Class_2I__V(MJIEnv env, int objRef, int annoClsRef, int prcId) {
+    ClassInfo aci = env.getReferredClassInfo(annoClsRef);
+    if(!prcIds.contains(prcId)) {
+      prcIds.add(prcId);
+      annClasses.add(aci);
+    }
+  }
+
+  protected static List<ClassInfo> getAnnotationClasses() {
+    return annClasses;
+  }
+
+  private static List<ClassLoaderInfo> classLoaders =  new ArrayList<ClassLoaderInfo>();
+
+  @MJI
+  public void keepClassLoader__Ljava_lang_ClassLoader_2I__V(MJIEnv env, int objRef, int clRef, int prcId) {
+    ClassLoaderInfo cl = env.getClassLoaderInfo(clRef);
+
+    if(!prcIds.contains(prcId)) {
+      prcIds.add(prcId);
+      classLoaders.add(cl);
+    }
+  }
+
+  protected static List<ClassLoaderInfo> getClassLoaders() {
+    return classLoaders;
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/MethodTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/MethodTest.java
new file mode 100644 (file)
index 0000000..e5dc997
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+import gov.nasa.jpf.vm.MethodInfo;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class MethodTest extends TestMultiProcessJPF {
+  public native void keepMethod(Method m, int prcId);
+
+  // To check the type safe cloning of methods
+  @Test
+  public void methodCloneTest() throws SecurityException, NoSuchMethodException {
+    if(!isJPFRun()) {
+      JPF_gov_nasa_jpf_vm_multiProcess_MethodTest.resetPrcIds();
+    }
+
+    if (mpVerifyNoPropertyViolation(2)) {
+      Method m = MethodTest.class.getMethod("methodCloneTest", new Class[]{});
+      int prcId = getProcessId();
+      keepMethod(m, prcId);
+    }
+
+    if(!isJPFRun()) {
+      List<MethodInfo> methods = JPF_gov_nasa_jpf_vm_multiProcess_MethodTest.getMethods();
+      assertEquals(methods.size(), 2);
+      assertTrue(methods.get(0)!=methods.get(1));
+    }
+  }
+
+  @Test
+  public void methodDeclaringClassTest() throws SecurityException, NoSuchMethodException {
+    if (mpVerifyNoPropertyViolation(2)) {
+      Class<?> cls = MethodTest.class;
+
+      // The loader of this class should be the same as the loader that loads 
+      // the class java.lang.Thread within this process
+      assertEquals(cls.getClassLoader(), ClassLoader.getSystemClassLoader());
+      for(Method m: cls.getDeclaredMethods()) {
+        assertEquals(m.getDeclaringClass(), cls);
+      }
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/NativePeerTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/NativePeerTest.java
new file mode 100644 (file)
index 0000000..f9044ce
--- /dev/null
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class NativePeerTest extends TestMultiProcessJPF {
+
+  native void incNativeCounters();
+
+  native int getNativeCounter();
+
+  // To make sure that native peers are kept separately - note different class
+  // with the same fully qualified name still share the same NativePeer class
+  // but they keep different instances of it
+  @Test
+  public void nativePeerTest() {
+    // Note that this code is executed 4 times (twice by each process main thread).
+    // Since we do not restore NativePeer states the maximum value of counter in 
+    // NativePeer should be 2
+    if (mpVerifyNoPropertyViolation(2, "+vm.max_transition_length=MAX")) { // make sure we don't get a spurious CG
+      incNativeCounters();
+
+      int i = getNativeCounter();
+      assertTrue(i==1 || i==2);
+    }
+
+    if(!isJPFRun()) {
+      // To make sure this code is executed 4 times
+      assertEquals(JPF_gov_nasa_jpf_vm_multiProcess_NativePeerTest.getStaticNativeCounter(), 4);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/StringTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/StringTest.java
new file mode 100644 (file)
index 0000000..9d453f1
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class StringTest extends TestMultiProcessJPF {
+  String[] args = {};
+  
+  @Test
+  public void testInterns() throws IOException {
+    
+    if (mpVerifyNoPropertyViolation(2, args)) {
+      String s0 = "something"; // interned string
+      String s1 = new String("something"); // a new string which is not interned
+      String s2 = s1.intern(); // taken from intern table
+      String s3 = "something".intern(); // taken from intern table
+      
+      assertSame(s0.getClass(), java.lang.String.class);
+      assertSame(s1.getClass(), java.lang.String.class);
+      
+      assertEquals(s0,s1);
+      assertFalse(s0==s1);
+      
+      assertSame(s0,s2);
+      assertSame(s0,s3);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/ThreadTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/ThreadTest.java
new file mode 100644 (file)
index 0000000..0ff7050
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+import gov.nasa.jpf.vm.ChoiceGenerator;
+import gov.nasa.jpf.vm.Scheduler;
+import gov.nasa.jpf.vm.SyncPolicy;
+import gov.nasa.jpf.vm.ThreadInfo;
+import gov.nasa.jpf.vm.VM;
+
+import java.util.List;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class ThreadTest extends TestMultiProcessJPF {
+  private native void keepThread(Thread thd, int prcId);
+
+  // To make sure that each process has its own main thread 
+  @Test
+  public void mainThreadsIdTest() {
+    if(!isJPFRun()) {
+      JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.resetPrcIds();
+    }
+
+    if (mpVerifyNoPropertyViolation(2)) {
+      int prcId = getProcessId();
+      keepThread(Thread.currentThread(), prcId);
+    }
+
+    if(!isJPFRun()) {
+      List<ThreadInfo> threads = JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.getThreads();
+      assertEquals(threads.size(), 2);
+      assertTrue(threads.get(0)!=threads.get(1));
+    }
+  }
+
+  private native void addToThreads(Thread thd);
+
+  public class TestThread extends Thread {
+    @Override
+       public void run() {
+      addToThreads(this);
+    }
+  }
+
+  // To make sure that the total number of threads created does not exceed 4
+  // where each processes includes two threads
+  @Test
+  public void numOfThreadsTest() {
+    if(!isJPFRun()) {
+      JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.resetThreads();
+    }
+    if (mpVerifyNoPropertyViolation(2)) {
+      TestThread thd = new TestThread();
+      thd.start();
+
+      addToThreads(Thread.currentThread());
+    }
+
+    if(!isJPFRun()) {
+      assertEquals(JPF_gov_nasa_jpf_vm_multiProcess_ThreadTest.getThreadIds().size(), 4);
+    }
+  }
+
+  public static class InterleaveCheckListener extends ListenerAdapter {
+    static int numOfCG = 0;
+
+    @Override
+    public void choiceGeneratorProcessed (VM vm, ChoiceGenerator<?> newCG) {
+      String id = newCG.getId();
+
+      if(!id.equals(SyncPolicy.ROOT) && !id.equals(SyncPolicy.TERMINATE)) {
+        fail("Threads from two different processes should only interleave at the thread termination point!");
+      }
+
+      numOfCG++;
+    }
+  }
+
+  private static int counter = 0;
+
+  // To make sure that the only point that threads from two processes interleave 
+  // is the thread termination point & number of choice generators does not 
+  // exceed 3
+  @Test
+  public void threadInterleavingTest() {
+    if (mpVerifyNoPropertyViolation(2, "+listener=gov.nasa.jpf.vm.multiProcess.ThreadTest$InterleaveCheckListener",
+            "+vm.max_transition_length=MAX")) {
+      // InterleaveCheck listener makes sure that transition is not broken at the 
+      // static field access
+      counter = 0;
+    }
+
+    if(!isJPFRun()) {
+      assertEquals(InterleaveCheckListener.numOfCG, 3);
+    }
+  }
+}
diff --git a/src/tests/gov/nasa/jpf/vm/multiProcess/TypeSeparationTest.java b/src/tests/gov/nasa/jpf/vm/multiProcess/TypeSeparationTest.java
new file mode 100644 (file)
index 0000000..dea1294
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 gov.nasa.jpf.vm.multiProcess;
+
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+import gov.nasa.jpf.util.test.TestMultiProcessJPF;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.ClassLoaderInfo;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class TypeSeparationTest extends TestMultiProcessJPF {
+
+  private static int counter = 0;
+
+  private static void incCounter() {
+    counter++;
+  }
+
+  // To make sure that our multiProcess VM keeps the static attributes separated
+  // This also checks for type safe clone for the InvokeStatic instruction.
+  @Test
+  public void staticCounterTest() {
+    // Note that this code is executed 4 times. Since every time this is executed its
+    // state is restored, the value of counter should be always 1
+    if (mpVerifyNoPropertyViolation(2)) {
+      int id = getProcessId();
+      switch(id) {
+      case 0:
+        incCounter();
+        break;
+      case 1:
+        incCounter();
+        break;
+      default:
+        fail("invalid process number!");
+      }
+
+      assertEquals(counter,1);
+    }
+  }
+
+  @Retention(RetentionPolicy.RUNTIME)
+  @Inherited
+  public @interface A0 {
+  }
+
+  private native void keepAnnotationClass(Class annCls, int prcId);
+
+  @Test
+  public void annotationsTest () {
+    if(!isJPFRun()) {
+      JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.resetPrcIds();
+    }
+
+    if (mpVerifyNoPropertyViolation(2)) {
+      int prcId = getProcessId();
+      keepAnnotationClass(A0.class, prcId);
+    }
+
+    if(!isJPFRun()) {
+      List<ClassInfo> annClassList = JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.getAnnotationClasses();
+      assertEquals(annClassList.size(), 2);
+      assertTrue(annClassList.get(0)!=annClassList.get(1));
+    }
+  }
+
+
+  private native void keepClassLoader(ClassLoader thd, int prcId);
+
+  // to make sure that each process accesses the classes loaded by the right
+  // system class loader
+  @Test
+  public void systemClassLoaderTest() {
+    if(!isJPFRun()) {
+      JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.resetPrcIds();
+    }
+
+    if (mpVerifyNoPropertyViolation(2)) {
+      ClassLoader cl = Object.class.getClassLoader();
+
+      // in our implementation this goes through the class hierarchy of the 
+      // current thread and it returns the class loader of the Thread class
+      ClassLoader sysLoader = ClassLoader.getSystemClassLoader();
+      assertEquals(cl, sysLoader);
+
+      int prcId = getProcessId();
+      keepClassLoader(cl, prcId);
+    }
+
+    if(!isJPFRun()) {
+      List<ClassLoaderInfo> classLoaders = JPF_gov_nasa_jpf_vm_multiProcess_TypeSeparationTest.getClassLoaders();
+      assertEquals(classLoaders.size(), 2);
+      assertTrue(classLoaders.get(0)!=classLoaders.get(1));
+    }
+  }
+}
diff --git a/src/tests/java8/DefaultMethodTest.java b/src/tests/java8/DefaultMethodTest.java
new file mode 100644 (file)
index 0000000..d1c8977
--- /dev/null
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java8;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+import gov.nasa.jpf.vm.Verify;
+import org.junit.Test;
+
+/**
+ * regression test for Java 8 default methods
+ */
+public class DefaultMethodTest extends TestJPF {
+  
+  //------------------------------------------ non-ambiguous recursive lookup
+  
+  interface A1 {
+    default int getValue(){
+      return 42;
+    }
+  } 
+  
+  interface B1 extends A1 {
+    // nothing
+  }
+  
+  static class C1 implements A1 {
+    // nothing
+  }
+  
+  static class D1 extends C1 {
+    // nothing
+  } 
+  
+  @Test
+  public void testSingleMethod (){
+    if (verifyNoPropertyViolation()){
+      D1 o = new D1();
+      int result = o.getValue();
+      System.out.println(result);
+      assertTrue (result == 42);
+    }
+  }
+  
+  //------------------------------------------ ambiguity resolution
+  
+  interface B2 {
+    default int getValue(){
+      return 3;
+    }
+  }
+  
+  static class D2 implements A1, B2 {
+    @Override
+    public int getValue(){
+      return A1.super.getValue() + B2.super.getValue();
+    }
+  }
+  
+  @Test
+  public void testExplicitDelegation (){
+    if (verifyNoPropertyViolation()){
+      D2 o = new D2();
+      int result = o.getValue();
+      System.out.println(result);
+      assertTrue (result == 45);
+    }    
+  }
+
+  //------------------------------------------- overloaded methods
+
+  interface E1 {
+    default int foo (String s1, String s2){
+      System.out.println("this is E1.foo(String,String)");
+      return 42;
+    }
+
+    default int foo (Object o1, Object o2){
+      System.out.println("this is E1.foo(Object,Object)");
+      return 0;
+    }
+  }
+
+  static class F implements E1 {
+    String getId() {
+      return "whatever";
+    }
+
+    void bar (){
+      int r = foo("blah", getId());
+      assertTrue(r == 42);
+    }
+  }
+
+  @Test
+  public void testOverloadedDefaults(){
+    if (verifyNoPropertyViolation()){
+      F o = new F();
+      o.bar();
+    }
+  }
+
+  //----------------------------------------------- native peer for interface
+
+  interface G1 {
+    default int foo (){  // should be intercepted by peer
+      System.out.println("this is bytecode G1.foo()");
+      return -1;
+    }
+  }
+
+  static class H implements G1 {
+    void bar (){
+      int r = foo();
+      //assertTrue(r == 42);
+    }
+  }
+
+  @Test
+  public void testInterfacePeer(){
+    if (verifyNoPropertyViolation()){
+      H o = new H();
+      o.bar();
+    }
+  }
+
+  // <2do> how to test IncompatibleClassChangeError without explicit classfile restore?
+}
+
diff --git a/src/tests/java8/JPF_java8_DefaultMethodTest$G1.java b/src/tests/java8/JPF_java8_DefaultMethodTest$G1.java
new file mode 100644 (file)
index 0000000..0a91afe
--- /dev/null
@@ -0,0 +1,16 @@
+package java8;
+
+import gov.nasa.jpf.annotation.MJI;
+import gov.nasa.jpf.vm.MJIEnv;
+import gov.nasa.jpf.vm.NativePeer;
+
+/**
+ * Created by pcmehlitz on 4/1/15.
+ */
+public class JPF_java8_DefaultMethodTest$G1 extends NativePeer {
+  @MJI
+  public int foo____I (MJIEnv env, int objRef){
+    System.out.println("this is native G1.foo()");
+    return 42;
+  }
+}
diff --git a/src/tests/java8/LambdaTest.java b/src/tests/java8/LambdaTest.java
new file mode 100644 (file)
index 0000000..b6ec695
--- /dev/null
@@ -0,0 +1,297 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java8;
+
+import gov.nasa.jpf.util.test.TestJPF;
+
+import org.junit.Test;
+
+/**
+ * @author Nastaran Shafiei <nastaran.shafiei@gmail.com>
+ */
+public class LambdaTest extends TestJPF{
+  
+  static class EnforcedException extends RuntimeException {
+    // nothing in here
+  }
+  
+  @Test
+  public void testFuncObjAssignment() {
+    if(verifyUnhandledException(EnforcedException.class.getName())) {
+      
+      Runnable r = () -> {
+        throw new EnforcedException(); // make sure it gets here
+        };
+      
+      assertTrue(r != null);
+      
+      (new Thread(r)).start();
+    }
+  }
+  
+  public interface FI1 {
+    void sam();
+  }
+  
+  public interface FI2 extends FI1 {
+    @Override
+       public String toString();
+  }
+  
+  @Test
+  public void testSyntheticFuncObjClass() {
+    if (verifyNoPropertyViolation()) {
+      
+      FI2 fi = () -> {
+        return;
+        };
+      
+      assertTrue(fi != null);
+      
+      Class cls = fi.getClass();
+      
+      assertEquals(cls.getInterfaces().length, 1);
+      
+      assertEquals(cls.getDeclaredMethods().length, 1);
+            
+      assertSame(cls.getInterfaces()[0], FI2.class);
+      
+      assertSame(cls.getSuperclass(), Object.class);
+    }
+  }
+  
+  public interface FI3 {
+    public String ret();
+  }
+  
+  @Test
+  public void testSAMReturn() {
+    if (verifyNoPropertyViolation()) {
+      FI3 rt = () -> {
+        return "something"; 
+        };
+      
+      assertEquals(rt.ret(),"something"); 
+    }
+  }
+  
+  public class C {
+    int x = 1;
+  }
+  
+  public interface IncX {
+    public int incX(C o);
+  }
+  
+  @Test
+  public void testLambdaArgument() {
+    if (verifyNoPropertyViolation()) {
+      IncX fo = (arg) -> {
+        return ++arg.x;
+        };
+      
+      C o = new C();
+      
+      assertEquals(fo.incX(o),2);
+      assertEquals(fo.incX(o),3);
+    }
+  }
+  
+  static Integer io = new Integer(20);
+  
+  @Test
+  public void testClosure() {
+    if (verifyNoPropertyViolation()) {
+      int i = 10;
+      
+      FI1 fi = () -> {
+        assertSame(i,10);
+        assertSame(io.intValue(), 20);
+      };
+      
+      fi.sam();
+    }
+  }
+  
+  static void method(FI1 fi) {
+    fi.sam();
+  }
+  
+  @Test
+  public void testPassingToMethod() {
+    if (verifyUnhandledException(EnforcedException.class.getName())) {
+      int i = 10;
+      
+      method(() -> {
+        assertSame(i,10);
+        assertSame(io.intValue(), 20);
+        throw new EnforcedException();
+      });
+    }
+  }
+  
+  // When invokedynamic executes for the first time, it creates a new function object.
+  // Re-executing the same bytecode returns the existing function object.
+  @Test
+  public void testRepeatInvokedynamic() {
+    if (verifyNoPropertyViolation()) {
+      int i = 10;
+      FI1 f1, f2 = null; 
+      
+      for(int j=0; j<2; j++) {
+        f1 = () -> {
+          System.out.println("hello world!");
+        };
+        
+        if(j==1) {
+          assertTrue(f1!=null);
+          assertSame(f1,f2);
+        }
+        
+        f2 = f1;
+      }
+    }
+  }
+  
+  public static class C2 {
+    public static void throwException() {
+      throw new EnforcedException();
+    }
+  }
+  
+  @Test
+  public void testDoubleCloneOperator() {
+    if (verifyUnhandledException(EnforcedException.class.getName())) {
+      FI1 fi = C2::throwException;
+      fi.sam();
+    }
+  }
+  
+  static class A {
+    static {
+      if(true) {
+        throw new EnforcedException();
+      }
+    }
+  }
+
+  @Test
+  public void testInitDoubleCloneOperator() {
+    if (verifyUnhandledException(EnforcedException.class.getName())) {
+      new Thread(A::new).start();
+    }
+  }
+  
+  static class D {
+    static final B b = new B();
+  }
+  
+  static class B {
+    static final D a = new D();
+  }
+  
+  @Test
+  public void testClinitDeadlock() {
+    if(verifyDeadlock()) {
+      new Thread(D::new).start();
+      new B();
+    }
+  }
+  
+  @Test
+  public void testLambdaTypeName() {
+    if(verifyNoPropertyViolation()) {
+      Runnable r1 = (A::new);
+      Runnable r2 = (B::new);
+      
+      assertFalse(r1.getClass().getName().equals(r2.getClass().getName()));
+    }
+  }
+  
+  public interface FI {
+    default boolean returnTrue() {
+      return true;
+    }
+    @Override
+    public String toString();
+    public String toString(int i);
+  }
+  
+  @Test
+  public void testLambdaWithOverridenDefaultMethods() {
+    if(verifyNoPropertyViolation()) {
+      FI fi = (int i) -> {return "output:"+ i;};
+      assertEquals(fi.toString(10),"output:10");
+    }
+  }
+  
+  public interface FI4 {
+  }
+  
+  public interface FI5 extends FI {
+    @Override
+    public boolean equals(Object obj);
+  }
+  
+  @Test
+  public void testLambdaWithMultipleSuperInterfaces() {
+    if(verifyNoPropertyViolation()) {
+      FI5 fi = (int i) -> {return "output:"+ i;};
+      assertEquals(fi.toString(10),"output:10");
+    }
+  }
+  
+  public static class Foo {
+    
+    Integer i = 0;
+    static Integer j = 1;
+    
+    
+    public FI1 invokSam(FI1 fi) {
+      fi.sam();
+      return fi;
+    }
+    
+    
+    public FI1 withFreeVar() {
+      return invokSam(()->{Foo foo = this;});
+    }
+    
+    public static FI1 withStatic(Foo foo) {
+      return foo.invokSam(()->{Foo.j = 10;});
+    }
+  }
+  
+  @Test
+  public void testFreeVariables() {
+    if(verifyNoPropertyViolation()) {
+      
+      Foo foo = new Foo();
+      
+      FI1 fi1 = foo.withFreeVar();  
+      FI1 fi2 = foo.withFreeVar();
+      
+      assertFalse(fi1==fi2);
+     
+      fi1 = Foo.withStatic(foo);
+      fi2 = Foo.withStatic(foo);
+      
+      assertSame(fi1,fi2);
+    }
+  }
+}
diff --git a/src/tests/java8/TypeAnnotationTest.java b/src/tests/java8/TypeAnnotationTest.java
new file mode 100644 (file)
index 0000000..eba1ed9
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * Copyright (C) 2014, United States Government, as represented by the
+ * Administrator of the National Aeronautics and Space Administration.
+ * All rights reserved.
+ *
+ * The Java Pathfinder core (jpf-core) platform is 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 java8;
+
+import gov.nasa.jpf.ListenerAdapter;
+import gov.nasa.jpf.jvm.ClassFile;
+import gov.nasa.jpf.util.test.TestJPF;
+import gov.nasa.jpf.vm.AbstractTypeAnnotationInfo;
+import gov.nasa.jpf.vm.ClassInfo;
+import gov.nasa.jpf.vm.FieldInfo;
+import gov.nasa.jpf.vm.LocalVarInfo;
+import gov.nasa.jpf.vm.MethodInfo;
+import gov.nasa.jpf.vm.VM;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import org.junit.Test;
+
+
+/**
+ * regression test for Java 8 type annotations (JSR 308)
+ */
+public class TypeAnnotationTest extends TestJPF {
+
+  //--- test type annotations
+  @Retention(RetentionPolicy.RUNTIME)
+  @Target(ElementType.TYPE_USE)
+  @interface MyTA {}
+
+  //--- test class hierarchy
+  
+  interface Ifc {}
+  static class Base {}
+  
+  public class Anno8 extends @MyTA Base implements @MyTA Ifc {
+
+    @MyTA int data;
+
+    @MyTA int baz (@MyTA int a, int b){
+      @MyTA int x = a + b;
+      return x;
+    }
+  }
+  
+  //--- listener to check annotations are set
+  public static class Listener extends ListenerAdapter {
+    
+    protected int numberOfTargetTypes (AbstractTypeAnnotationInfo[] annos, int targetType){
+      int n = 0;
+      for (AbstractTypeAnnotationInfo tai : annos){
+        if (tai.getTargetType() == targetType){
+          n++;
+        }
+      }
+      return n;
+    }
+    
+    @Override
+    public void classLoaded(VM vm, ClassInfo loadedClass) {
+      if (loadedClass.getName().equals("java8.TypeAnnotationTest$Anno8")){
+        System.out.println("checking loaded class " + loadedClass.getName() + " for type annotations..");
+        
+        // <2do> - needs more tests..
+        
+        System.out.println("--- super types");
+        AbstractTypeAnnotationInfo[] tais = loadedClass.getTypeAnnotations();
+        for (AbstractTypeAnnotationInfo tai : tais){
+          System.out.println("  " + tai);
+        }
+        assertTrue(tais.length == 2);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.CLASS_EXTENDS) == 2); // base and interface
+        
+        System.out.println("--- fields");
+        FieldInfo fi = loadedClass.getDeclaredInstanceField("data");
+        tais = fi.getTypeAnnotations();
+        for (AbstractTypeAnnotationInfo tai : tais){
+          System.out.println("  " + tai);
+        }
+        assertTrue(tais.length == 1);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.FIELD) == 1);
+        
+        System.out.println("--- methods");
+        MethodInfo mi = loadedClass.getMethod("baz(II)I", false);
+        tais = mi.getTypeAnnotations();
+        for (AbstractTypeAnnotationInfo tai : tais){
+          System.out.println("  " + tai);
+        }
+        assertTrue(tais.length == 3);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.METHOD_RETURN) == 1);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.METHOD_FORMAL_PARAMETER) == 1);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.LOCAL_VARIABLE) == 1);
+        
+        LocalVarInfo lv = mi.getLocalVar("x", 4);
+        System.out.println("--- local var " + lv);
+        tais = lv.getTypeAnnotations();
+        for (AbstractTypeAnnotationInfo tai : tais){
+          System.out.println("  " + tai);
+        }
+        assertTrue(tais.length == 1);
+        assertTrue( numberOfTargetTypes(tais, ClassFile.LOCAL_VARIABLE) == 1);
+        
+      }
+    }
+  }
+  
+  @Test
+  public void testBasicTypeAnnotations (){
+    if (verifyNoPropertyViolation("+listener=java8.TypeAnnotationTest$Listener")){
+      Anno8 anno8 = new Anno8();
+    }
+  }
+  
+}