Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
4331a62
feat: invokedynamic for method parameter boundary
thisisalexandercook May 1, 2026
a75249f
chore: spotless
thisisalexandercook May 1, 2026
b75c547
feat: use invokedynamic for proper dynamic dispatch handling
thisisalexandercook May 4, 2026
f1afb5f
chore: spotless
thisisalexandercook May 4, 2026
79bd8c7
feat: invokedynamic for interface parameters
thisisalexandercook May 4, 2026
26f9b27
feat: invokedynamic for return values
thisisalexandercook May 4, 2026
86ef73a
fix: handle final split for indy properly
thisisalexandercook May 4, 2026
f6a155e
fix: handle synchronized methods with indy correctly
thisisalexandercook May 5, 2026
e0f1db8
fix: do not split native methods
thisisalexandercook May 5, 2026
2f24f4c
feat: indy bridge methods
thisisalexandercook May 5, 2026
fe26435
test: add generic bridge test case
thisisalexandercook May 5, 2026
c84d60e
feat: inherited method resolution for indy boundary rewrites
thisisalexandercook May 5, 2026
e691109
feat: inherited method resolution for return checks
thisisalexandercook May 5, 2026
0d438a4
feat: indy resoltuion for interface defaults
thisisalexandercook May 5, 2026
0665341
feat: indy inheritance for bridge methods
thisisalexandercook May 5, 2026
5c9694f
chore: agent.md
thisisalexandercook May 7, 2026
4e090a8
doc: initialization examples
thisisalexandercook May 7, 2026
b9e82e6
feat: indy abstact methods
thisisalexandercook May 11, 2026
9135887
test: cross package dispatch tests
thisisalexandercook May 11, 2026
3e11cff
fix: proper static resolution
thisisalexandercook May 11, 2026
d9d9a38
fix: proper interface resolution
thisisalexandercook May 11, 2026
4ad2d43
test: check for package-private shadowing
thisisalexandercook May 11, 2026
130594b
docs: update indy writing
thisisalexandercook May 12, 2026
5c8d6a9
chore: spotless
thisisalexandercook May 12, 2026
e315f30
chore: agent cleanup
thisisalexandercook May 12, 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
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ public void testInvokeScenarios() throws Exception {
false);
}

@Test
public void testInterfaceScenarios() throws Exception {
runDirectoryTest(
"nullness-interface",
"io.github.eisop.runtimeframework.checker.nullness.NullnessRuntimeChecker",
false);
}

@Test
public void testFieldReadScenarios() throws Exception {
runDirectoryTest(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ public static void main(String[] args) {
@Nullable String[] values = UncheckedLib.getNullableElementArray();
values[0] = null;
accept(values[0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@ public static void main(String[] args) {
UncheckedLib lib = new UncheckedLib();
lib.values[0] = null;
accept(lib.values[0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ public static void main(String[] args) {
@Nullable String[][] grid = new @Nullable String[1][1];
grid[0][0] = null;
accept(grid[0][0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,5 @@ public static void main(String[] args) {
NullableElementsFromField holder = new NullableElementsFromField();
holder.values[0] = null;
accept(holder.values[0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,5 @@ public static void main(String[] args) {
@Nullable String[] values = UncheckedLib.makeValues();
values[0] = null;
accept(values[0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,5 @@ public static void main(String[] args) {
@Nullable String[] values = new @Nullable String[1];
values[0] = null;
accept(values[0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,5 @@ public static void main(String[] args) {
grid[0] = new @Nullable String[1];
grid[0][0] = null;
accept(grid[0][0]);
// :: error: (Parameter 0 must be NonNull)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ public static void main(String[] args) {

test.overrideMe("safe", "safe");

// :: error: (Parameter 0 must be NonNull)
test.overrideMe(null, "unsafe");

test.overrideMe("safe", "null");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedBridgeInterfaceDispatch {
public static void main(String[] args) {
BridgeInterfaceSink<String> checked = new CheckedStringBridgeInterfaceSink();
checked.accept(null);

UncheckedBridgeInterfaceCaller.call(new CheckedStringBridgeInterfaceSink());
}
}

@AnnotatedFor("nullness")
interface BridgeInterfaceSink<T> {
void accept(T value);
}

@AnnotatedFor("nullness")
class CheckedStringBridgeInterfaceSink implements BridgeInterfaceSink<String> {
public void accept(String value) {
}
}

class UncheckedBridgeInterfaceCaller {
static void call(BridgeInterfaceSink<String> sink) {
// :: error: (Parameter 0 must be NonNull)
sink.accept(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedDefaultBridgeInterfaceDispatch implements CheckedDefaultBridgeChild {
public static void main(String[] args) {
CheckedDefaultBridgeParent<String> checked =
new CheckedDefaultBridgeInterfaceDispatch();
checked.accept(null);

UncheckedDefaultBridgeCaller.call(new CheckedDefaultBridgeInterfaceDispatch());
}
}

@AnnotatedFor("nullness")
interface CheckedDefaultBridgeParent<T> {
default void accept(T value) {
}
}

@AnnotatedFor("nullness")
interface CheckedDefaultBridgeChild extends CheckedDefaultBridgeParent<String> {
default void accept(String value) {
}
}

class UncheckedDefaultBridgeCaller {
static void call(CheckedDefaultBridgeParent<String> target) {
// :: error: (Parameter 0 must be NonNull)
target.accept(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedInheritedBridgeInterfaceDispatch {
public static void main(String[] args) {
InheritedBridgeSink<String> checked = new CheckedInheritedBridgeChild();
checked.accept(null);

UncheckedInheritedBridgeCaller.call(new CheckedInheritedBridgeChild());
}
}

@AnnotatedFor("nullness")
interface InheritedBridgeSink<T> {
void accept(T value);
}

@AnnotatedFor("nullness")
class CheckedInheritedBridgeBase {
public void accept(String value) {
}
}

@AnnotatedFor("nullness")
class CheckedInheritedBridgeChild
extends CheckedInheritedBridgeBase
implements InheritedBridgeSink<String> {
}

class UncheckedInheritedBridgeCaller {
static void call(InheritedBridgeSink<String> sink) {
// :: error: (Parameter 0 must be NonNull)
sink.accept(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedInheritedInterfaceDispatch {
public static void main(String[] args) {
CheckedInheritedChildInterface checked = new CheckedInheritedInterfaceImpl();
checked.accept(null);

UncheckedInheritedInterfaceCaller.call(new CheckedInheritedInterfaceImpl());
}
}

@AnnotatedFor("nullness")
interface CheckedInheritedParentInterface {
void accept(Object value);
}

@AnnotatedFor("nullness")
interface CheckedInheritedChildInterface extends CheckedInheritedParentInterface {
}

@AnnotatedFor("nullness")
class CheckedInheritedInterfaceImpl implements CheckedInheritedChildInterface {
public void accept(Object value) {
}
}

class UncheckedInheritedInterfaceCaller {
static void call(CheckedInheritedChildInterface target) {
// :: error: (Parameter 0 must be NonNull)
target.accept(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedInheritedUncheckedInterfaceReturn {
public static void main(String[] args) {
CheckedReturnChildInterface receiver = new UncheckedReturnInterfaceImpl();
receiver.produce();
// :: error: (Return value of produce (Boundary) must be NonNull)
}
}

interface UncheckedReturnParentInterface {
Object produce();
}

@AnnotatedFor("nullness")
interface CheckedReturnChildInterface extends UncheckedReturnParentInterface {
}

class UncheckedReturnInterfaceImpl implements CheckedReturnChildInterface {
public Object produce() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedInterfaceDefaultMethod implements CheckedDefaultContract {

static class Poison {
static String VALUE = null;
}

public static void main(String[] args) {
CheckedDefaultContract checked = new CheckedInterfaceDefaultMethod();
checked.defaultConsume(null);

UncheckedDefaultCaller.call(new CheckedInterfaceDefaultMethod());

// :: error: (Read Field 'VALUE' must be NonNull)
System.out.println(Poison.VALUE);
}
}

class UncheckedDefaultCaller {
static void call(CheckedDefaultContract receiver) {
// :: error: (Parameter 0 must be NonNull)
receiver.defaultConsume(null);
}
}

@AnnotatedFor("nullness")
interface CheckedDefaultContract {
default void defaultConsume(Object value) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedInterfaceDispatch {

static class Poison {
static String VALUE = null;
}

public static void main(String[] args) {
CheckedApi checked = new CheckedImpl();
checked.accept(null);

CheckedApi unchecked = new UncheckedImpl();
unchecked.accept(null);

// :: error: (Read Field 'VALUE' must be NonNull)
System.out.println(Poison.VALUE);
}
}

@AnnotatedFor("nullness")
interface CheckedApi {
void accept(Object value);
}

@AnnotatedFor("nullness")
class CheckedImpl implements CheckedApi {
public void accept(Object value) {
}
}

class UncheckedImpl implements CheckedApi {
public void accept(Object value) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;
import org.checkerframework.checker.nullness.qual.Nullable;

@AnnotatedFor("nullness")
public class CheckedInterfaceReturnBoundary {
public static void main(String[] args) {
ReturningContract checked = new CheckedReturningImpl();
checked.produce();

ReturningContract unchecked = new UncheckedReturningImpl();
unchecked.produce();
// :: error: (Return value of produce (Boundary) must be NonNull)

NullableReturningContract nullable = new UncheckedNullableReturningImpl();
nullable.produceNullable();
}
}

@AnnotatedFor("nullness")
interface ReturningContract {
Object produce();
}

@AnnotatedFor("nullness")
class CheckedReturningImpl implements ReturningContract {
public Object produce() {
return new Object();
}
}

class UncheckedReturningImpl implements ReturningContract {
public Object produce() {
return null;
}
}

@AnnotatedFor("nullness")
interface NullableReturningContract {
@Nullable Object produceNullable();
}

class UncheckedNullableReturningImpl implements NullableReturningContract {
public Object produceNullable() {
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import io.github.eisop.runtimeframework.qual.AnnotatedFor;

@AnnotatedFor("nullness")
public class CheckedNativeInterfaceDispatch {
static class Poison {
static String VALUE = null;
}

public static void main(String[] args) {
CheckedNativeContract receiver = new CheckedNativeInterfaceImpl();
boolean reachedNative = false;
try {
receiver.produce(new Object());
} catch (UnsatisfiedLinkError expected) {
reachedNative = true;
} catch (AssertionError wrongSafeStub) {
reachedNative = false;
}

if (!reachedNative) {
System.out.println(Poison.VALUE);
}
}
}

@AnnotatedFor("nullness")
interface CheckedNativeContract {
Object produce(Object value);
}

@AnnotatedFor("nullness")
class CheckedNativeInterfaceImpl implements CheckedNativeContract {
public native Object produce(Object value);
}
Loading
Loading