There are three versions of DCRuntime.java: the master one (in this directory) that contains
the actual DynComp runtime code, and two small, derivative versions with the same public
methods: one in ../dcomp-dummy and one in ../dcomp-transfer. This README explains why this
is the case and, should it be necessary, how to modify the derivative versions.

Post Java 8 there are increased security checks when loading JDK classes. In particular, the
core classes contained in the java.base module may not reference anything outside of
java.base. This means that for JDK 11 we cannot pre-instrument classes in the same manner as
was done for Java 8 as this would introduce external references to the DynComp runtime.

We get around this restriction in the following manner.  We create a shadow
DynComp runtime called java.lang.DCRuntime that contains all the public methods of
daikon.dcomp.DCRuntime, but the method bodies contain only a return statement.  When we
pre-instrument java.base we do two things differently from the JDK 8 case: we change all
references to daikon.dcomp.DCRuntime to refer to java.lang.DCRuntime instead, and we add
the dummy java.lang.DCRuntime to our java.base replacement jar. This allows us to pass the
security test when a class from java.base is loaded. Then, during DynComp startup, this
dummy version is replaced with the version in dcomp-transfer where each method calls the
corresponding method in daikon.dcomp.DCRuntime.

Post JDK 21, additional restrictions have been introduced that prevent developers
from explicitly creating or modifying classes in the java.lang package (or any
package in java.base) at compile time. However, runtime class redefinition with
Instrumentation.redefineClasses() still works, so with a small change to the build
process we can continue to use the transfer method. We declare the package to be
"jaxa.lang" instead of "java.lang" and then use sed to edit the classfile after
compilation back to "java.lang".

We do not anticipate that the two derivative versions will ever need to be modified.  This
would only happen if it was decided to add an additional public method to DCRuntime that
was to be called from instrumented code. This would require changes to DCInstrument to
add calls to this new runtime method from instrumented code. The new method would need to be
added to the master DCRuntime in this directory and the two derivative versions of DCRuntime
would need to be modified as well. Here are the steps to do so:

(cd ../dcomp-transfer && mv DCRuntime.java DCRuntime.java.temp && rm -f DCRuntime.class)
(cd ../dcomp-dummy && mv DCRuntime.java.dummy DCRuntime.java)
# Edit ../dcomp-dummy/DCRuntime.java to add the new dummy method
(cd ../dcomp-dummy && make && mv DCRuntime.class DCRuntime.class.dummy && mv DCRuntime.java DCRuntime.java.dummy)
(cd ../dcomp-transfer && mv DCRuntime.java.temp DCRuntime.java)
# Edit ../dcomp-transfer/DCRuntime.java to add the new transfer method
#    (method body is a call to matching new method in daikon.dcomp.DCRuntime)
(cd ../dcomp-transfer && make)

At this point, everything should be ready to proceed with a normal build of DynComp.
Note that the file daikon/dcomp-dummy/DCRuntime.class.dummy is used later in the build process
during the construction of dcomp_rt.jar.

These instructions work fine if you are working with a local build.  However, there is a
caveat when checking in your changes. Since we assumed that changing the interface would
be a rare event, the Makefile is not setup to support a clean build with just the three
DCRuntime.java files changed. You must include dcomp-dummy/DCRuntime.class.dummy in the
checkin otherwise the checkin tests will fail.
