blob: fa1c0e870ebfc7af1edeb5872cdc0b2520fa97b9 [file] [log] [blame]
import org.checkerframework.checker.guieffect.qual.AlwaysSafe;
import org.checkerframework.checker.guieffect.qual.PolyUI;
import org.checkerframework.checker.guieffect.qual.PolyUIEffect;
import org.checkerframework.checker.guieffect.qual.PolyUIType;
import org.checkerframework.checker.guieffect.qual.UI;
import org.checkerframework.checker.guieffect.qual.UIEffect;
public class Java8Lambdas {
private interface SafeFunctionalInterface<T> {
public void executeAlwaysSafe(T arg);
}
private interface UIFunctionalInterface<T> {
@UIEffect
public void executeUI(T arg);
}
@PolyUIType
private interface PolymorphicFunctionalInterface<T> {
@PolyUIEffect
public void executePolymorphic(T arg);
}
private static class LambdaRunner {
private final UIElement arg;
public LambdaRunner(UIElement arg) {
this.arg = arg;
}
public void doSafe(SafeFunctionalInterface<UIElement> func) {
func.executeAlwaysSafe(this.arg);
}
@UIEffect
public void doUI(UIFunctionalInterface<UIElement> func) {
func.executeUI(this.arg);
}
// Needs to be @UIEffect, because the functional interface method is @UIEffect
public void unsafeDoUI(UIFunctionalInterface<UIElement> func) {
// :: error: (call.ui)
func.executeUI(this.arg);
}
public void doEither(@PolyUI PolymorphicFunctionalInterface<UIElement> func) {
// In a real program some intelligent dispatch could be done here to avoid running on UI
// thread unless needed.
arg.runOnUIThread(
new IAsyncUITask() {
final UIElement e2 = arg;
@Override
public void doStuff() { // should inherit UI effect
func.executePolymorphic(e2); // should be okay
}
});
}
public void doUISafely(@UI PolymorphicFunctionalInterface<UIElement> func) {
// In a real program some intelligent dispatch could be done here to avoid running on UI
// thread unless needed.
arg.runOnUIThread(
new IAsyncUITask() {
final UIElement e2 = arg;
@Override
public void doStuff() { // should inherit UI effect
func.executePolymorphic(e2); // should be okay
}
});
}
}
@PolyUIType
private static class PolymorphicLambdaRunner {
private final UIElement arg;
public PolymorphicLambdaRunner(UIElement arg) {
this.arg = arg;
}
@PolyUIEffect
public void doEither(@PolyUI PolymorphicFunctionalInterface<UIElement> func) {
func.executePolymorphic(this.arg);
}
}
public static void safeContextTestCases(UIElement elem) {
LambdaRunner runner = new LambdaRunner(elem);
runner.doSafe(e -> e.repaint());
// :: error: (call.ui)
runner.doSafe(e -> e.dangerous()); // Not allowed in doSafe
// :: error: (call.ui)
runner.doUI(e -> e.repaint()); // Not allowed in safe context
// :: error: (call.ui)
runner.doUI(e -> e.dangerous()); // Not allowed in safe context
runner.doEither(e -> e.repaint());
runner.doEither(e -> e.dangerous());
runner.doUISafely(e -> e.dangerous());
@AlwaysSafe PolymorphicLambdaRunner safePolymorphicLambdaRunner = new PolymorphicLambdaRunner(elem);
safePolymorphicLambdaRunner.doEither(e -> e.repaint());
// This next two are ok for this patch since the behavior is the same (no report) for
// lambdas as for annon classes. However, shouldn't this be (argument)
// just because safePolymorphicLambdaRunner is not an @UI PolymorphicLambdaRunner ? Or,
// failing that (call.ui) since doEither is @PolyUIEffect ?
safePolymorphicLambdaRunner.doEither(e -> e.dangerous());
safePolymorphicLambdaRunner.doEither(
new @UI PolymorphicFunctionalInterface<UIElement>() {
public void executePolymorphic(UIElement arg) {
arg.dangerous();
}
});
@UI PolymorphicLambdaRunner uiPolymorphicLambdaRunner = new @UI PolymorphicLambdaRunner(elem);
// :: error: (call.ui)
uiPolymorphicLambdaRunner.doEither(
e -> e.repaint()); // Safe at runtime, but not by the type system!
// :: error: (call.ui)
uiPolymorphicLambdaRunner.doEither(e -> e.dangerous());
PolymorphicFunctionalInterface<UIElement> func1 = e -> e.repaint();
// :: error: (assignment)
PolymorphicFunctionalInterface<UIElement> func2 = e -> e.dangerous(); // Incompatible types!
PolymorphicFunctionalInterface<UIElement> func2p =
// :: error: (assignment)
(new @UI PolymorphicFunctionalInterface<UIElement>() {
public void executePolymorphic(UIElement arg) {
arg.dangerous();
}
});
@UI PolymorphicFunctionalInterface<UIElement> func3 = e -> e.dangerous();
safePolymorphicLambdaRunner.doEither(func1);
safePolymorphicLambdaRunner.doEither(func2);
// :: error: (call.ui)
uiPolymorphicLambdaRunner.doEither(func1);
// :: error: (call.ui)
uiPolymorphicLambdaRunner.doEither(func2);
// :: error: (call.ui)
uiPolymorphicLambdaRunner.doEither(func3);
}
@UIEffect
public static void uiContextTestCases(UIElement elem) {
LambdaRunner runner = new LambdaRunner(elem);
// :: error: (call.ui)
runner.doSafe(e -> e.dangerous());
runner.doUI(e -> e.repaint());
runner.doUI(e -> e.dangerous());
PolymorphicLambdaRunner safePolymorphicLambdaRunner = new PolymorphicLambdaRunner(elem);
// No error, why? :: error: (argument)
safePolymorphicLambdaRunner.doEither(e -> e.dangerous());
@UI PolymorphicLambdaRunner uiPolymorphicLambdaRunner = new @UI PolymorphicLambdaRunner(elem);
uiPolymorphicLambdaRunner.doEither(e -> e.dangerous());
}
public @UI PolymorphicFunctionalInterface<UIElement> returnLambdasTest1() {
return e -> e.dangerous();
}
// This should be an error without an @UI annotation on the return type. No?
public PolymorphicFunctionalInterface<UIElement> returnLambdasTest2() {
// :: error: (return)
return e -> {
e.dangerous();
};
}
// Just to check
public PolymorphicFunctionalInterface<UIElement> returnLambdasTest3() {
// :: error: (return)
return (new @UI PolymorphicFunctionalInterface<UIElement>() {
public void executePolymorphic(UIElement arg) {
arg.dangerous();
}
});
}
}