From ec3990c6197bf28762bae73709823a35f5cc2168 Mon Sep 17 00:00:00 2001 From: Anders Schack-Mulligen Date: Thu, 9 Sep 2021 15:04:22 +0200 Subject: [PATCH 1/2] Java: Fix FunctionalInterface. --- java/ql/lib/semmle/code/java/Type.qll | 38 +++++++++++++++++++ .../code/java/dispatch/WrappedInvocation.qll | 31 --------------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/java/ql/lib/semmle/code/java/Type.qll b/java/ql/lib/semmle/code/java/Type.qll index 3b05665d0557..5395e855701d 100755 --- a/java/ql/lib/semmle/code/java/Type.qll +++ b/java/ql/lib/semmle/code/java/Type.qll @@ -874,6 +874,44 @@ class ClassOrInterface extends RefType, @classorinterface { } } +private string getAPublicObjectMethodSignature() { + exists(Method m | + m.getDeclaringType() instanceof TypeObject and + m.isPublic() and + result = m.getSignature() + ) +} + +private Method getAnAbstractMethod(Interface interface) { + interface.inherits(result) and + result.isAbstract() and + // JLS 9.8, ignore Object methods + not result.getSignature() = getAPublicObjectMethodSignature() and + // Make sure that there is no other non-abstract method + // (e.g. `default`) which overrides the abstract one + not exists(Method m | + interface.inherits(m) and + not m.isAbstract() and + m.overrides(result) + ) +} + +/** + * A functional interface is an interface that has just one abstract method + * (aside from the methods of Object), and thus represents a single function + * contract. + * + * See JLS 9.8, Functional Interfaces. + */ +class FunctionalInterface extends Interface { + Method run; + + FunctionalInterface() { run = unique(Method r | r = getAnAbstractMethod(this)) } + + /** Gets the single abstract method of this interface. */ + Method getRunMethod() { result = run } +} + /** * A primitive type. * diff --git a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll index cbff880ca961..26fcd109df2c 100644 --- a/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll +++ b/java/ql/lib/semmle/code/java/dispatch/WrappedInvocation.qll @@ -6,37 +6,6 @@ import java import VirtualDispatch -private string getAPublicObjectMethodSignature() { - exists(Method m | - m.getDeclaringType() instanceof TypeObject and - m.isPublic() and - result = m.getSignature() - ) -} - -private Method getAPotentialRunMethod(Interface i) { - i.inherits(result) and - result.isPublic() and - not result.getSignature() = getAPublicObjectMethodSignature() -} - -/** - * A functional interface is an interface that has just one abstract method - * (aside from the methods of Object), and thus represents a single function - * contract. - * - * See JLS 9.8, Functional Interfaces. - */ -class FunctionalInterface extends Interface { - FunctionalInterface() { - 1 = strictcount(getAPotentialRunMethod(this)) and - not exists(Method m | this.inherits(m) and m.isDefault()) - } - - /** Gets the single method of this interface. */ - Method getRunMethod() { getAPotentialRunMethod(this).getSourceDeclaration() = result } -} - /** * Holds if `m` might invoke `runmethod` through a functional interface on the * `n`th parameter. From a0bf170d0206d380b9ac55e9f6b6179aaaa47052 Mon Sep 17 00:00:00 2001 From: Chris Smowton Date: Thu, 9 Sep 2021 15:00:42 +0100 Subject: [PATCH 2/2] Add test for functional interfaces --- .../functional-interfaces/Test.java | 29 +++++++++++++++++++ .../functional-interfaces/test.expected | 3 ++ .../functional-interfaces/test.ql | 3 ++ 3 files changed, 35 insertions(+) create mode 100644 java/ql/test/library-tests/functional-interfaces/Test.java create mode 100644 java/ql/test/library-tests/functional-interfaces/test.expected create mode 100644 java/ql/test/library-tests/functional-interfaces/test.ql diff --git a/java/ql/test/library-tests/functional-interfaces/Test.java b/java/ql/test/library-tests/functional-interfaces/Test.java new file mode 100644 index 000000000000..ad0d4c079b42 --- /dev/null +++ b/java/ql/test/library-tests/functional-interfaces/Test.java @@ -0,0 +1,29 @@ +public class Test { + + interface Functional { + int f(); + } + + class Concrete implements Functional { + public int f() { return 0; } + } + + interface FunctionalWithDefaults { + int f(); + + default int g() { return 1; } + } + + interface NotFunctional { + default int g() { return 1; } + } + + interface FunctionalWithObjectMethods { + int f(); + + String toString(); + + int hashCode(); + } + +} diff --git a/java/ql/test/library-tests/functional-interfaces/test.expected b/java/ql/test/library-tests/functional-interfaces/test.expected new file mode 100644 index 000000000000..4d6b4a1a4c05 --- /dev/null +++ b/java/ql/test/library-tests/functional-interfaces/test.expected @@ -0,0 +1,3 @@ +| Test.java:3:13:3:22 | Functional | +| Test.java:11:13:11:34 | FunctionalWithDefaults | +| Test.java:21:13:21:39 | FunctionalWithObjectMethods | diff --git a/java/ql/test/library-tests/functional-interfaces/test.ql b/java/ql/test/library-tests/functional-interfaces/test.ql new file mode 100644 index 000000000000..514df6a634ca --- /dev/null +++ b/java/ql/test/library-tests/functional-interfaces/test.ql @@ -0,0 +1,3 @@ +import java + +select any(FunctionalInterface fi | fi.fromSource())