Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c446a21
restore version from master
markro49 Aug 4, 2026
34c0e81
Merge remote-tracking branch 'upstream/master'
markro49 Aug 20, 2026
e10b77b
Merge remote-tracking branch 'upstream/master'
markro49 Sep 1, 2026
333118f
Restore null check in DCInstrument24.getDefiningInterface
markro49 Sep 2, 2026
56d54cf
Parse java.version robustly in Runtime
markro49 Sep 2, 2026
4555bb3
Remove ClassGen24.isStatic
markro49 Sep 2, 2026
b9aaa26
Explain why the JUnit state machine skips JDK classes
markro49 Sep 2, 2026
f0110ad
Record the correct method name when instrumentation fails
markro49 Sep 2, 2026
82cc298
Maintain the tag stack in an oversized method
markro49 Sep 2, 2026
98a6491
Fix javadoc reference into the Java 24 sibling
markro49 Sep 2, 2026
1b12d82
Use ASCII in a comment
markro49 Sep 3, 2026
c8bcda7
Respond to CodeRabbit review of PR 828
markro49 Sep 3, 2026
733bd57
Merge ../daikon-branch-master into dcomp24-fixes
mernst Sep 5, 2026
11352d3
Check for interface
mernst Sep 5, 2026
cb76b29
Update comment
mernst Sep 5, 2026
7fc6b2b
Code review
mernst Sep 5, 2026
e4c87dd
Code review
mernst Sep 5, 2026
789f3b8
Address remaining PR review comments
mernst Sep 5, 2026
ac64fa4
Large methods
mernst Sep 6, 2026
a6431ae
Suppress warnings
mernst Sep 6, 2026
ff23447
Code review
mernst Sep 6, 2026
657ad64
fix lock checker issue
markro49 Sep 6, 2026
8f69df8
Re-entrancy
mernst Sep 7, 2026
24da4b4
Merge branch 'dcomp24-fixes' of github.com:markro49/daikon into dcomp…
mernst Sep 7, 2026
b6caf94
Fix CI failures
mernst Sep 8, 2026
15c171c
Code review
mernst Sep 8, 2026
26419dc
Code review
mernst Sep 8, 2026
68191ee
Code review
mernst Sep 8, 2026
68e7598
Better ignore
mernst Sep 8, 2026
de7f0ec
`checkbashisms`
mernst Sep 8, 2026
58b72a9
Merge ../daikon-branch-master into dcomp24-fixes
mernst Sep 8, 2026
7f7c7df
Remove a dead test for a JUnit test class
markro49 Sep 10, 2026
4893865
Select by descriptor in codeLength
markro49 Sep 10, 2026
79ae6d7
Test that a rerun code builder registers a tracked method once
markro49 Sep 10, 2026
e0433cb
Merge master into dcomp24-fixes
markro49 Sep 10, 2026
73b7d43
fix nullness check error
markro49 Sep 10, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,13 @@ CHECKLINK ?= ${DAIKONDIR}/.utils/checklink
PLUME_SCRIPTS ?= ${DAIKONDIR}/.utils/plume-scripts

ifeq (,$(wildcard ${PLUME_SCRIPTS}))
dummy := $(shell mkdir ${DAIKONDIR}/.utils && git clone --depth=1 -q https://github.com/plume-lib/plume-scripts.git ${PLUME_SCRIPTS})
dummy := $(shell mkdir -p "$(dir ${PLUME_SCRIPTS})" && git clone --depth=1 -q https://github.com/plume-lib/plume-scripts.git "${PLUME_SCRIPTS}")
endif
SORT_DIRECTORY_ORDER := ${PLUME_SCRIPTS}/sort-directory-order
ifneq "$(wildcard ${SORT_DIRECTORY_ORDER})" "${SORT_DIRECTORY_ORDER}"
# The clone above did not happen or did not succeed, so sort-directory-order is not available.
SORT_DIRECTORY_ORDER := sort
endif

JAVA_RELEASE_NUMBER := $(shell java -version 2>&1 | head -1 | cut -d'"' -f2 | sed '/^1\./s///' | cut -d'.' -f1 | sed 's/-ea//')

Expand Down
3 changes: 3 additions & 0 deletions java/daikon/chicory/Instrument24.java
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,9 @@ private void instrumentCode(
MethodInfo curMethodInfo,
int method_info_index) {

// This handler modifies mgen, and may be run more than once.
mgen.resetForCodeBuilder();

MethodGen24.MInfo24 minfo =
new MethodGen24.MInfo24(method_info_index, mgen.getMaxLocals(), codeBuilder);

Expand Down
71 changes: 71 additions & 0 deletions java/daikon/chicory/MethodGen24.java
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,12 @@ public class MethodGen24 {
// TODO: Should uses of this be synchronized?
private ConstantPoolBuilder poolBuilder;

/**
* The mutable state of this method, as recorded by the first call to {@link
* #resetForCodeBuilder}. Null until then.
*/
private @Nullable State savedState;

/** Information about the current method. */
public static class MInfo24 {

Expand Down Expand Up @@ -766,6 +772,71 @@ public void setInstructionList(List<CodeElement> il) {
codeList = il;
}

/**
* A copy of the mutable state of a MethodGen24; see {@link MethodGen24#resetForCodeBuilder}.
* These are the fields that instrumentation modifies. The arrays and lists are copies, so that a
* State is unaffected by later modifications to the MethodGen24 it was made from.
*
* @param codeList a copy of {@link MethodGen24#codeList}
* @param localsTable a copy of {@link MethodGen24#localsTable}
* @param maxLocals the value of {@link MethodGen24#maxLocals}
* @param paramTypes a copy of {@link MethodGen24#paramTypes}
* @param paramNames a copy of {@link MethodGen24#paramNames}
* @param origLocalVariables a copy of {@link MethodGen24#origLocalVariables}
*/
@SuppressWarnings("ArrayRecordComponent") // defensive copies previent mutation of array fields
private record State(
List<CodeElement> codeList,
List<LocalVariable> localsTable,
int maxLocals,
ClassDesc[] paramTypes,
@Identifier String[] paramNames,
LocalVariable[] origLocalVariables) {}

/**
* Undoes every modification made to this MethodGen24 since the first call to this method, and
* returns true if this is that first call.
*
* <p>Call this at the top of every {@code CodeBuilder} handler that modifies this MethodGen24.
* The java.lang.classfile implementation may run such a handler more than once: if the code the
* handler built contains a branch whose target does not fit in the branch instruction's 2-byte
* operand, the implementation discards what was built and runs the handler again, this time
* widening those branches. The second run starts from a fresh CodeBuilder, but not from a fresh
* MethodGen24, so without this call the handler's modifications -- adding the DCompMarker
* parameter and renumbering the locals that follow it, for instance -- would be applied a second
* time to a MethodGen24 that already has them.
*
* <p>A handler that has other side effects must use the return value to perform them only once.
*
* @return true if this is the first call to this method on this MethodGen24
*/
public boolean resetForCodeBuilder() {
State state = savedState;
if (state == null) {
savedState =
new State(
new ArrayList<>(codeList),
new ArrayList<>(localsTable),
maxLocals,
paramTypes.clone(),
paramNames.clone(),
origLocalVariables.clone());
return true;
}
// As in the constructor, a LinkedList is the right choice for codeList.
@SuppressWarnings("JdkObsolete")
List<CodeElement> cl = new LinkedList<CodeElement>(state.codeList());
codeList = cl;
// Modify localsTable in place, because clients hold references to it.
localsTable.clear();
localsTable.addAll(state.localsTable());
maxLocals = state.maxLocals();
paramTypes = state.paramTypes().clone();
paramNames = state.paramNames().clone();
origLocalVariables = state.origLocalVariables().clone();
return false;
}

/**
* Returns string representation close to declaration format, 'public static void main(String[])',
* e.g.
Expand Down
48 changes: 42 additions & 6 deletions java/daikon/chicory/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -931,9 +931,11 @@ public Class<?> primitiveClass() {
}
}

/** The major version of the running JVM: 8 for Java 8, 24 for Java 24, and so on. */
private static final int javaMajorVersion = javaMajorVersion(System.getProperty("java.version"));

/** True if the running JVM is for Java 9 or later. */
private static final boolean isJava9orLater =
!System.getProperty("java.version").startsWith("1.");
private static final boolean isJava9orLater = javaMajorVersion >= 9;

/**
* Returns true if the running JVM is for Java 9 or later.
Expand All @@ -945,10 +947,7 @@ public static boolean isJava9orLater() {
}

/** True if the running JVM is for Java 24 or later. */
private static final boolean isJava24orLater =
!System.getProperty("java.version").startsWith("1.")
&& !System.getProperty("java.version").startsWith("9.")
&& Integer.parseInt(System.getProperty("java.version").substring(0, 2)) >= 24;
private static final boolean isJava24orLater = javaMajorVersion >= 24;

/**
* Returns true if the running JVM is for Java 24 or later.
Expand All @@ -958,4 +957,41 @@ public static boolean isJava9orLater() {
public static boolean isJava24orLater() {
return isJava24orLater;
}

/**
* Returns the major version encoded in a {@code java.version} property value: 8 for Java 8, 24
* for Java 24, and so on.
*
* <p>Both version schemes are accepted: the pre-Java-9 {@code "1.8.0_432"} form, whose major
* version is its second component, and the Java 9 and later {@code "24"}, {@code "24.0.1"}, and
* {@code "24-ea"} forms, whose major version is the first. In the latter scheme the major version
* may stand alone with no separator at all, as it does for a GA release such as {@code "9"}.
*
* <p>Only the leading digits are examined; anything after them is ignored rather than rejected,
* so {@code "9foo"} yields 9. That is deliberate. This runs from a static initializer, so
* throwing on an unrecognized suffix would turn an unanticipated vendor version string into an
* ExceptionInInitializerError inside an instrumented program, which is the failure this method
* exists to prevent. Ignoring the suffix instead yields the right major version for any string
* that begins with one. A value with no leading digits at all is still rejected.
*
* @param version the value of the {@code java.version} system property
* @return the major version it encodes
*/
// Package-private rather than private so that RuntimeTest can exercise it directly; the value
// derived from the running JVM is fixed at class-initialization time and cannot be varied.
static int javaMajorVersion(String version) {
// Java 8 and earlier report "1.N..."; the major version is the second component.
String rest = version.startsWith("1.") ? version.substring(2) : version;
// The major version is the leading run of digits. What follows it is "." for a release with
// minor components, "-" or "+" for a pre-release or build identifier, and nothing at all for a
// bare GA release such as "9".
int end = 0;
while (end < rest.length() && Character.isDigit(rest.charAt(end))) {
end++;
}
if (end == 0) {
throw new IllegalArgumentException("Cannot parse java.version: " + version);
}
return Integer.parseInt(rest.substring(0, end));
Comment thread
coderabbitai[bot] marked this conversation as resolved.
}
}
50 changes: 50 additions & 0 deletions java/daikon/chicory/RuntimeTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package daikon.chicory;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;

/** Tests for {@link daikon.chicory.Runtime}. */
@RunWith(JUnit4.class)
public class RuntimeTest {

/**
* Tests {@link Runtime#javaMajorVersion}. The values are real {@code java.version} strings; the
* bare "9" is the one that matters most, because Java 9 GA reported its version with no separator
* after the major number.
*/
@Test
public void testJavaMajorVersion() {
// Java 8 and earlier: the major version is the second component.
assertEquals(8, Runtime.javaMajorVersion("1.8.0_432"));
assertEquals(7, Runtime.javaMajorVersion("1.7.0_80"));
// Java 9 and later, with minor components.
assertEquals(9, Runtime.javaMajorVersion("9.0.4"));
assertEquals(11, Runtime.javaMajorVersion("11.0.28"));
assertEquals(24, Runtime.javaMajorVersion("24.0.1"));
assertEquals(25, Runtime.javaMajorVersion("25.0.2"));
// Java 9 and later, major version alone.
assertEquals(9, Runtime.javaMajorVersion("9"));
assertEquals(24, Runtime.javaMajorVersion("24"));
// Pre-release and build identifiers.
assertEquals(9, Runtime.javaMajorVersion("9-ea"));
assertEquals(26, Runtime.javaMajorVersion("26-ea"));
assertEquals(26, Runtime.javaMajorVersion("26+11"));

// Trailing junk after the major version is ignored rather than rejected; this is deliberate,
// and javaMajorVersion's javadoc says why. A value with no leading digits still throws.
assertEquals(9, Runtime.javaMajorVersion("9foo"));
assertThrows(IllegalArgumentException.class, () -> Runtime.javaMajorVersion("bogus"));
}

/** Tests that {@link Runtime#isJava24orLater} agrees with the JVM running the test. */
@Test
public void testIsJava24orLater() {
int major = Runtime.javaMajorVersion(System.getProperty("java.version"));
assertEquals(major >= 24, Runtime.isJava24orLater());
assertEquals(major >= 9, Runtime.isJava9orLater());
}
}
Binary file modified java/daikon/dcomp-dummy/DCRuntime.class.dummy
Binary file not shown.
9 changes: 9 additions & 0 deletions java/daikon/dcomp-dummy/DCRuntime.java.dummy
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ public final class DCRuntime {
public static void normal_exit_primitive(Object[] tag_frame) {
}

public static void uninstrumented_enter(int tagCount) {
}

public static void uninstrumented_exit() {
}

public static void uninstrumented_exit_primitive() {
}

public static void exception_exit(Object throwable) {
}

Expand Down
12 changes: 12 additions & 0 deletions java/daikon/dcomp-transfer/DCRuntime.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,18 @@ public static void normal_exit_primitive(Object[] tag_frame) {
daikon.dcomp.DCRuntime.normal_exit_primitive(tag_frame);
}

public static void uninstrumented_enter(int tagCount) {
daikon.dcomp.DCRuntime.uninstrumented_enter(tagCount);
}

public static void uninstrumented_exit() {
daikon.dcomp.DCRuntime.uninstrumented_exit();
}

public static void uninstrumented_exit_primitive() {
daikon.dcomp.DCRuntime.uninstrumented_exit_primitive();
}

public static void exception_exit(Object throwable) {
daikon.dcomp.DCRuntime.exception_exit(throwable);
}
Expand Down
13 changes: 0 additions & 13 deletions java/daikon/dcomp/ClassGen24.java
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ public class ClassGen24 {
/** True if this class is an interface. */
private final boolean isInterface;

/** True if this class is static. */
private final boolean isStatic;

/**
* Creates a ClassGen24 object.
*
Expand All @@ -84,7 +81,6 @@ public ClassGen24(

accessFlags = classModel.flags();
isInterface = accessFlags.has(AccessFlag.INTERFACE);
isStatic = accessFlags.has(AccessFlag.STATIC);

superclassName = getSuperclassName(classModel);

Expand Down Expand Up @@ -146,15 +142,6 @@ public final boolean isInterface() {
return isInterface;
}

/**
* Returns true if this class is static.
*
* @return true if this class is static
*/
public final boolean isStatic() {
return isStatic;
}

/**
* Returns this class's name, in binary format.
*
Expand Down
Loading
Loading